neo4j-ruby-driver 4.4.0.alpha.5 → 4.4.0.alpha.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) 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/config.rb +1 -1
  6. data/ruby/neo4j/driver/graph_database.rb +2 -2
  7. data/ruby/neo4j/driver/internal/async/inbound/inbound_message_dispatcher.rb +2 -3
  8. data/ruby/neo4j/driver/internal/async/network_session.rb +4 -3
  9. data/ruby/neo4j/driver/internal/async/pool/{netty_channel_tracker.rb → channel_tracker.rb} +6 -8
  10. data/ruby/neo4j/driver/internal/async/pool/connection_pool_impl.rb +3 -3
  11. data/ruby/neo4j/driver/internal/async/unmanaged_transaction.rb +18 -20
  12. data/ruby/neo4j/driver/internal/cluster/cluster_composition.rb +10 -20
  13. data/ruby/neo4j/driver/internal/cluster/cluster_composition_lookup_result.rb +2 -2
  14. data/ruby/neo4j/driver/internal/cluster/cluster_routing_table.rb +38 -55
  15. data/ruby/neo4j/driver/internal/cluster/identity_resolver.rb +1 -4
  16. data/ruby/neo4j/driver/internal/cluster/loadbalancing/least_connected_load_balancing_strategy.rb +6 -6
  17. data/ruby/neo4j/driver/internal/cluster/loadbalancing/load_balancer.rb +44 -80
  18. data/ruby/neo4j/driver/internal/cluster/multi_databases_routing_procedure_runner.rb +6 -9
  19. data/ruby/neo4j/driver/internal/cluster/rediscovery_impl.rb +65 -155
  20. data/ruby/neo4j/driver/internal/cluster/route_message_routing_procedure_runner.rb +2 -2
  21. data/ruby/neo4j/driver/internal/cluster/routing_procedure_cluster_composition_provider.rb +8 -12
  22. data/ruby/neo4j/driver/internal/cluster/routing_procedure_response.rb +19 -3
  23. data/ruby/neo4j/driver/internal/cluster/routing_table_handler_impl.rb +46 -67
  24. data/ruby/neo4j/driver/internal/cluster/routing_table_registry_impl.rb +42 -61
  25. data/ruby/neo4j/driver/internal/cluster/single_database_routing_procedure_runner.rb +15 -19
  26. data/ruby/neo4j/driver/internal/cursor/async_result_cursor_impl.rb +31 -19
  27. data/ruby/neo4j/driver/internal/cursor/disposable_async_result_cursor.rb +12 -15
  28. data/ruby/neo4j/driver/internal/database_name_util.rb +3 -3
  29. data/ruby/neo4j/driver/internal/default_bookmark_holder.rb +1 -7
  30. data/ruby/neo4j/driver/internal/direct_connection_provider.rb +1 -1
  31. data/ruby/neo4j/driver/internal/driver_factory.rb +4 -4
  32. data/ruby/neo4j/driver/internal/handlers/commit_tx_response_handler.rb +4 -4
  33. data/ruby/neo4j/driver/internal/handlers/legacy_pull_all_response_handler.rb +90 -51
  34. data/ruby/neo4j/driver/internal/handlers/pulln/auto_pull_response_handler.rb +33 -44
  35. data/ruby/neo4j/driver/internal/handlers/rollback_tx_response_handler.rb +7 -1
  36. data/ruby/neo4j/driver/internal/impersonation_util.rb +2 -2
  37. data/ruby/neo4j/driver/internal/internal_bookmark.rb +1 -1
  38. data/ruby/neo4j/driver/internal/internal_database_name.rb +3 -5
  39. data/ruby/neo4j/driver/internal/internal_result.rb +5 -5
  40. data/ruby/neo4j/driver/internal/internal_session.rb +1 -1
  41. data/ruby/neo4j/driver/internal/internal_transaction.rb +4 -4
  42. data/ruby/neo4j/driver/internal/messaging/bolt_protocol_version.rb +3 -1
  43. data/ruby/neo4j/driver/internal/messaging/encode/route_message_encoder.rb +8 -2
  44. data/ruby/neo4j/driver/internal/messaging/encode/route_v44_message_encoder.rb +8 -13
  45. data/ruby/neo4j/driver/internal/messaging/request/abstract_streaming_message.rb +4 -1
  46. data/ruby/neo4j/driver/internal/messaging/request/begin_message.rb +2 -3
  47. data/ruby/neo4j/driver/internal/messaging/request/multi_database_util.rb +2 -2
  48. data/ruby/neo4j/driver/internal/messaging/request/route_message.rb +5 -10
  49. data/ruby/neo4j/driver/internal/messaging/request/run_with_metadata_message.rb +5 -3
  50. data/ruby/neo4j/driver/internal/messaging/request/transaction_metadata_builder.rb +2 -2
  51. data/ruby/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb +8 -4
  52. data/ruby/neo4j/driver/internal/messaging/v44/message_writer_v44.rb +1 -1
  53. data/ruby/neo4j/driver/internal/read_only_bookmark_holder.rb +13 -0
  54. data/ruby/neo4j/driver/internal/resolved_bolt_server_address.rb +35 -0
  55. data/ruby/neo4j/driver/internal/security/security_plan_impl.rb +14 -9
  56. data/ruby/neo4j/driver/internal/util/error_util.rb +1 -1
  57. data/ruby/neo4j/driver/internal/util/result_holder.rb +70 -0
  58. data/ruby/neo4j/driver/net/{server_address1.rb → server_address.rb} +2 -2
  59. data/ruby/neo4j/driver/query.rb +1 -1
  60. data/ruby/neo4j/driver/transaction_config.rb +5 -1
  61. data/ruby/neo4j/driver/values.rb +3 -3
  62. data/ruby/neo4j/driver/version.rb +1 -1
  63. metadata +16 -14
  64. data/ruby/neo4j/driver/internal/database_name.rb +0 -12
