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.
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