Skip to content

Commit

Permalink
4.0 multi database (#384)
Browse files Browse the repository at this point in the history
* removed redundant imports

* implemented get routing table call for bolt 4.0

* moved neo4j driver test to an explicit neo4j driver test file

* refactor renamed cx.server to cx.server_info

* whitespace fix

* implemented driver.supports_multi_db function

* summary.protocol_version is now in summary.server.protocol_version

* updated the success data to be like the actual data sent

* added variable database to summary information

* access mode fix and specifying database is implemented through config settings

access mode is fixed and tested properly

* added comments about routing

* rotuing tables are updated per database

includes new tests, code improvements and some minor fixes.

* fixed test against Neo4j 3.4

* added comment about circular dependency for Bolt subclass

* resolved pr comments

* fixed begin_transaction function api
  • Loading branch information
martin-neotech authored Apr 8, 2020
1 parent f9d15f9 commit 3482e6a
Show file tree
Hide file tree
Showing 89 changed files with 1,128 additions and 374 deletions.
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ Argument Renaming Changes
* :code:`Transaction.run(statement, ...` is now :code:`Transaction.run(query, ...`
* :code:`StatementResultSummary.statement` is now :code:`ResultSummary.query`
* :code:`StatementResultSummary.statement_type` is now :code:`ResultSummary.query_type`
* :code:`StatementResultSummary.protocol_version` is now :code:`ResultSummary.server.protocol_version`


Dependency Changes
Expand Down
2 changes: 1 addition & 1 deletion docs/source/usage_patterns.rst
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,6 @@ Transaction Object Work Pattern
query = Query("RETURN 1 AS x, timeout=10, metadata={"hello": 123})
tx = session.begin_transaction(bookmark=None, metadata=None, timeout=None)
tx = session.begin_transaction(metadata=None, timeout=None)
tx.run(query)
tx.commit()
55 changes: 40 additions & 15 deletions neo4j/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,6 @@ def driver(cls, uri, *, auth=None, **config):
URI_SCHEME_NEO4J_SELF_SIGNED_CERTIFICATE,
URI_SCHEME_NEO4J_SECURE,
)
from neo4j.conf import (
TRUST_ALL_CERTIFICATES,
TRUST_SYSTEM_CA_SIGNED_CERTIFICATES
)

driver_type, security_type, parsed = parse_neo4j_uri(uri)

Expand Down Expand Up @@ -252,8 +248,7 @@ def parse_targets(cls, *targets):
targets = " ".join(targets)
if not targets:
targets = cls.default_targets
addresses = Address.parse_list(targets, default_host=cls.default_host,
default_port=cls.default_port)
addresses = Address.parse_list(targets, default_host=cls.default_host, default_port=cls.default_port)
return addresses


Expand Down Expand Up @@ -314,6 +309,26 @@ def verify_connectivity(self, **config):
"""
raise NotImplementedError

@experimental("Feature support query, based on Bolt Protocol Version and Neo4j Server Version will change in the future.")
def supports_multi_db(self):
""" Check if the server or cluster supports multi-databases.
:return: Returns true if the server or cluster the driver connects to supports multi-databases, otherwise false.
:rtype: bool
"""
from neo4j.io._bolt4x0 import Bolt4x0

multi_database = False
cx = self._pool.acquire(access_mode=READ_ACCESS, timeout=self._pool.workspace_config.connection_acquisition_timeout, database=self._pool.workspace_config.database)

# TODO: This logic should be inside the Bolt subclasses, because it can change depending on Bolt Protocol Version.
if cx.PROTOCOL_VERSION >= Bolt4x0.PROTOCOL_VERSION and cx.server_info.version_info() >= Version(4, 0, 0):
multi_database = True

self._pool.release(cx)

return multi_database


class BoltDriver(Direct, Driver):
""" A :class:`.BoltDriver` is created from a ``bolt`` URI and addresses
Expand All @@ -326,10 +341,18 @@ class BoltDriver(Direct, Driver):

@classmethod
def open(cls, target, *, auth=None, **config):
"""
:param target:
:param auth:
:param config: The values that can be specified are found in :class: `neo4j.PoolConfig` and :class: `neo4j.WorkspaceConfig`
:return:
:rtype: :class: `neo4j.BoltDriver`
"""
from neo4j.io import BoltPool
address = cls.parse_target(target)
pool_config, default_workspace_config = Config.consume_chain(config, PoolConfig, WorkspaceConfig)
pool = BoltPool.open(address, auth=auth, **pool_config)
pool = BoltPool.open(address, auth=auth, pool_config=pool_config, workspace_config=default_workspace_config)
return cls(pool, default_workspace_config)

def __init__(self, pool, default_workspace_config):
Expand All @@ -338,6 +361,12 @@ def __init__(self, pool, default_workspace_config):
self._default_workspace_config = default_workspace_config

def session(self, **config):
"""
:param config: The values that can be specified are found in :class: `neo4j.SessionConfig`
:return:
:rtype: :class: `neo4j.Session`
"""
from neo4j.work.simple import Session
session_config = SessionConfig(self._default_workspace_config, config)
SessionConfig.consume(config) # Consume the config
Expand Down Expand Up @@ -372,16 +401,15 @@ def open(cls, *targets, auth=None, routing_context=None, **config):
from neo4j.io import Neo4jPool
addresses = cls.parse_targets(*targets)
pool_config, default_workspace_config = Config.consume_chain(config, PoolConfig, WorkspaceConfig)
pool = Neo4jPool.open(*addresses, auth=auth, routing_context=routing_context, **pool_config)
pool = Neo4jPool.open(*addresses, auth=auth, routing_context=routing_context, pool_config=pool_config, workspace_config=default_workspace_config)
return cls(pool, default_workspace_config)

def __init__(self, pool, default_workspace_config):
Routing.__init__(self, pool.routing_table.initial_routers)
Routing.__init__(self, pool.get_default_database_initial_router_addresses())
Driver.__init__(self, pool)
self._default_workspace_config = default_workspace_config

def session(self, **config):
from neo4j.work.simple import Session
session_config = SessionConfig(self._default_workspace_config, config)
SessionConfig.consume(config) # Consume the config
return Session(self._pool, session_config)
Expand All @@ -392,9 +420,6 @@ def pipeline(self, **config):
PipelineConfig.consume(config) # Consume the config
return Pipeline(self._pool, pipeline_config)

def get_routing_table(self):
return self._pool.routing_table

def verify_connectivity(self, **config):
"""
:raise ServiceUnavailable: raised if the server does not support routing or if routing support is broken.
Expand All @@ -406,11 +431,11 @@ def _verify_routing_connectivity(self):
from neo4j.exceptions import ServiceUnavailable
from neo4j._exceptions import BoltHandshakeError

table = self.get_routing_table()
table = self._pool.get_routing_table_for_default_database()
routing_info = {}
for ix in list(table.routers):
try:
routing_info[ix] = self._pool.fetch_routing_info(table.routers[0])
routing_info[ix] = self._pool.fetch_routing_info(address=table.routers[0], timeout=self._default_workspace_config.connection_acquisition_timeout, database=self._default_workspace_config.database)
except BoltHandshakeError as error:
routing_info[ix] = None

Expand Down
2 changes: 1 addition & 1 deletion neo4j/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
TRUST_ALL_CERTIFICATES = "TRUST_ALL_CERTIFICATES"

SYSTEM_DATABASE = "system"
DEFAULT_DATABASE = None
DEFAULT_DATABASE = None # Must be a non string hashable value


class Auth:
Expand Down
20 changes: 18 additions & 2 deletions neo4j/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,14 +304,15 @@ class SessionConfig(WorkspaceConfig):
#: Bookmarks
bookmarks = ()

# Default AccessMode
#: Default AccessMode
default_access_mode = WRITE_ACCESS
# access_mode = DeprecatedAlias("default_access_mode")


class TransactionConfig(Config):
""" Transaction configuration.
""" Transaction configuration. This is internal for now.
neo4j.session.begin_transaction
neo4j.Query
neo4j.unit_of_work
Expand All @@ -322,3 +323,18 @@ class TransactionConfig(Config):

#: Timeout
timeout = None # seconds


class RoutingConfig(Config):
""" Neo4jDriver routing settings. This is internal for now.
"""

#: Routing Table Purge_Delay
routing_table_purge_delay = 30.0 # seconds
# The TTL + routing_table_purge_delay should be used to check if the database routing table should be removed.

#: Max Routing Failures
# max_routing_failures = 1

#: Retry Timeout Delay
# retry_timeout_delay = 5.0 # seconds
Loading

0 comments on commit 3482e6a

Please sign in to comment.