@@ -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
@@ -2,78 +2,57 @@ module Neo4j::Driver
2
2
  module Internal
3
3
  module Cluster
4
4
  class RoutingTableRegistryImpl
5
- def initialize(connection_pool, rediscovery, clock, logger, routing_table_purge_delay_ms)
6
- @factory = RoutingTableHandlerFactory.new(connection_pool, rediscovery, clock, logger, routing_table_purge_delay_ms)
7
- @routing_table_handlers = Concurrent::Hash.new
8
- @principal_to_database_name_stage = {}
5
+ def initialize(connection_pool, rediscovery, clock, logger, routing_table_purge_delay)
6
+ @factory = RoutingTableHandlerFactory.new(connection_pool, rediscovery, clock, logger, routing_table_purge_delay)
7
+ @routing_table_handlers = Concurrent::Map.new
8
+ @principal_to_database_name = {}
9
9
  @clock = clock
10
10
  @connection_pool = connection_pool
11
11
  @rediscovery = rediscovery
12
12
  @log = logger
13
+ @mutex = Mutex.new
13
14
  end
14
15
 
15
16
  def ensure_routing_table(context)
16
- ensure_database_name_is_completed(context).then_compose do |ctx_and_handler|
17
- completed_context = ctx_and_handler.context
18
- handler = ctx_and_handler.handler.nil? ? get_or_create(Util::Futures.join_now_or_else_throw(completed_context.database_name_future, Async::ConnectionContext::PENDING_DATABASE_NAME_EXCEPTION_SUPPLIER)) : ctx_and_handler.handler
19
- handler.ensure_routing_table(completed_context).then_apply(-> (_ignored) { handler })
20
- end
17
+ ctx_and_handler = ensure_database_name_is_completed(context)
18
+ (ctx_and_handler.handler || get_or_create(context.database_name))
19
+ .tap { |handler| handler.ensure_routing_table(ctx_and_handler.context) }
21
20
  end
22
21
 
23
22
  private def ensure_database_name_is_completed(context)
