neo4j-ruby-driver 4.4.0.alpha.6 → 4.4.0.alpha.7
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/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/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 +37 -54
- 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 +8 -10
- data/ruby/neo4j/driver/internal/cursor/async_result_cursor_impl.rb +2 -1
- data/ruby/neo4j/driver/internal/cursor/disposable_async_result_cursor.rb +11 -14
- 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/legacy_pull_all_response_handler.rb +34 -24
- 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/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/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 +1 -1
- 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/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 +6 -5
- data/ruby/neo4j/driver/internal/database_name.rb +0 -12
@@ -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
|
@@ -6,109 +6,88 @@ module Neo4j::Driver
|
|
6
6
|
|
7
7
|
delegate :servers, to: :routing_table
|
8
8
|
|
9
|
-
def initialize(routing_table, rediscovery, connection_pool, routing_table_registry, logger,
|
9
|
+
def initialize(routing_table, rediscovery, connection_pool, routing_table_registry, logger, routing_table_purge_delay)
|
10
10
|
@routing_table = routing_table
|
11
11
|
@database_name = routing_table.database
|
12
12
|
@rediscovery = rediscovery
|
13
13
|
@connection_pool = connection_pool
|
14
14
|
@routing_table_registry = routing_table_registry
|
15
15
|
@log = logger
|
16
|
-
@
|
17
|
-
@
|
18
|
-
@refresh_routing_table_future = nil
|
16
|
+
@routing_table_purge_delay = routing_table_purge_delay
|
17
|
+
@mutex = Concurrent::ReentrantReadWriteLock.new
|
19
18
|
end
|
20
19
|
|
21
20
|
def on_connection_failure(address)
|
22
21
|
# remove server from the routing table, to prevent concurrent threads from making connections to this address
|
23
|
-
routing_table.forget(address)
|
22
|
+
@routing_table.forget(address)
|
24
23
|
end
|
25
24
|
|
26
25
|
def on_write_failure(address)
|
27
|
-
routing_table.forget_writer(address)
|
26
|
+
@routing_table.forget_writer(address)
|
28
27
|
end
|
29
28
|
|
30
29
|
def ensure_routing_table(context)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
error = Util::Futures.completion_exception_cause(completion_error)
|
43
|
-
|
44
|
-
if error.nil?
|
45
|
-
fresh_cluster_composition_fetched(composition)
|
46
|
-
else
|
47
|
-
cluster_composition_lookup_failed(error)
|
48
|
-
end
|
30
|
+
@mutex.with_write_lock do
|
31
|
+
if @routing_table.stale_for?(context.mode)
|
32
|
+
# existing routing table is not fresh and should be updated
|
33
|
+
@log.debug("Routing table for database '#{@database_name.description}' is stale. #{@routing_table}")
|
34
|
+
|
35
|
+
fresh_cluster_composition_fetched(
|
36
|
+
@rediscovery.lookup_cluster_composition(@routing_table, @connection_pool, context.rediscovery_bookmark,
|
37
|
+
nil))
|
38
|
+
else
|
39
|
+
# existing routing table is fresh, use it
|
40
|
+
@routing_table
|
49
41
|
end
|
50
|
-
|
51
|
-
|
52
|
-
else
|
53
|
-
# existing routing table is fresh, use it
|
54
|
-
java.util.concurrent.CompletableFuture.completed_future(routing_table)
|
42
|
+
rescue => error
|
43
|
+
cluster_composition_lookup_failed(error)
|
55
44
|
end
|
56
45
|
end
|
57
46
|
|
58
47
|
def update_routing_table(composition_lookup_result)
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
return java.util.concurrent.CompletableFuture.completed_future(routing_table)
|
48
|
+
@mutex.with_write_lock do
|
49
|
+
if composition_lookup_result.cluster_composition.expiration_timestamp < @routing_table.expiration_timestamp
|
50
|
+
@routing_table
|
51
|
+
else
|
52
|
+
fresh_cluster_composition_fetched(composition_lookup_result)
|
65
53
|
end
|
66
|
-
|
67
|
-
result_future = java.util.concurrent.CompletableFuture.new
|
68
|
-
@refresh_routing_table_future = result_future
|
69
|
-
fresh_cluster_composition_fetched(composition_lookup_result)
|
70
|
-
result_future
|
71
54
|
end
|
72
55
|
end
|
73
56
|
|
74
|
-
private
|
75
|
-
begin
|
76
|
-
@log.debug("Fetched cluster composition for database '#{@database_name.description}'. #{composition_lookup_result.cluster_composition}")
|
77
|
-
routing_table.update(composition_lookup_result.cluster_composition)
|
78
|
-
@routing_table_registry.remove_aged
|
57
|
+
private
|
79
58
|
|
80
|
-
|
81
|
-
|
59
|
+
def fresh_cluster_composition_fetched(composition_lookup_result)
|
60
|
+
@log.debug("Fetched cluster composition for database '#{@database_name.description}'. #{composition_lookup_result.cluster_composition}")
|
61
|
+
@routing_table.update(composition_lookup_result.cluster_composition)
|
62
|
+
@routing_table_registry.remove_aged
|
63
|
+
addresses_to_retain = @routing_table_registry.all_servers.map(&:unicast_stream).reduce(&:+)
|
82
64
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
end
|
65
|
+
composition_lookup_result.resolved_initial_routers&.then do |addresses|
|
66
|
+
addresses_to_retain << addresses
|
67
|
+
end
|
87
68
|
|
88
|
-
|
89
|
-
@connection_pool.retain_all(addresses_to_retain)
|
69
|
+
@connection_pool.retain_all(addresses_to_retain)
|
90
70
|
|
91
|
-
|
71
|
+
@log.debug("Updated routing table for database '#{@database_name.description}'. #{routing_table}")
|
72
|
+
@routing_table
|
73
|
+
rescue => error
|
74
|
+
cluster_composition_lookup_failed(error)
|
75
|
+
end
|
92
76
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
rescue StandardError => error
|
97
|
-
cluster_composition_lookup_failed(error)
|
77
|
+
def cluster_composition_lookup_failed(error)
|
78
|
+
@log.error do
|
79
|
+
"Failed to update routing table for database '#{@database_name.description}'. Current routing table: #{@routing_table}."
|
98
80
|
end
|
81
|
+
@log.error(error)
|
82
|
+
@routing_table_registry.remove(@database_name)
|
83
|
+
raise error
|
99
84
|
end
|
100
85
|
|
101
|
-
|
102
|
-
@log.error("Failed to update routing table for database '#{database_name.description}'. Current routing table: #{routing_table}.", error)
|
103
|
-
@routing_table_registry.remove(database_name)
|
104
|
-
routing_table_future = @refresh_routing_table_future
|
105
|
-
@refresh_routing_table_future = nil
|
106
|
-
routing_table_future.complete_exceptionally(error)
|
107
|
-
end
|
86
|
+
public
|
108
87
|
|
109
88
|
# This method cannot be synchronized as it will be visited by all routing table handler's threads concurrently
|
110
89
|
def routing_table_aged?
|
111
|
-
@
|
90
|
+
@mutex.with_read_lock { @routing_table.has_been_stale_for?(@routing_table_purge_delay) }
|
112
91
|
end
|
113
92
|
end
|
114
93
|
end
|