neo4j-ruby-driver 4.4.0.alpha.6 → 4.4.0.alpha.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +4 -4
  2. data/lib/neo4j/driver/exceptions/protocol_exception.rb +2 -2
  3. data/lib/neo4j/driver/internal/bolt_server_address.rb +6 -6
  4. data/lib/neo4j/driver/types/time.rb +4 -2
  5. data/ruby/neo4j/driver/internal/async/network_session.rb +4 -3
  6. data/ruby/neo4j/driver/internal/async/pool/{netty_channel_tracker.rb → channel_tracker.rb} +6 -8
  7. data/ruby/neo4j/driver/internal/async/pool/connection_pool_impl.rb +3 -3
  8. data/ruby/neo4j/driver/internal/cluster/cluster_composition.rb +10 -20
  9. data/ruby/neo4j/driver/internal/cluster/cluster_composition_lookup_result.rb +2 -2
  10. data/ruby/neo4j/driver/internal/cluster/cluster_routing_table.rb +37 -54
  11. data/ruby/neo4j/driver/internal/cluster/identity_resolver.rb +1 -4
  12. data/ruby/neo4j/driver/internal/cluster/loadbalancing/least_connected_load_balancing_strategy.rb +6 -6
  13. data/ruby/neo4j/driver/internal/cluster/loadbalancing/load_balancer.rb +44 -80
  14. data/ruby/neo4j/driver/internal/cluster/multi_databases_routing_procedure_runner.rb +6 -9
  15. data/ruby/neo4j/driver/internal/cluster/rediscovery_impl.rb +65 -155
  16. data/ruby/neo4j/driver/internal/cluster/route_message_routing_procedure_runner.rb +2 -2
  17. data/ruby/neo4j/driver/internal/cluster/routing_procedure_cluster_composition_provider.rb +8 -12
  18. data/ruby/neo4j/driver/internal/cluster/routing_procedure_response.rb +19 -3
  19. data/ruby/neo4j/driver/internal/cluster/routing_table_handler_impl.rb +46 -67
  20. data/ruby/neo4j/driver/internal/cluster/routing_table_registry_impl.rb +42 -61
  21. data/ruby/neo4j/driver/internal/cluster/single_database_routing_procedure_runner.rb +8 -10
  22. data/ruby/neo4j/driver/internal/cursor/async_result_cursor_impl.rb +2 -1
  23. data/ruby/neo4j/driver/internal/cursor/disposable_async_result_cursor.rb +11 -14
  24. data/ruby/neo4j/driver/internal/database_name_util.rb +3 -3
  25. data/ruby/neo4j/driver/internal/default_bookmark_holder.rb +1 -7
  26. data/ruby/neo4j/driver/internal/direct_connection_provider.rb +1 -1
  27. data/ruby/neo4j/driver/internal/driver_factory.rb +4 -4
  28. data/ruby/neo4j/driver/internal/handlers/legacy_pull_all_response_handler.rb +34 -24
  29. data/ruby/neo4j/driver/internal/impersonation_util.rb +2 -2
  30. data/ruby/neo4j/driver/internal/internal_bookmark.rb +1 -1
  31. data/ruby/neo4j/driver/internal/internal_database_name.rb +3 -5
  32. data/ruby/neo4j/driver/internal/messaging/bolt_protocol_version.rb +3 -1
  33. data/ruby/neo4j/driver/internal/messaging/encode/route_message_encoder.rb +8 -2
  34. data/ruby/neo4j/driver/internal/messaging/encode/route_v44_message_encoder.rb +8 -13
  35. data/ruby/neo4j/driver/internal/messaging/request/begin_message.rb +2 -3
  36. data/ruby/neo4j/driver/internal/messaging/request/multi_database_util.rb +2 -2
  37. data/ruby/neo4j/driver/internal/messaging/request/route_message.rb +5 -10
  38. data/ruby/neo4j/driver/internal/messaging/request/run_with_metadata_message.rb +5 -3
  39. data/ruby/neo4j/driver/internal/messaging/request/transaction_metadata_builder.rb +2 -2
  40. data/ruby/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +1 -1
  41. data/ruby/neo4j/driver/internal/messaging/v44/message_writer_v44.rb +1 -1
  42. data/ruby/neo4j/driver/internal/read_only_bookmark_holder.rb +13 -0
  43. data/ruby/neo4j/driver/internal/resolved_bolt_server_address.rb +35 -0
  44. data/ruby/neo4j/driver/internal/security/security_plan_impl.rb +14 -9
  45. data/ruby/neo4j/driver/internal/util/error_util.rb +1 -1
  46. data/ruby/neo4j/driver/net/{server_address1.rb → server_address.rb} +2 -2
  47. data/ruby/neo4j/driver/query.rb +1 -1
  48. data/ruby/neo4j/driver/transaction_config.rb +5 -1
  49. data/ruby/neo4j/driver/values.rb +3 -3
  50. data/ruby/neo4j/driver/version.rb +1 -1
  51. metadata +6 -5
  52. 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(routing_table, connection_pool, bookmark, impersonated_user, failures = nil, previous_delay = nil, result = nil, base_error = nil)