24
- context_database_name_future = context.database_name_future
23
+ context_database_name = context.database_name
24
+
25
+ return ConnectionContextAndHandler.new(context, nil) if context_database_name
26
+ @mutex.synchronize do
27
+ return ConnectionContextAndHandler.new(context, nil) if context_database_name
25
28
 
26
- if context_database_name_future.done?
27
- context_and_handler_stage = java.util.concurrent.CompletableFuture.completed_future(ConnectionContextAndHandler.new(context, nil))
28
- else
29
29
  impersonated_user = context.impersonated_user
30
30
  principal = Principal.new(impersonated_user)
31
- database_name_stage = @principal_to_database_name_stage[principal]
32
- handler_ref = java.util.concurrent.atomic.AtomicReference.new
33
-
34
- if database_name_stage.nil?
35
- database_name_future = java.util.concurrent.CompletableFuture.new
36
- @principal_to_database_name_stage[principal] = database_name_future
37
- database_name_stage = database_name_future
38
-
39
- routing_table = ClusterRoutingTable.new(DatabaseNameUtil::DEFAULT_DATABASE, @clock)
40
-
41
- @rediscovery.lookup_cluster_composition(routing_table, @connection_pool, context.rediscovery_bookmark, impersonated_user).then_compose do |composition_lookup_result|
42
- database_name = DatabaseNameUtil.database(composition_lookup_result.cluster_composition.database_name)
43
- handler = get_or_create(database_name)
44
- handler_ref.set(handler)
45
- handler.update_routing_table(composition_lookup_result).then_apply(-> (_ignored) { database_name })
46
- end.when_complete do |database_name, _throwable|
47
- @principal_to_database_name_stage.delete(principal)
48
- end.when_complete do |database_name, throwable|
49
- if throwable.nil?
50
- database_name_future.complete(database_name)
51
- else
52
- database_name_future.complete_exceptionally(throwable)
53
- end
54
- end
55
- end
31
+ database_name = @principal_to_database_name[principal]
32
+ handler_ref = Concurrent::AtomicReference.new
56
33
 
57
- context_and_handler_stage =
58
- database_name_stage.then_apply do |database_name|
59
- context_database_name_future.complete(database_name)
60
- ConnectionContextAndHandler.new(context, handler_ref)
34
+ if database_name.nil?
35
+ @principal_to_database_name[principal] = database_name
36
+
37
+ routing_table = ClusterRoutingTable.new(DatabaseNameUtil.default_database, @clock)
38
+
39
+ composition_lookup_result = @rediscovery.lookup_cluster_composition(routing_table, @connection_pool, context.rediscovery_bookmark, impersonated_user)
40
+ database_name = DatabaseNameUtil.database(composition_lookup_result.cluster_composition.database_name)
41
+ handler = get_or_create(database_name)
42
+ handler_ref.set(handler)
43
+ handler.update_routing_table(composition_lookup_result)
44
+ @principal_to_database_name.delete(principal)
61
45
  end
62
- end
63
46
 
64
- context_and_handler_stage
47
+ context.database_name = database_name
48
+ ConnectionContextAndHandler.new(context, handler_ref.get)
49
+ end
65
50
  end
66
51
 
67
52
  def all_servers
68
53
  # obviously we just had a snapshot of all servers in all routing tables
69
54
  # after we read it, the set could already be changed.
70
- servers = []
71
-
72
- @routing_table_handlers.values.each do |table_handler|
73
- servers << table_handler.servers
74
- end
75
-
76
- servers
55
+ @routing_table_handlers.values.map(&:servers).reduce(&:+)
77
56
  end
78
57
 
79
58
  def remove(database_name)
@@ -90,17 +69,19 @@ module Neo4j::Driver
90
69
  end
91
70
  end
92
71
 
93
- def get_routing_table_handler(database_name)
94
- java.util.Optional.of_nullable(@routing_table_handlers[database_name])
72
+ def routing_table_handler(database_name)
73
+ database_name = DatabaseNameUtil.database(database_name) unless database_name.is_a?(InternalDatabaseName)
74
+ @routing_table_handlers[database_name]
95
75
  end
96
76
 
