neo4j-ruby-driver 4.4.0.alpha.5 → 4.4.0.alpha.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/neo4j/driver/exceptions/protocol_exception.rb +2 -2
- data/lib/neo4j/driver/internal/bolt_server_address.rb +6 -6
- data/lib/neo4j/driver/types/time.rb +4 -2
- data/ruby/neo4j/driver/config.rb +1 -1
- data/ruby/neo4j/driver/graph_database.rb +2 -2
- data/ruby/neo4j/driver/internal/async/inbound/inbound_message_dispatcher.rb +2 -3
- data/ruby/neo4j/driver/internal/async/network_session.rb +4 -3
- data/ruby/neo4j/driver/internal/async/pool/{netty_channel_tracker.rb → channel_tracker.rb} +6 -8
- data/ruby/neo4j/driver/internal/async/pool/connection_pool_impl.rb +3 -3
- data/ruby/neo4j/driver/internal/async/unmanaged_transaction.rb +18 -20
- data/ruby/neo4j/driver/internal/cluster/cluster_composition.rb +10 -20
- data/ruby/neo4j/driver/internal/cluster/cluster_composition_lookup_result.rb +2 -2
- data/ruby/neo4j/driver/internal/cluster/cluster_routing_table.rb +38 -55
- data/ruby/neo4j/driver/internal/cluster/identity_resolver.rb +1 -4
- data/ruby/neo4j/driver/internal/cluster/loadbalancing/least_connected_load_balancing_strategy.rb +6 -6
- data/ruby/neo4j/driver/internal/cluster/loadbalancing/load_balancer.rb +44 -80
- data/ruby/neo4j/driver/internal/cluster/multi_databases_routing_procedure_runner.rb +6 -9
- data/ruby/neo4j/driver/internal/cluster/rediscovery_impl.rb +65 -155
- data/ruby/neo4j/driver/internal/cluster/route_message_routing_procedure_runner.rb +2 -2
- data/ruby/neo4j/driver/internal/cluster/routing_procedure_cluster_composition_provider.rb +8 -12
- data/ruby/neo4j/driver/internal/cluster/routing_procedure_response.rb +19 -3
- data/ruby/neo4j/driver/internal/cluster/routing_table_handler_impl.rb +46 -67
- data/ruby/neo4j/driver/internal/cluster/routing_table_registry_impl.rb +42 -61
- data/ruby/neo4j/driver/internal/cluster/single_database_routing_procedure_runner.rb +15 -19
- data/ruby/neo4j/driver/internal/cursor/async_result_cursor_impl.rb +31 -19
- data/ruby/neo4j/driver/internal/cursor/disposable_async_result_cursor.rb +12 -15
- data/ruby/neo4j/driver/internal/database_name_util.rb +3 -3
- data/ruby/neo4j/driver/internal/default_bookmark_holder.rb +1 -7
- data/ruby/neo4j/driver/internal/direct_connection_provider.rb +1 -1
- data/ruby/neo4j/driver/internal/driver_factory.rb +4 -4
- data/ruby/neo4j/driver/internal/handlers/commit_tx_response_handler.rb +4 -4
- data/ruby/neo4j/driver/internal/handlers/legacy_pull_all_response_handler.rb +90 -51
- data/ruby/neo4j/driver/internal/handlers/pulln/auto_pull_response_handler.rb +33 -44
- data/ruby/neo4j/driver/internal/handlers/rollback_tx_response_handler.rb +7 -1
- data/ruby/neo4j/driver/internal/impersonation_util.rb +2 -2
- data/ruby/neo4j/driver/internal/internal_bookmark.rb +1 -1
- data/ruby/neo4j/driver/internal/internal_database_name.rb +3 -5
- data/ruby/neo4j/driver/internal/internal_result.rb +5 -5
- data/ruby/neo4j/driver/internal/internal_session.rb +1 -1
- data/ruby/neo4j/driver/internal/internal_transaction.rb +4 -4
- data/ruby/neo4j/driver/internal/messaging/bolt_protocol_version.rb +3 -1
- data/ruby/neo4j/driver/internal/messaging/encode/route_message_encoder.rb +8 -2
- data/ruby/neo4j/driver/internal/messaging/encode/route_v44_message_encoder.rb +8 -13
- data/ruby/neo4j/driver/internal/messaging/request/abstract_streaming_message.rb +4 -1
- data/ruby/neo4j/driver/internal/messaging/request/begin_message.rb +2 -3
- data/ruby/neo4j/driver/internal/messaging/request/multi_database_util.rb +2 -2
- data/ruby/neo4j/driver/internal/messaging/request/route_message.rb +5 -10
- data/ruby/neo4j/driver/internal/messaging/request/run_with_metadata_message.rb +5 -3
- data/ruby/neo4j/driver/internal/messaging/request/transaction_metadata_builder.rb +2 -2
- data/ruby/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +8 -4
- data/ruby/neo4j/driver/internal/messaging/v44/message_writer_v44.rb +1 -1
- data/ruby/neo4j/driver/internal/read_only_bookmark_holder.rb +13 -0
- data/ruby/neo4j/driver/internal/resolved_bolt_server_address.rb +35 -0
- data/ruby/neo4j/driver/internal/security/security_plan_impl.rb +14 -9
- data/ruby/neo4j/driver/internal/util/error_util.rb +1 -1
- data/ruby/neo4j/driver/internal/util/result_holder.rb +70 -0
- data/ruby/neo4j/driver/net/{server_address1.rb → server_address.rb} +2 -2
- data/ruby/neo4j/driver/query.rb +1 -1
- data/ruby/neo4j/driver/transaction_config.rb +5 -1
- data/ruby/neo4j/driver/values.rb +3 -3
- data/ruby/neo4j/driver/version.rb +1 -1
- metadata +16 -14
- data/ruby/neo4j/driver/internal/database_name.rb +0 -12
@@ -8,8 +8,6 @@ module Neo4j::Driver
|
|
8
8
|
CONNECTION_ACQUISITION_ATTEMPT_FAILURE_MESSAGE = "Failed to obtain a connection towards address %s, will try other addresses if available. Complete failure is reported separately from this entry."
|
9
9
|
BOLT_SERVER_ADDRESSES_EMPTY_ARRAY = []
|
10
10
|
|
11
|
-
attr_reader :routing_tables
|
12
|
-
|
13
11
|
delegate :close, to: :@connection_pool
|
14
12
|
|
15
13
|
def initialize(initial_router, settings, connection_pool, event_executor_group, logger, load_balancing_strategy, resolver, &domain_name_resolver)
|
@@ -23,106 +21,72 @@ module Neo4j::Driver
|
|
23
21
|
end
|
24
22
|
|
25
23
|
def acquire_connection(context)
|
26
|
-
@routing_tables.ensure_routing_table(context)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
context.mode, context.impersonated_user, handler)
|
31
|
-
end
|
32
|
-
end
|
24
|
+
handler = @routing_tables.ensure_routing_table(context)
|
25
|
+
connection = acquire(context.mode, handler.routing_table)
|
26
|
+
Async::Connection::RoutingConnection.new(connection, context.database_name, context.mode,
|
27
|
+
context.impersonated_user, handler)
|
33
28
|
end
|
34
29
|
|
35
30
|
def verify_connectivity
|
36
|
-
supports_multi_db
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
'Unable to connect to database management service, ensure the database is running and that there is a working network connection to it.',
|
41
|
-
error)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
31
|
+
@routing_tables.ensure_routing_table(Async::ImmutableConnectionContext.simple(supports_multi_db?))
|
32
|
+
rescue Exceptions::ServiceUnavailableException
|
33
|
+
raise Exceptions::ServiceUnavailableException,
|
34
|
+
'Unable to connect to database management service, ensure the database is running and that there is a working network connection to it.'
|
45
35
|
end
|
46
36
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
rescue StandardError => error
|
51
|
-
return Util::Futures.failed_future(error)
|
52
|
-
end
|
37
|
+
def routing_table_registry
|
38
|
+
@routing_tables
|
39
|
+
end
|
53
40
|
|
54
|
-
|
41
|
+
def supports_multi_db?
|
42
|
+
addresses = @rediscovery.resolve
|
55
43
|
base_error = Exceptions::ServiceUnavailableException.new("Failed to perform multi-databases feature detection with the following servers: #{addresses}")
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
private_suports_multi_db?(address)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
Util::Futures.on_error_continue(result, base_error) do |error|
|
69
|
-
# If we failed with security errors, then we rethrow the security error out, otherwise we throw the chained errors.
|
70
|
-
Util::Futures.failed_future((error.is_a? Exceptions::SecurityException) ? error : base_error)
|
71
|
-
end
|
44
|
+
addresses.lazy.map do |address|
|
45
|
+
private_suports_multi_db?(address)
|
46
|
+
rescue Exceptions::SecurityException
|
47
|
+
raise
|
48
|
+
rescue => error
|
49
|
+
Util::Futures.combine_errors(base_error, error)
|
50
|
+
end.find { |result| !result.nil? } or raise base_error
|
72
51
|
end
|
73
52
|
|
74
53
|
private
|
75
54
|
|
76
55
|
def private_suports_multi_db?(address)
|
77
|
-
@connection_pool.acquire(address)
|
78
|
-
|
79
|
-
|
80
|
-
|
56
|
+
conn = @connection_pool.acquire(address)
|
57
|
+
Messaging::Request::MultiDatabaseUtil.supports_multi_database?(conn)
|
58
|
+
ensure
|
59
|
+
conn&.release
|
81
60
|
end
|
82
61
|
|
83
|
-
def acquire(mode, routing_table,
|
84
|
-
unless result.present? && attempt_errors.present?
|
85
|
-
result = java.util.concurrent.CompletableFuture.new
|
86
|
-
attempt_exceptions = []
|
87
|
-
end
|
88
|
-
|
62
|
+
def acquire(mode, routing_table, attempt_errors = [])
|
89
63
|
addresses = addresses_by_mode(mode, routing_table)
|
90
64
|
address = select_address(mode, addresses)
|
91
65
|
|
92
|
-
|
93
|
-
|
94
|
-
attempt_errors.each
|
95
|
-
|
96
|
-
@log.error(
|
97
|
-
|
98
|
-
return
|
66
|
+
unless address
|
67
|
+
error = Exceptions::SessionExpiredException.new(CONNECTION_ACQUISITION_COMPLETION_EXCEPTION_MESSAGE % [mode, routing_table])
|
68
|
+
attempt_errors.each(&error.method(:add_suppressed))
|
69
|
+
@log.error(CONNECTION_ACQUISITION_COMPLETION_FAILURE_MESSAGE)
|
70
|
+
@log.error(error)
|
71
|
+
raise error
|
99
72
|
end
|
100
73
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
else
|
110
|
-
attempt_message = CONNECTION_ACQUISITION_ATTEMPT_FAILURE_MESSAGE % address
|
111
|
-
@log.warn(attempt_message)
|
112
|
-
@log.debug(attempt_message, error)
|
113
|
-
attempt_errors << error
|
114
|
-
routing_table.forget(address)
|
115
|
-
@event_executor_group.next.execute(-> () { acquire(mode, routing_table, result, attempt_errors) })
|
116
|
-
end
|
117
|
-
end
|
74
|
+
begin
|
75
|
+
@connection_pool.acquire(address)
|
76
|
+
rescue Exceptions::ServiceUnavailableException => error
|
77
|
+
@log.warn { CONNECTION_ACQUISITION_ATTEMPT_FAILURE_MESSAGE % address }
|
78
|
+
@log.debug(error)
|
79
|
+
attempt_errors << error
|
80
|
+
routing_table.forget(address)
|
81
|
+
acquire(mode, routing_table, attempt_errors)
|
118
82
|
end
|
119
83
|
end
|
120
84
|
|
121
85
|
def addresses_by_mode(mode, routing_table)
|
122
86
|
case mode
|
123
|
-
when READ
|
87
|
+
when AccessMode::READ
|
124
88
|
routing_table.readers
|
125
|
-
when WRITE
|
89
|
+
when AccessMode::WRITE
|
126
90
|
routing_table.writers
|
127
91
|
else
|
128
92
|
raise unknown_mode mode
|
@@ -131,9 +95,9 @@ module Neo4j::Driver
|
|
131
95
|
|
132
96
|
def select_address(mode, addresses)
|
133
97
|
case mode
|
134
|
-
when READ
|
98
|
+
when AccessMode::READ
|
135
99
|
@load_balancing_strategy.select_reader(addresses)
|
136
|
-
when WRITE
|
100
|
+
when AccessMode::WRITE
|
137
101
|
@load_balancing_strategy.select_writer(addresses)
|
138
102
|
else
|
139
103
|
raise unknown_mode mode
|
@@ -141,7 +105,7 @@ module Neo4j::Driver
|
|
141
105
|
end
|
142
106
|
|
143
107
|
def create_routing_tables(connection_pool, rediscovery, settings, clock, logger)
|
144
|
-
RoutingTableRegistryImpl.new(connection_pool, rediscovery, clock, logger,
|
108
|
+
RoutingTableRegistryImpl.new(connection_pool, rediscovery, clock, logger, settings.routing_table_purge_delay)
|
145
109
|
end
|
146
110
|
|
147
111
|
def create_rediscovery(event_executor_group, initial_router, resolver, settings, clock, logger, domain_name_resolver)
|
@@ -5,13 +5,9 @@ module Neo4j::Driver
|
|
5
5
|
# This implementation of the {@link RoutingProcedureRunner} works with multi database versions of Neo4j calling
|
6
6
|
# the procedure `dbms.routing.getRoutingTable`
|
7
7
|
class MultiDatabasesRoutingProcedureRunner < SingleDatabaseRoutingProcedureRunner
|
8
|
-
DATABASE_NAME =
|
8
|
+
DATABASE_NAME = :database
|
9
9
|
MULTI_DB_GET_ROUTING_TABLE = "CALL dbms.routing.getRoutingTable($%s, $%s)" % [SingleDatabaseRoutingProcedureRunner::ROUTING_CONTEXT, DATABASE_NAME]
|
10
10
|
|
11
|
-
def initialize(context)
|
12
|
-
super(context)
|
13
|
-
end
|
14
|
-
|
15
11
|
private
|
16
12
|
|
17
13
|
def bookmark_holder(bookmark)
|
@@ -19,10 +15,11 @@ module Neo4j::Driver
|
|
19
15
|
end
|
20
16
|
|
21
17
|
def procedure_query(server_version, database_name)
|
22
|
-
map = {
|
23
|
-
|
24
|
-
|
25
|
-
|
18
|
+
map = {
|
19
|
+
SingleDatabaseRoutingProcedureRunner::ROUTING_CONTEXT => @context.to_h,
|
20
|
+
DATABASE_NAME => database_name.database_name
|
21
|
+
}
|
22
|
+
Query.new(MULTI_DB_GET_ROUTING_TABLE, **map)
|
26
23
|
end
|
27
24
|
|
28
25
|
def connection(connection)
|
@@ -24,59 +24,38 @@ module Neo4j::Driver
|
|
24
24
|
# @param routingTable current routing table of the given database.
|
25
25
|
# @param connectionPool connection pool.
|
26
26
|
# @return new cluster composition and an optional set of resolved initial router addresses.
|
27
|
-
def lookup_cluster_composition(
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
base_error = Exceptions::ServiceUnavailableException.new(NO_ROUTERS_AVAILABLE % routing_table.database.description)
|
32
|
-
|
33
|
-
lookup(routing_table, connection_pool, bookmark, impersonated_user, base_error)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
result.complete_exceptionally(error)
|
38
|
-
elsif !composition_lookup_result.nil?
|
39
|
-
result.complete(composition_lookup_result)
|
27
|
+
def lookup_cluster_composition(
|
28
|
+
routing_table, connection_pool, bookmark, impersonated_user,
|
29
|
+
failures = 0,
|
30
|
+
previous_delay = 0,
|
31
|
+
base_error = Exceptions::ServiceUnavailableException.new(NO_ROUTERS_AVAILABLE % routing_table.database.description))
|
32
|
+
|
33
|
+
lookup(routing_table, connection_pool, bookmark, impersonated_user, base_error) ||
|
34
|
+
if failures > @settings.max_routing_failures
|
35
|
+
# now we throw our saved error out
|
36
|
+
raise base_error
|
40
37
|
else
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
result.complete_exceptionally(base_error)
|
46
|
-
else
|
47
|
-
next_delay = java.lang.Math.max(@settings.retry_timeout_delay, previous_delay * 2)
|
48
|
-
@log.info("Unable to fetch new routing table, will try again in #{next_delay} ms")
|
49
|
-
|
50
|
-
@event_executor_group.next.schedule(next_delay, java.util.concurrent.TimeUnit::MILLISECONDS) do
|
51
|
-
lookup_cluster_composition(routing_table, connection_pool, bookmark, impersonated_user, new_failures, next_delay, result, base_error)
|
52
|
-
end
|
53
|
-
end
|
38
|
+
next_delay = [@settings.retry_timeout_delay, previous_delay * 2].max
|
39
|
+
@log.info("Unable to fetch new routing table, will try again in #{next_delay} ms")
|
40
|
+
sleep next_delay
|
41
|
+
lookup_cluster_composition(routing_table, connection_pool, bookmark, impersonated_user, failures + 1, next_delay, base_error)
|
54
42
|
end
|
55
|
-
end
|
56
|
-
|
57
|
-
result
|
58
43
|
end
|
59
44
|
|
60
45
|
def resolve
|
61
|
-
resolved_addresses = java.util.LinkedList.new
|
62
46
|
exception = nil
|
63
47
|
|
64
|
-
@resolver.
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
exception.add_suppressed(e)
|
72
|
-
end
|
73
|
-
end
|
48
|
+
resolved_addresses = @resolver.call(@initial_router).flat_map do |server_address|
|
49
|
+
resolve_all_by_domain_name(server_address).unicast_stream
|
50
|
+
# rescue java.net.UnknownHostException => e
|
51
|
+
rescue => e
|
52
|
+
exception&.add_suppressed(e)
|
53
|
+
exception ||= e
|
54
|
+
[]
|
74
55
|
end
|
75
56
|
|
76
57
|
# give up only if there are no addresses to work with at all
|
77
|
-
if resolved_addresses.empty? &&
|
78
|
-
raise exception
|
79
|
-
end
|
58
|
+
raise exception if resolved_addresses.empty? && exception
|
80
59
|
|
81
60
|
resolved_addresses
|
82
61
|
end
|
@@ -84,153 +63,84 @@ module Neo4j::Driver
|
|
84
63
|
private
|
85
64
|
|
86
65
|
def lookup(routing_table, connection_pool, bookmark, impersonated_user, base_error)
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
66
|
+
if routing_table.prefer_initial_router
|
67
|
+
lookup_on_initial_router_then_on_known_routers(routing_table, connection_pool, bookmark, impersonated_user, base_error)
|
68
|
+
else
|
69
|
+
lookup_on_known_routers_then_on_initial_router(routing_table, connection_pool, bookmark, impersonated_user, base_error)
|
70
|
+
end
|
92
71
|
end
|
93
72
|
|
94
73
|
def lookup_on_known_routers_then_on_initial_router(routing_table, connection_pool, bookmark, impersonated_user, base_error)
|
95
|
-
seen_servers =
|
96
|
-
|
97
|
-
lookup_on_known_routers(routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error).then_compose do |composition_lookup_result|
|
98
|
-
if composition_lookup_result.nil?
|
99
|
-
java.util.concurrent.CompletableFuture.completed_future(composition_lookup_result)
|
100
|
-
end
|
101
|
-
|
74
|
+
seen_servers = Set.new
|
75
|
+
lookup_on_known_routers(routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error) ||
|
102
76
|
lookup_on_initial_router(routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
|
103
|
-
end
|
104
77
|
end
|
105
78
|
|
106
79
|
def lookup_on_initial_router_then_on_known_routers(routing_table, connection_pool, bookmark, impersonated_user, base_error)
|
107
|
-
|
108
|
-
|
109
|
-
lookup_on_initial_router(routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error).then_compose do |composition_lookup_result|
|
110
|
-
if composition_lookup_result.nil?
|
111
|
-
java.util.concurrent.CompletableFuture.completed_future(composition_lookup_result)
|
112
|
-
end
|
113
|
-
|
114
|
-
lookup_on_known_routers(routing_table, connection_pool, {}, bookmark, impersonated_user, base_error)
|
115
|
-
end
|
80
|
+
lookup_on_initial_router(routing_table, connection_pool, Set.new, bookmark, impersonated_user, base_error) ||
|
81
|
+
lookup_on_known_routers(routing_table, connection_pool, Set.new, bookmark, impersonated_user, base_error)
|
116
82
|
end
|
117
83
|
|
118
84
|
def lookup_on_known_routers(routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
result = result.then_compose do |composition|
|
123
|
-
if !composition.nil?
|
124
|
-
java.util.concurrent.CompletableFuture.completed_future(composition)
|
125
|
-
else
|
126
|
-
lookup_on_router(address, true, routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
result.then_apply do |composition|
|
132
|
-
composition.nil? ? nil : ClusterCompositionLookupResult.new(composition)
|
133
|
-
end
|
85
|
+
routing_table.routers.lazy.map do |address|
|
86
|
+
lookup_on_router(address, true, routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
|
87
|
+
end.first&.then(&ClusterCompositionLookupResult.method(:new))
|
134
88
|
end
|
135
89
|
|
136
90
|
def lookup_on_initial_router(routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
end
|
142
|
-
|
143
|
-
resolved_router_set = [resolved_routers]
|
144
|
-
resolved_routers - seen_servers
|
145
|
-
|
146
|
-
result = Util::Futures.completed_with_null
|
147
|
-
|
148
|
-
resolved_routers.each do |address|
|
149
|
-
result = result.then_compose do |composition|
|
150
|
-
if !composition.nil?
|
151
|
-
java.util.concurrent.CompletableFuture.completed_future(composition)
|
152
|
-
else
|
153
|
-
lookup_on_router(address, false, routing_table, connection_pool, nil, bookmark, impersonated_user, base_error)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
result.then_apply do |composition|
|
159
|
-
composition.nil? ? nil : ClusterCompositionLookupResult.new(composition, resolved_router_set)
|
160
|
-
end
|
91
|
+
resolved_routers = resolve
|
92
|
+
(resolved_routers - seen_servers.to_a).lazy.filter_map do |address|
|
93
|
+
lookup_on_router(address, false, routing_table, connection_pool, nil, bookmark,
|
94
|
+
impersonated_user, base_error)
|
95
|
+
end.first&.then { |composition| ClusterCompositionLookupResult.new(composition, Set.new(resolved_routers)) }
|
161
96
|
end
|
162
97
|
|
163
98
|
def lookup_on_router(router_address, resolve_address, routing_table, connection_pool, seen_servers, bookmark, impersonated_user, base_error)
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
connection_pool::acquire
|
172
|
-
end.then_apply do |connection|
|
173
|
-
ImpersonationUtil.ensure_impersonation_support(connection, impersonated_user)
|
174
|
-
end.then_compose do |connection|
|
175
|
-
@provider.get_cluster_composition(connection, routing_table.database, bookmark, impersonated_user)
|
176
|
-
end.handle do |response, error|
|
177
|
-
cause = Util::Futures.completion_exception_cause(error)
|
178
|
-
|
179
|
-
if !cause.nil?
|
180
|
-
handle_routing_procedure_error(cause, routing_table, router_address, base_error)
|
181
|
-
else
|
182
|
-
response
|
183
|
-
end
|
184
|
-
end
|
99
|
+
address = resolve_address ? resolve_by_domain_name_or_throw_completion_exception(router_address, routing_table) : router_address
|
100
|
+
seen_servers&.send(:<<, address)
|
101
|
+
connection = connection_pool.acquire(address)
|
102
|
+
ImpersonationUtil.ensure_impersonation_support(connection, impersonated_user)
|
103
|
+
@provider.get_cluster_composition(connection, routing_table.database, bookmark, impersonated_user)
|
104
|
+
rescue => error
|
105
|
+
handle_routing_procedure_error(error, routing_table, router_address, base_error)
|
185
106
|
end
|
186
107
|
|
187
108
|
def handle_routing_procedure_error(error, routing_table, router_address, base_error)
|
188
|
-
raise
|
109
|
+
raise error if must_abort_discovery(error)
|
189
110
|
|
190
111
|
# Retriable error happened during discovery.
|
191
|
-
discovery_error = Exceptions::DiscoveryException.new(RECOVERABLE_ROUTING_ERROR % router_address, error)
|
112
|
+
discovery_error = Exceptions::DiscoveryException.new(nil, RECOVERABLE_ROUTING_ERROR % router_address, error)
|
192
113
|
Util::Futures.combine_errors(base_error, discovery_error) # we record each failure here
|
193
114
|
warning_message = RECOVERABLE_DISCOVERY_ERROR_WITH_SERVER % router_address
|
194
115
|
@log.warn(warning_message)
|
195
|
-
@log.debug(
|
116
|
+
@log.debug(discovery_error)
|
196
117
|
routing_table.forget(router_address)
|
197
118
|
nil
|
198
119
|
end
|
199
120
|
|
200
|
-
def must_abort_discovery(
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
end
|
211
|
-
end
|
212
|
-
|
213
|
-
def add_and_return(collection, element)
|
214
|
-
collection << element unless collection.nil?
|
215
|
-
|
216
|
-
element
|
121
|
+
def must_abort_discovery(error)
|
122
|
+
!error.is_a?(Exceptions::AuthorizationExpiredException) && error.is_a?(Exceptions::SecurityException) ||
|
123
|
+
error.is_a?(Exceptions::FatalDiscoveryException) ||
|
124
|
+
error.is_a?(Exceptions::IllegalStateException) &&
|
125
|
+
Spi::ConnectionPool::CONNECTION_POOL_CLOSED_ERROR_MESSAGE == error.message ||
|
126
|
+
error.is_a?(Exceptions::ClientException) &&
|
127
|
+
[INVALID_BOOKMARK_CODE, INVALID_BOOKMARK_MIXTURE_CODE].include?(error.code) ||
|
128
|
+
# Not sure why this is not im java
|
129
|
+
!error.is_a?(Exceptions::Neo4jException) ||
|
130
|
+
Util::ErrorUtil.fatal?(error)
|
217
131
|
end
|
218
132
|
|
219
133
|
def resolve_by_domain_name_or_throw_completion_exception(address, routing_table)
|
220
|
-
|
221
|
-
|
222
|
-
routing_table.replace_router_if_present(address, resolved_address)
|
134
|
+
resolved_address = resolve_all_by_domain_name(address)
|
135
|
+
routing_table.replace_router_if_present(address, resolved_address)
|
223
136
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
rescue StandardError => e
|
228
|
-
raise java.util.concurrent.CompletionException, e
|
229
|
-
end
|
137
|
+
resolved_address.unicast_stream.first or
|
138
|
+
raise Exceptions::IllegalStateException,
|
139
|
+
'Unexpected condition, the ResolvedBoltServerAddress must always have at least one unicast address'
|
230
140
|
end
|
231
141
|
|
232
142
|
def resolve_all_by_domain_name(address)
|
233
|
-
ResolvedBoltServerAddress.new(address.host, address.port, domain_name_resolver.
|
143
|
+
ResolvedBoltServerAddress.new(address.host, address.port, *@domain_name_resolver.call(address.host))
|
234
144
|
end
|
235
145
|
end
|
236
146
|
end
|
@@ -16,9 +16,9 @@ module Neo4j::Driver
|
|
16
16
|
Messaging::Request::RouteMessage.new(@routing_context, bookmark, database_name.database_name,
|
17
17
|
impersonated_user),
|
18
18
|
Handlers::RouteMessageResponseHandler.new(self))
|
19
|
-
RoutingProcedureResponse.new(query(database_name), to_record(routing_table))
|
19
|
+
RoutingProcedureResponse.new(query(database_name), records: [to_record(@routing_table)])
|
20
20
|
rescue => e
|
21
|
-
RoutingProcedureResponse.new(query(database_name), e)
|
21
|
+
RoutingProcedureResponse.new(query(database_name), error: e)
|
22
22
|
ensure
|
23
23
|
direct_connection.release
|
24
24
|
end
|
@@ -4,34 +4,30 @@ module Neo4j::Driver
|
|
4
4
|
class RoutingProcedureClusterCompositionProvider
|
5
5
|
PROTOCOL_ERROR_MESSAGE = "Failed to parse '%s' result received from server due to "
|
6
6
|
|
7
|
-
def initialize(
|
8
|
-
@clock = clock
|
7
|
+
def initialize(_clock, routing_context)
|
9
8
|
@single_database_routing_procedure_runner = SingleDatabaseRoutingProcedureRunner.new(routing_context)
|
10
9
|
@multi_database_routing_procedure_runner = MultiDatabasesRoutingProcedureRunner.new(routing_context)
|
11
10
|
@route_message_routing_procedure_runner = RouteMessageRoutingProcedureRunner.new(routing_context)
|
12
11
|
end
|
13
12
|
|
14
13
|
def get_cluster_composition(connection, database_name, bookmark, impersonated_user)
|
15
|
-
runner = if Messaging::Request::MultiDatabaseUtil.supports_route_message(connection)
|
14
|
+
runner = if Messaging::Request::MultiDatabaseUtil.supports_route_message?(connection)
|
16
15
|
@route_message_routing_procedure_runner
|
17
|
-
elsif Messaging::Request::MultiDatabaseUtil.supports_multi_database(connection)
|
16
|
+
elsif Messaging::Request::MultiDatabaseUtil.supports_multi_database?(connection)
|
18
17
|
@multi_database_routing_procedure_runner
|
19
18
|
else
|
20
19
|
@single_database_routing_procedure_runner
|
21
20
|
end
|
22
21
|
|
23
|
-
runner.run(connection, database_name, bookmark, impersonated_user)
|
24
|
-
self::process_routing_response
|
25
|
-
end
|
22
|
+
process_routing_response(runner.run(connection, database_name, bookmark, impersonated_user))
|
26
23
|
end
|
27
24
|
|
28
25
|
def process_routing_response(response)
|
29
|
-
|
30
|
-
raise
|
26
|
+
unless response.success?
|
27
|
+
raise response.error.class, "Failed to run '#{invoked_procedure_string(response)}' on server. Please make sure that there is a Neo4j server or cluster up running."
|
31
28
|
end
|
32
29
|
|
33
30
|
records = response.records
|
34
|
-
now = clock.millis
|
35
31
|
|
36
32
|
# the record size is wrong
|
37
33
|
if records.size != 1
|
@@ -40,7 +36,7 @@ module Neo4j::Driver
|
|
40
36
|
|
41
37
|
# failed to parse the record
|
42
38
|
begin
|
43
|
-
cluster = ClusterComposition.parse( records[0], now)
|
39
|
+
cluster = ClusterComposition.parse( records[0], Time.now)
|
44
40
|
rescue Exceptions::Value::ValueException => e
|
45
41
|
raise Exceptions::ProtocolException, "#{PROTOCOL_ERROR_MESSAGE % invoked_procedure_string(response)} unparsable record received. #{e}"
|
46
42
|
end
|
@@ -56,7 +52,7 @@ module Neo4j::Driver
|
|
56
52
|
|
57
53
|
def invoked_procedure_string(response)
|
58
54
|
query = response.procedure
|
59
|
-
query.text
|
55
|
+
"#{query.text} #{query.parameters}"
|
60
56
|
end
|
61
57
|
end
|
62
58
|
end
|
@@ -2,16 +2,32 @@ module Neo4j::Driver
|
|
2
2
|
module Internal
|
3
3
|
module Cluster
|
4
4
|
class RoutingProcedureResponse
|
5
|
-
attr_reader :procedure
|
5
|
+
attr_reader :procedure
|
6
6
|
|
7
|
-
def initialize(procedure, records
|
7
|
+
def initialize(procedure, records: nil, error: nil)
|
8
8
|
@procedure = procedure
|
9
9
|
@records = records
|
10
10
|
@error = error
|
11
11
|
end
|
12
12
|
|
13
13
|
def success?
|
14
|
-
|
14
|
+
!@records.nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
def records
|
18
|
+
if success?
|
19
|
+
@records
|
20
|
+
else
|
21
|
+
raise Exceptions::IllegalStateException, "Can't access records of a failed result #{@error}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def error
|
26
|
+
if success?
|
27
|
+
raise Exceptions::IllegalStateException, "Can't access error of a succeeded result #{@records}"
|
28
|
+
else
|
29
|
+
@error
|
30
|
+
end
|
15
31
|
end
|
16
32
|
end
|
17
33
|
end
|