28
- result = java.util.concurrent.CompletableFuture.new
29
-
30
- # if we failed discovery, we will chain all errors into this one.
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).when_complete do |composition_lookup_result, completion_error|
34
- error = Util::Futures.completion_exception_cause(completion_error)
35
-
36
- if !error.nil?
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
- new_failures = failures + 1
42
-
43
- if new_failures >= @settings.max_routing_failures
44
- # now we throw our saved error out
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.resolve(initial_router).each do |server_address|
65
- begin
66
- resolve_all_by_domain_name(server_address).unicast_stream.for_each(resolved_addresses::add)
67
- rescue java.net.UnknownHostException => e
68
- if exception.nil?
69
- exception = e
70
- else
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? && !exception.nil?
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
- composition_stage = if routing_table.prefer_initial_router
88
- lookup_on_initial_router_then_on_known_routers(routing_table, connection_pool, bookmark, impersonated_user, base_error)
89
- else
90
- lookup_on_known_routers_then_on_initial_router(routing_table, connection_pool, bookmark, impersonated_user, base_error)
91
- end
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
- seen_servers = [].freeze
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
- result = Util::Futures.completed_with_null
120
-
121
- routing_table.routers.each do |address|
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
- begin
138
- resolved_routers = resolve
139
- rescue StandardError => error
140
- Util::Futures.failed_future(error)
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
- address_future = java.util.concurrent.CompletableFuture.completed_future(router_address)
165
-
166
- address_future.then_apply do |address|
167
- resolve_address ? resolve_by_domain_name_or_throw_completion_exception(address, routing_table) : address
168
- end.then_apply do |address|
169
- add_and_return(seen_servers, address)
170
- end.then_compose do
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 java.util.concurrent.CompletionException, error if must_abort_discovery(error)
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(warning_message, discovery_error)
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(throwable)
201
- abort = if !(throwable.is_a? Exceptions::AuthorizationExpiredException) && (throwable.is_a? Exceptions::SecurityException)
202
- true
203
- elsif throwable.is_a? Exceptions::FatalDiscoveryException
204
- true
205
- elsif throwable.is_a? Exceptions::IllegalStateException && Spi::ConnectionPool::CONNECTION_POOL_CLOSED_ERROR_MESSAGE == throwable.message
206
- true
207
- elsif throwable.is_a? Exceptions::ClientException
208
- code = throwable.code
209
- INVALID_BOOKMARK_CODE.eql?(code) || INVALID_BOOKMARK_MIXTURE_CODE.eql?(code)
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
- begin
221
- resolved_address = resolve_all_by_domain_name(address)
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
- resolved_address.unicast_stream.find_first.or_else_throw do
225
- Exceptions::IllegalStateException.new('Unexpected condition, the ResolvedBoltServerAddress must always have at least one unicast address')
226
- end
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.resolve(address.host))
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(clock, routing_context)
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).then_apply do
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
- if !response.success?
30
- raise java.util.concurrent.CompletionException, "Failed to run '#{invoked_procedure_string(response)}' on server. Please make sure that there is a Neo4j server or cluster up running.", response.error
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 + '' + query.parameters
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, :records, :error
5
+ attr_reader :procedure
6
6
 
7
- def initialize(procedure, records = nil, error = nil)
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
- !records.nil?
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, routing_table_purge_delay_ms)
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
- @routing_table_purge_delay_ms = routing_table_purge_delay_ms
17
- @resolved_initial_routers = []
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
- # refresh is already happening concurrently, just use it's result
32
- return @refresh_routing_table_future unless @refresh_routing_table_future.nil?
33
-
34
- if routing_table.stale_for?(context.mode)
35
- # existing routing table is not fresh and should be updated
36
- @log.debug( "Routing table for database '#{@database_name.description}' is stale. #{routing_table}")
37
-
38
- result_future = java.util.concurrent.CompletableFuture.new
39
- @refresh_routing_table_future = result_future
40
-
41
- @rediscovery.lookup_cluster_composition(routing_table, @connection_pool, context.rediscovery_bookmark, nil).when_complete do |composition, completion_error|
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
- result_future
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
- if !@refresh_routing_table_future.nil?
60
- # refresh is already happening concurrently, just use its result
61
- @refresh_routing_table_future
62
- else
63
- if composition_lookup_result.get_cluster_composition.expiration_timestamp < routing_table.expiration_timestamp
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 def fresh_cluster_composition_fetched(composition_lookup_result)
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
- addresses_to_retain = []
81
- @routing_table_registry.all_servers.stream.flat_map(BoltServerAddress::unicast_stream).for_each(addresses_to_retain::add)
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
- composition_lookup_result.resolved_initial_routers.if_present do |addresses|
84
- @resolved_initial_routers.clear
85
- @resolved_initial_routers << addresses
86
- end
65
+ composition_lookup_result.resolved_initial_routers&.then do |addresses|
66
+ addresses_to_retain << addresses
67
+ end
87
68
 
88
- addresses_to_retain << @resolved_initial_routers
89
- @connection_pool.retain_all(addresses_to_retain)
69
+ @connection_pool.retain_all(addresses_to_retain)
90
70
 
91
- @log.debug("Updated routing table for database '#{@database_name.description}'. #{routing_table}")
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
- routing_table_future = @refresh_routing_table_future
94
- @refresh_routing_table_future = nil
95
- routing_table_future.complete( routing_table)
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
- private def cluster_composition_lookup_failed(error)
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
- @refresh_routing_table_future.nil? && routing_table.has_been_stale_for?(@routing_table_purge_delay_ms)
90
+ @mutex.with_read_lock { @routing_table.has_been_stale_for?(@routing_table_purge_delay) }
112
91
  end
113
92
  end
114
93
  end