97
- def contains(database_name)
98
- @routing_table_handlers.keys.include?(database_name)
99
- end
77
+ # For tests
78
+ delegate :key?, to: :@routing_table_handlers
100
79
 
101
80
  def get_or_create(database_name)
102
- @routing_table_handlers.compute_if_absent(database_name) do |name|
103
- handler = @factory.new_instance(name, self)
81
+ @routing_table_handlers.compute_if_absent(database_name) do
82
+ # TODO: Verify if applies
83
+ # Note: Atomic methods taking a block do not allow the self instance to be used within the block. Doing so will cause a deadlock.
84
+ handler = @factory.new_instance(database_name, self)
104
85
  @log.debug("Routing table handler for database '#{database_name.description}' is added.")
105
86
  handler
106
87
  end
@@ -109,17 +90,17 @@ module Neo4j::Driver
109
90
  private
110
91
 
111
92
  class RoutingTableHandlerFactory
112
- def initialize(connection_pool, rediscovery, clock, logger, routing_table_purge_delay_ms)
93
+ def initialize(connection_pool, rediscovery, clock, logger, routing_table_purge_delay)
113
94
  @connection_pool = connection_pool
114
95
  @rediscovery = rediscovery
115
96
  @clock = clock
116
97
  @logger = logger
117
- @routing_table_purge_delay_ms = routing_table_purge_delay_ms
98
+ @routing_table_purge_delay = routing_table_purge_delay
118
99
  end
119
100
 
120
101
  def new_instance(database_name, all_tables)
121
102
  routing_table = ClusterRoutingTable.new(database_name, @clock)
122
- RoutingTableHandlerImpl.new(routing_table, @rediscovery, @connection_pool, all_tables, @logger, @routing_table_purge_delay_ms)
103
+ RoutingTableHandlerImpl.new(routing_table, @rediscovery, @connection_pool, all_tables, @logger, @routing_table_purge_delay)
123
104
  end
124
105
  end
125
106
 
@@ -5,7 +5,7 @@ module Neo4j::Driver
5
5
  # This implementation of the {@link RoutingProcedureRunner} works with single database versions of Neo4j calling
6
6
  # the procedure `dbms.cluster.routing.getRoutingTable`
7
7
  class SingleDatabaseRoutingProcedureRunner
8
- ROUTING_CONTEXT = 'context'
8
+ ROUTING_CONTEXT = :context
9
9
  GET_ROUTING_TABLE = "CALL dbms.cluster.routing.getRoutingTable($#{ROUTING_CONTEXT})"
10
10
 
11
11
  def initialize(context)
@@ -16,17 +16,15 @@ module Neo4j::Driver
16
16
  delegate = connection(connection)
17
17
  procedure = procedure_query(connection.server_version, database_name)
18
18
  bookmark_holder = bookmark_holder(bookmark)
19
- run_procedure(delegate, procedure, bookmark_holder).then_compose do |records|
20
- release_connection(delegate, records).handle do |records, error|
21
- process_procedure_response(procedure, records, error)
22
- end
23
- end
19
+ run_procedure(delegate, procedure, bookmark_holder)
20
+ .side { release_connection(delegate) }
21
+ .chain { |records, error| process_procedure_response(procedure, records, error) }
24
22
  end
25
23
 
26
24
  private
27
25
 
28
26
  def connection(connection)
29
- Async::Connection::DirectConnection.new(connection, default_database, AccessMode::WRITE, nil)
27
+ Async::Connection::DirectConnection.new(connection, DatabaseNameUtil.default_database, AccessMode::WRITE, nil)
30
28
  end
31
29
 
32
30
  def procedure_query(server_version, database_name)
@@ -34,7 +32,7 @@ module Neo4j::Driver
34
32
  raise Exceptions::FatalDiscoveryException, "Refreshing routing table for multi-databases is not supported in server version lower than 4.0. Current server version: #{server_version}. Database name: '#{database_name.description}'"
35
33
  end
36
34
 
37
- Query.new(GET_ROUTING_TABLE, Values.parameters(ROUTING_CONTEXT, @context.to_map))
35
+ Query.new(GET_ROUTING_TABLE, ROUTING_CONTEXT => @context.to_h)
38
36
  end
39
37
 
40
38
  def bookmark_holder(_ignored)
@@ -45,30 +43,28 @@ module Neo4j::Driver
45
43
  connection.protocol
46
44
  .run_in_auto_commit_transaction(connection, procedure, bookmark_holder, TransactionConfig.empty,
47
45
  Handlers::Pulln::FetchSizeUtil::UNLIMITED_FETCH_SIZE)
48
- .async_result.then_compose(Async::ResultCursor::list_async)
46
+ .async_result.then(&:to_a)
49
47
  end
50
48
 
51
- def release_connection(connection, records)
49
+ def release_connection(connection)
52
50
  # It is not strictly required to release connection after routing procedure invocation because it'll
53
51
  # be released by the PULL_ALL response handler after result is fully fetched. Such release will happen
54
52
  # in background. However, releasing it early as part of whole chain makes it easier to reason about
55
53
  # rediscovery in stub server tests. Some of them assume connections to instances not present in new
56
54
  # routing table will be closed immediately.
57
- connection.release.then_apply(->(_ignore) { records } )
55
+ connection.release
58
56
  end
59
57
 
60
58
  def process_procedure_response(procedure, records, error)
61
- cause = Util::Futures.completion_exception_cause(error)
62
-
63
- return RoutingProcedureResponse.new(procedure, records) if cause.nil?
64
-
65
- handle_error(procedure, cause)
59
+ error ? handle_error(procedure, error) : RoutingProcedureResponse.new(procedure, records: records)
66
60
  end
67
61
 
68
62
  def handle_error(procedure, error)
69
- return RoutingProcedureResponse.new(procedure, records) if error.is_a? Exceptions::ClientException
70
-
71
- raise java.util.concurrent.CompletionException, error
63
+ if error.is_a? Exceptions::ClientException
64
+ RoutingProcedureResponse.new(procedure, error: error)
65
+ else
66
+ raise error
67
+ end
72
68
  end
73
69
  end
74
70
  end
@@ -2,6 +2,7 @@ module Neo4j::Driver
2
2
  module Internal
3
3
  module Cursor
4
4
  class AsyncResultCursorImpl
5
+ include Enumerable
5
6
  delegate :consume_async, :next_async, :peek_async, to: :@pull_all_handler
6
7
 
7
8
  def initialize(run_handler, pull_all_handler)
@@ -14,20 +15,24 @@ module Neo4j::Driver
14
15
  end
15
16
 
16
17
  def single_async
17
- first_record = next_async
18
- unless first_record
19
- raise Exceptions::NoSuchRecordException, 'Cannot retrieve a single record, because this result is empty.'
20
- end
21
- if next_async
22
- raise Exceptions::NoSuchRecordException,
23
- 'Expected a result with a single record, but this result contains at least one more. Ensure your query returns only one record.'
18
+ next_async.compose do |first_record|
19
+ unless first_record
20
+ raise Exceptions::NoSuchRecordException, 'Cannot retrieve a single record, because this result is empty.'
21
+ end
22
+ next_async.then do |second_record|
23
+ if second_record
24
+ raise Exceptions::NoSuchRecordException,
25
+ 'Expected a result with a single record, but this result contains at least one more. Ensure your query returns only one record.'
26
+ end
27
+ first_record
28
+ end
24
29
  end
25
- first_record
26
30
  end
27
31
 
28
- def each_async(&action)
29
- internal_for_each_async(&action)
30
- consume_async
32
+ def each(&action)
33
+ result_holder = Util::ResultHolder.new
34
+ internal_for_each_async(result_holder, &action)
35
+ result_holder.then { consume_async }
31
36
  end
32
37
 
33
38
  def to_async(&map_function)
@@ -36,7 +41,7 @@ module Neo4j::Driver
36
41
 
37
42
  def discard_all_failure_async
38
43
  # runError has priority over other errors and is expected to have been reported to user by now
39
- consume_async #.chain { |_fulfilled, _summary, error| @run_error ? nil : error }
44
+ consume_async.chain { |_summary, error| run_error ? nil : error }
40
45
  nil
41
46
  end
42
47
 
@@ -45,18 +50,25 @@ module Neo4j::Driver
45
50
  @pull_all_handler.pull_all_failure_async.then { |error| run_error ? nil : error }
46
51
  end
47
52
 
48
- private def internal_for_each_async
49
- while record = next_async
50
- begin
51
- yield record
52
- rescue
53
- nil
53
+ private def internal_for_each_async(result_holder, &action)
54
+ next_async.chain do |record, error|
55
+ if error
56
+ result_holder.fail(error)
57
+ elsif record
58
+ begin
59
+ yield record
60
+ rescue => action_error
61
+ result_holder.fail(action_error)
62
+ end
63
+ internal_for_each_async(result_holder, &action)
64
+ else
65
+ result_holder.succeed(nil)
54
66
  end
55
67
  end
56
68
  end
57
69
 
58
70
  def map_successful_run_completion_async
59
- run_error || self
71
+ run_error&.then(&Util::ResultHolder.method(:failed)) || Util::ResultHolder.successful(self)
60
72
  end
61
73
 
62
74
  def run_error
@@ -2,6 +2,7 @@ module Neo4j::Driver
2
2
  module Internal
3
3
  module Cursor
4
4
  class DisposableAsyncResultCursor
5
+ include Enumerable
5
6
  delegate :keys, :pull_all_failure_async, to: :@delegate
6
7
 
7
8
  def initialize(delegate)
@@ -14,25 +15,23 @@ module Neo4j::Driver
14
15
  end
15
16
 
16
17
  def next_async
17
- assert_not_disposed.then_compose(-> (_ignored) { @delegate.next_async })
18
+ assert_not_disposed
19
+ @delegate.next_async
18
20
  end
19
21
 
20
22
  def peek_async
21
- assert_not_disposed.then_compose(-> (_ignored) { @delegate.peek_async })
23
+ assert_not_disposed
24
+ @delegate.peek_async
22
25
  end
23
26
 
24
27
  def single_async
25
- assert_not_disposed.then_compose(-> (_ignored) { @delegate.single_async })
28
+ assert_not_disposed
29
+ @delegate.single_async
26
30
  end
27
31
 
28
- def for_each_async(action)
29
- assert_not_disposed.then_compose(-> (_ignored) { @delegate.for_each_async(action) })
30
- end
31
-
32
- def list_async(map_function = nil)
33
- return assert_not_disposed.then_compose(-> (_ignored) { @delegate.list_async(map_function) }) if map_function.present?
34
-
35
- assert_not_disposed.then_compose(-> (_ignored) { @delegate.list_async })
32
+ def each(&action)
33
+ assert_not_disposed
34
+ @delegate.each(&action)
36
35
  end
37
36
 
38
37
  def discard_all_failure_async
@@ -41,9 +40,7 @@ module Neo4j::Driver
41
40
  end
42
41
 
43
42
  private def assert_not_disposed
44
- return Util::Futures.failed_future(new_result_consumed_error) if @disposed
45
-
46
- Util::Futures.completed_with_null
43
+ raise Util::ErrorUtil.new_result_consumed_error if @disposed
47
44
  end
48
45
 
49
46
  def disposed?
@@ -51,7 +48,7 @@ module Neo4j::Driver
51
48
  end
52
49
 
53
50
  def map_successful_run_completion_async
54
- @delegate.map_successful_run_completion_async
51
+ @delegate.map_successful_run_completion_async.then { self }
55
52
  end
56
53
  end
57
54
  end
@@ -6,8 +6,8 @@ module Neo4j::Driver
6
6
 
7
7
  private
8
8
 
9
- DEFAULT_DATABASE = Struct.new(:database_name, :description).new(nil, '<default database>')
10
- SYSTEM_DATABASE = InternalDatabaseName.new(SYSTEM_DATABASE_NAME)
9
+ DEFAULT_DATABASE = InternalDatabaseName.new(description: '<default database>')
10
+ SYSTEM_DATABASE = InternalDatabaseName.new(database_name: SYSTEM_DATABASE_NAME)
11
11
 
12
12
  public
13
13
 
@@ -27,7 +27,7 @@ module Neo4j::Driver
27
27
  when SYSTEM_DATABASE_NAME
28
28
  system_database
29
29
  else
30
- InternalDatabaseName.new(name)
30
+ InternalDatabaseName.new(database_name: name)
31
31
  end
32
32
  end
33
33
  end
@@ -1,12 +1,6 @@
1
1
  module Neo4j::Driver
2
2
  module Internal
3
- class DefaultBookmarkHolder
4
- attr_reader :bookmark
5
-
6
- def initialize(bookmark = InternalBookmark.empty)
7
- @bookmark = bookmark
8
- end
9
-
3
+ class DefaultBookmarkHolder < ReadOnlyBookmarkHolder
10
4
  def bookmark=(bookmark)
11
5
  @bookmark = bookmark if bookmark.present?
12
6
  end
@@ -22,7 +22,7 @@ module Neo4j::Driver
22
22
 
23
23
  def supports_multi_db?
24
24
  private_acquire_connection.then do |conn|
25
- supports_multi_database?(conn)
25
+ Messaging::Request::MultiDatabaseUtil.supports_multi_database?(conn)
26
26
  ensure
27
27
  conn.release
28
28
  end
@@ -83,8 +83,8 @@ module Neo4j::Driver::Internal
83
83
  driver(:Direct, securityPlan, address, connection_provider, retryLogic, metricsProvider, config)
84
84
  end
85
85
 
86
- def create_routing_driver(securityPlan, address, connection_pool, eventExecutorGroup, routingSettings, retryLogic, metricsProvider, config)
87
- connection_provider = create_load_balancer(address, connection_pool, eventExecutorGroup, config, routingSettings)
86
+ def create_routing_driver(securityPlan, address, connection_pool, eventExecutorGroup, routing_settings, retryLogic, metricsProvider, config)
87
+ connection_provider = create_load_balancer(address, connection_pool, eventExecutorGroup, config, routing_settings)
88
88
  driver(:Routing, securityPlan, address, connection_provider, retryLogic, metricsProvider, config)
89
89
  end
90
90
 
@@ -95,11 +95,11 @@ module Neo4j::Driver::Internal
95
95
  end
96
96
  end
97
97
 
98
- def create_load_balancer(address, connection_pool, eventExecutorGroup, config, routingSettings)
98
+ def create_load_balancer(address, connection_pool, eventExecutorGroup, config, routing_settings)
99
99
  load_balancing_strategy = Cluster::Loadbalancing::LeastConnectedLoadBalancingStrategy.new(connection_pool, config[:logger])
100
100
  resolver = create_resolver(config)
101
101
  Cluster::Loadbalancing::LoadBalancer.new(
102
- address, routingSettings, connection_pool, eventExecutorGroup,
102
+ address, routing_settings, connection_pool, eventExecutorGroup,
103
103
  config[:logger], load_balancing_strategy, resolver, &method(:domain_name_resolver))
104
104
  end
105
105
 
@@ -4,16 +4,16 @@ module Neo4j::Driver
4
4
  class CommitTxResponseHandler
5
5
  include Spi::ResponseHandler
6
6
 
7
- def initialize(completion_listener)
8
- @completion_listener = completion_listener
7
+ def initialize(result_holder)
8
+ @result_holder = result_holder
9
9
  end
10
10
 
11
11
  def on_success(metadata)
12
- @completion_listener.bookmark = metadata[:bookmark]&.then(&InternalBookmark.method(:parse))
12
+ @result_holder.succeed(metadata[:bookmark]&.then(&InternalBookmark.method(:parse)))
13
13
  end
14
14
 
15
15
  def on_failure(error)
16
- raise error
16
+ @result_holder.fail(error)
17
17
  end
18
18
 
19
19
  def on_record(fields)