neo4j-ruby-driver 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ffi/bolt/config.rb +26 -1
- data/ffi/bolt/socket_options.rb +14 -0
- data/ffi/neo4j/driver.rb +2 -0
- data/ffi/neo4j/driver/config.rb +1 -1
- data/ffi/neo4j/driver/graph_database.rb +2 -2
- data/ffi/neo4j/driver/internal/async/direct_connection.rb +2 -1
- data/ffi/neo4j/driver/internal/direct_connection_provider.rb +3 -2
- data/ffi/neo4j/driver/internal/driver_factory.rb +27 -8
- data/ffi/neo4j/driver/internal/error_handling.rb +22 -0
- data/ffi/neo4j/driver/internal/handlers/pull_all_response_handler.rb +0 -2
- data/ffi/neo4j/driver/internal/handlers/response_handler.rb +3 -1
- data/ffi/neo4j/driver/internal/network_session.rb +1 -6
- data/ffi/neo4j/driver/internal/retry/exponential_backoff_retry_logic.rb +80 -0
- data/lib/neo4j/driver/version.rb +1 -1
- metadata +4 -3
- data/ffi/bolt/values_private.rb +0 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c7d38011d191d0e26b26b02e6b147672b27806cd66d9ebb416d326bc8ed0406
|
4
|
+
data.tar.gz: 8bef825331172211356c41ba82a7b5d5c927f939d6e0a716fb6a7f0a18cfb947
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ceec98de33377a4dea166d894ba5c6179ff7510744c6efbaa07fbb5dfbd237c0b08278587f9ebf410f601a41e006d395cbcc3f0ebd9114439348bc24bab25d3
|
7
|
+
data.tar.gz: 16c1bb6ba7b910c86f8dc605980c393a8a4f10e0fa57eeeeb109b6fea25efaacb5125f14518666960fc0050ea82a3a64d2c8bd260db79a084522af4a701fc0f4
|
data/ffi/bolt/config.rb
CHANGED
@@ -3,10 +3,35 @@
|
|
3
3
|
module Bolt
|
4
4
|
module Config
|
5
5
|
extend Bolt::Library
|
6
|
+
typedef :int32_t, :bolt_scheme
|
7
|
+
typedef :int32_t, :bolt_transport
|
8
|
+
|
6
9
|
attach_function :create, :BoltConfig_create, [], :auto_pointer
|
7
10
|
attach_function :destroy, :BoltConfig_destroy, [:pointer], :void
|
11
|
+
attach_function :get_scheme, :BoltConfig_get_scheme, [:pointer], :bolt_scheme
|
12
|
+
attach_function :set_scheme, :BoltConfig_set_scheme, %i[pointer bolt_scheme], :int32_t
|
13
|
+
attach_function :get_transport, :BoltConfig_get_transport, [:pointer], :bolt_transport
|
14
|
+
attach_function :set_transport, :BoltConfig_set_transport, %i[pointer bolt_transport], :int32_t
|
15
|
+
attach_function :get_trust, :BoltConfig_get_trust, [:pointer], :pointer
|
16
|
+
attach_function :set_trust, :BoltConfig_set_trust, %i[pointer pointer], :int32_t
|
17
|
+
attach_function :get_user_agent, :BoltConfig_get_user_agent, [:pointer], :string
|
18
|
+
attach_function :set_user_agent, :BoltConfig_set_user_agent, %i[pointer string], :int32
|
19
|
+
attach_function :get_routing_context, :BoltConfig_get_routing_context, [:pointer], :pointer
|
20
|
+
attach_function :set_routing_context, :BoltConfig_set_routing_context, %i[pointer pointer], :int32_t
|
21
|
+
attach_function :get_address_resolver, :BoltConfig_get_address_resolver, [:pointer], :pointer
|
22
|
+
attach_function :set_address_resolver, :BoltConfig_set_address_resolver, %i[pointer pointer], :int32_t
|
8
23
|
attach_function :get_log, :BoltConfig_get_log, [:pointer], :pointer
|
9
24
|
attach_function :set_log, :BoltConfig_set_log, %i[pointer pointer], :int32
|
10
|
-
attach_function :
|
25
|
+
attach_function :get_max_pool_size, :BoltConfig_get_max_pool_size, [:pointer], :int32_t
|
26
|
+
attach_function :set_max_pool_size, :BoltConfig_set_max_pool_size, %i[pointer int32_t], :int32_t
|
27
|
+
attach_function :get_max_connection_life_time, :BoltConfig_get_max_connection_life_time, [:pointer], :int32_t
|
28
|
+
attach_function :set_max_connection_life_time, :BoltConfig_set_max_connection_life_time, %i[pointer int32_t],
|
29
|
+
:int32_t
|
30
|
+
attach_function :get_max_connection_acquisition_time, :BoltConfig_get_max_connection_acquisition_time, [:pointer],
|
31
|
+
:int32_t
|
32
|
+
attach_function :set_max_connection_acquisition_time, :BoltConfig_set_max_connection_acquisition_time,
|
33
|
+
%i[pointer int32_t], :int32_t
|
34
|
+
attach_function :get_socket_options, :BoltConfig_get_socket_options, [:pointer], :pointer
|
35
|
+
attach_function :set_socket_options, :BoltConfig_set_socket_options, %i[pointer pointer], :int32_t
|
11
36
|
end
|
12
37
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bolt
|
4
|
+
module SocketOptions
|
5
|
+
extend Bolt::Library
|
6
|
+
|
7
|
+
attach_function :create, :BoltSocketOptions_create, [], :auto_pointer
|
8
|
+
attach_function :destroy, :BoltSocketOptions_destroy, [:pointer], :void
|
9
|
+
attach_function :get_connect_timeout, :BoltSocketOptions_get_connect_timeout, [:pointer], :int32_t
|
10
|
+
attach_function :set_connect_timeout, :BoltSocketOptions_set_connect_timeout, %i[pointer int32_t], :int32_t
|
11
|
+
attach_function :get_keep_alive, :BoltSocketOptions_get_keep_alive, [:pointer], :int32_t
|
12
|
+
attach_function :set_keep_alive, :BoltSocketOptions_set_keep_alive, %i[pointer int32_t], :int32_t
|
13
|
+
end
|
14
|
+
end
|
data/ffi/neo4j/driver.rb
CHANGED
data/ffi/neo4j/driver/config.rb
CHANGED
@@ -14,9 +14,9 @@ module Neo4j
|
|
14
14
|
|
15
15
|
auto_closable :driver
|
16
16
|
|
17
|
-
def driver(uri, auth_token = Neo4j::Driver::AuthTokens.none, config =
|
17
|
+
def driver(uri, auth_token = Neo4j::Driver::AuthTokens.none, config = nil)
|
18
18
|
raise Exceptions::AuthenticationException, 'Unsupported authentication token' unless auth_token
|
19
|
-
|
19
|
+
config ||= Config.default_config
|
20
20
|
# routing_settings = config.routing_settings
|
21
21
|
# retry_settings = config.retry_settings
|
22
22
|
routing_settings = nil
|
@@ -10,8 +10,9 @@ module Neo4j
|
|
10
10
|
attr_reader :protocol
|
11
11
|
attr_reader :bolt_connection
|
12
12
|
|
13
|
-
def initialize(connector, mode)
|
13
|
+
def initialize(connector, mode, config)
|
14
14
|
@connector = connector
|
15
|
+
@config = config
|
15
16
|
@bolt_connection = with_status { |status| Bolt::Connector.acquire(@connector, mode, status) }
|
16
17
|
|
17
18
|
# @protocol = Messaging::BoltProtocol.for_version(Bolt::Connection.server(bolt_connection).first)
|
@@ -6,12 +6,13 @@ module Neo4j
|
|
6
6
|
class DirectConnectionProvider
|
7
7
|
include ErrorHandling
|
8
8
|
|
9
|
-
def initialize(connector)
|
9
|
+
def initialize(connector, config)
|
10
10
|
@connector = connector
|
11
|
+
@config = config
|
11
12
|
end
|
12
13
|
|
13
14
|
def acquire_connection(mode)
|
14
|
-
Async::DirectConnection.new(@connector, mode)
|
15
|
+
Async::DirectConnection.new(@connector, mode, @config)
|
15
16
|
end
|
16
17
|
|
17
18
|
def verify_connectivity
|
@@ -13,7 +13,8 @@ module Neo4j
|
|
13
13
|
def new_instance(uri, auth_token, routing_settings, retry_settings, config)
|
14
14
|
uri = URI(uri)
|
15
15
|
connector, logger = create_connector(uri, auth_token, config)
|
16
|
-
|
16
|
+
retry_logic = Retry::ExponentialBackoffRetryLogic.new(config[:max_transaction_retry_time], config[:logger])
|
17
|
+
create_driver(uri.scheme, connector, logger, routing_settings, retry_logic, config).tap(&:verify_connectivity)
|
17
18
|
end
|
18
19
|
|
19
20
|
private
|
@@ -22,19 +23,37 @@ module Neo4j
|
|
22
23
|
address = Bolt::Address.create(host(uri).gsub(/^\[(.*)\]$/, '\\1'), port(uri).to_s)
|
23
24
|
bolt_config = bolt_config(config)
|
24
25
|
logger = InternalLogger.register(bolt_config, config[:logger])
|
26
|
+
set_socket_options(bolt_config, config)
|
25
27
|
[Bolt::Connector.create(address, auth_token, bolt_config), logger]
|
26
28
|
end
|
27
29
|
|
28
30
|
def bolt_config(config)
|
29
31
|
bolt_config = Bolt::Config.create
|
30
32
|
config.each do |key, value|
|
31
|
-
|
32
|
-
|
33
|
+
case key
|
34
|
+
when :max_connection_pool_size
|
35
|
+
check_error Bolt::Config.set_max_pool_size(bolt_config, value)
|
36
|
+
when :max_connection_life_time
|
37
|
+
check_error Bolt::Config.set_max_connection_life_time(bolt_config, value)
|
38
|
+
when :connection_acquisition_timeout
|
39
|
+
check_error Bolt::Config.set_max_connection_acquisition_time(bolt_config, value)
|
40
|
+
end
|
33
41
|
end
|
34
42
|
check_error Bolt::Config.set_user_agent(bolt_config, 'seabolt-cmake/1.7')
|
35
43
|
bolt_config
|
36
44
|
end
|
37
45
|
|
46
|
+
def set_socket_options(bolt_config, config)
|
47
|
+
socket_options = nil
|
48
|
+
config.each do |key, value|
|
49
|
+
case key
|
50
|
+
when :connection_timeout
|
51
|
+
check_error Bolt::SocketOptions.set_connect_timeout(socket_options ||= Bolt::SocketOptions.create, value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
check_error Bolt::Config.set_socket_options(bolt_config, socket_options) if socket_options
|
55
|
+
end
|
56
|
+
|
38
57
|
def host(uri)
|
39
58
|
uri.host.tap { |host| raise ArgumentError, "Invalid address format `#{uri}`" unless host }
|
40
59
|
end
|
@@ -44,12 +63,12 @@ module Neo4j
|
|
44
63
|
DEFAULT_PORT
|
45
64
|
end
|
46
65
|
|
47
|
-
def create_driver(scheme, connector, logger, routing_settings)
|
66
|
+
def create_driver(scheme, connector, logger, routing_settings, retry_logic, config)
|
48
67
|
case scheme
|
49
68
|
when BOLT_URI_SCHEME
|
50
69
|
# assert_no_routing_context( uri, routing_settings )
|
51
70
|
# return createDirectDriver( securityPlan, address, connectionPool, retryLogic, metrics, config );
|
52
|
-
create_direct_driver(connector, logger)
|
71
|
+
create_direct_driver(connector, logger, retry_logic, config)
|
53
72
|
when BOLT_ROUTING_URI_SCHEME, NEO4J_URI_SCHEME
|
54
73
|
# create_routing_driver( security_plan, address, connection_ool, eventExecutorGroup, routingSettings, retryLogic, metrics, config );
|
55
74
|
else
|
@@ -57,9 +76,9 @@ module Neo4j
|
|
57
76
|
end
|
58
77
|
end
|
59
78
|
|
60
|
-
def create_direct_driver(connector, logger)
|
61
|
-
connection_provider = DirectConnectionProvider.new(connector)
|
62
|
-
session_factory = create_session_factory(connection_provider)
|
79
|
+
def create_direct_driver(connector, logger, retry_logic, config)
|
80
|
+
connection_provider = DirectConnectionProvider.new(connector, config)
|
81
|
+
session_factory = create_session_factory(connection_provider, retry_logic, config)
|
63
82
|
InternalDriver.new(session_factory, logger)
|
64
83
|
end
|
65
84
|
|
@@ -15,6 +15,13 @@ module Neo4j
|
|
15
15
|
# Connection refused
|
16
16
|
when Bolt::Error::BOLT_CONNECTION_REFUSED
|
17
17
|
throw Exceptions::ServiceUnavailableException.new(error_code, 'unable to acquire connection')
|
18
|
+
# Connection pool is full
|
19
|
+
when Bolt::Error::BOLT_POOL_FULL
|
20
|
+
throw Exceptions::ClientException.new(
|
21
|
+
error_code,
|
22
|
+
'Unable to acquire connection from the pool within configured maximum time of ' \
|
23
|
+
"#{@config[:connection_acquisition_timeout] * 1000}ms"
|
24
|
+
)
|
18
25
|
# Error set in connection
|
19
26
|
when Bolt::Error::BOLT_CONNECTION_HAS_MORE_INFO, Bolt::Error::BOLT_STATUS_SET
|
20
27
|
status = Bolt::Connection.status(bolt_connection)
|
@@ -37,6 +44,21 @@ module Neo4j
|
|
37
44
|
check_status(status)
|
38
45
|
end
|
39
46
|
|
47
|
+
def new_neo4j_error(code:, message:)
|
48
|
+
case code.split('.')[1]
|
49
|
+
when 'ClientError'
|
50
|
+
if code.casecmp('Neo.ClientError.Security.Unauthorized').zero?
|
51
|
+
Exceptions::AuthenticationException
|
52
|
+
else
|
53
|
+
Exceptions::ClientException
|
54
|
+
end
|
55
|
+
when 'TransientError'
|
56
|
+
Exceptions::TransientException
|
57
|
+
else
|
58
|
+
Exceptions::DatabaseException
|
59
|
+
end.new(code, message)
|
60
|
+
end
|
61
|
+
|
40
62
|
private
|
41
63
|
|
42
64
|
def throw(error)
|
@@ -5,6 +5,8 @@ module Neo4j
|
|
5
5
|
module Internal
|
6
6
|
module Handlers
|
7
7
|
class ResponseHandler
|
8
|
+
include ErrorHandling
|
9
|
+
|
8
10
|
delegate :bolt_connection, to: :connection
|
9
11
|
attr_reader :connection, :failure
|
10
12
|
attr_accessor :request, :previous
|
@@ -33,7 +35,7 @@ module Neo4j
|
|
33
35
|
else
|
34
36
|
return if previous&.failure
|
35
37
|
@failure = Value::ValueAdapter.to_ruby(Bolt::Connection.failure(bolt_connection))
|
36
|
-
raise
|
38
|
+
raise new_neo4j_error(@failure)
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
@@ -15,12 +15,7 @@ module Neo4j
|
|
15
15
|
@open = Concurrent::AtomicBoolean.new(true)
|
16
16
|
@connection_provider = connection_provider
|
17
17
|
@mode = mode
|
18
|
-
|
19
|
-
@retry_logic = Class.new do
|
20
|
-
def retry
|
21
|
-
yield
|
22
|
-
end
|
23
|
-
end.new
|
18
|
+
@retry_logic = retry_logic
|
24
19
|
end
|
25
20
|
|
26
21
|
def run(statement, parameters = {}, config = nil)
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Neo4j
|
4
|
+
module Driver
|
5
|
+
module Internal
|
6
|
+
module Retry
|
7
|
+
class ExponentialBackoffRetryLogic
|
8
|
+
DEFAULT_MAX_RETRY_TIME = 30
|
9
|
+
INITIAL_RETRY_DELAY = 1
|
10
|
+
RETRY_DELAY_MULTIPLIER = 2.0
|
11
|
+
RETRY_DELAY_JITTER_FACTOR = 0.2
|
12
|
+
|
13
|
+
def initialize(max_retry_time = nil, logger = nil)
|
14
|
+
@max_retry_time = max_retry_time || DEFAULT_MAX_RETRY_TIME
|
15
|
+
@log = logger
|
16
|
+
end
|
17
|
+
|
18
|
+
def retry
|
19
|
+
next_delay = INITIAL_RETRY_DELAY
|
20
|
+
start_time = nil
|
21
|
+
errors = nil
|
22
|
+
loop do
|
23
|
+
return yield
|
24
|
+
rescue Exceptions::Neo4jException => error
|
25
|
+
if can_retry_on?(error)
|
26
|
+
curr_time = current_time
|
27
|
+
start_time ||= curr_time
|
28
|
+
elapsed_time = curr_time - start_time
|
29
|
+
if elapsed_time < @max_retry_time
|
30
|
+
delay_with_jitter = compute_delay_with_jitter(next_delay)
|
31
|
+
@log&.warn("Transaction failed and will be retried in #{delay_with_jitter}ms", error)
|
32
|
+
sleep(delay_with_jitter) # verify time units
|
33
|
+
next_delay *= RETRY_DELAY_MULTIPLIER
|
34
|
+
(errors ||= []) << error
|
35
|
+
next
|
36
|
+
end
|
37
|
+
end
|
38
|
+
add_suppressed(error, errors)
|
39
|
+
raise error
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def can_retry_on?(error)
|
46
|
+
error.is_a?(Exceptions::SessionExpiredException) ||
|
47
|
+
error.is_a?(Exceptions::ServiceUnavailableException) ||
|
48
|
+
transient_error?(error)
|
49
|
+
end
|
50
|
+
|
51
|
+
def transient_error?(error)
|
52
|
+
# Retries should not happen when transaction was explicitly terminated by the user.
|
53
|
+
# Termination of transaction might result in two different error codes depending on where it was
|
54
|
+
# terminated. These are really client errors but classification on the server is not entirely correct and
|
55
|
+
# they are classified as transient.
|
56
|
+
error.is_a?(Exceptions::TransientException) &&
|
57
|
+
!%w[Neo.TransientError.Transaction.Terminated Neo.TransientError.Transaction.LockClientStopped]
|
58
|
+
.include?(error.code)
|
59
|
+
end
|
60
|
+
|
61
|
+
def compute_delay_with_jitter(delay)
|
62
|
+
jitter = delay * RETRY_DELAY_JITTER_FACTOR
|
63
|
+
min = delay - jitter
|
64
|
+
max = delay + jitter
|
65
|
+
@rand ||= Random.new
|
66
|
+
@rand.rand(min..max)
|
67
|
+
end
|
68
|
+
|
69
|
+
def current_time
|
70
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_suppressed(error, suppressed_errors)
|
74
|
+
suppressed_errors&.reject(&error.method(:equal?))&.each(&error.method(:add_suppressed))
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/neo4j/driver/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: neo4j-ruby-driver
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Heinrich Klobuczek
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-12-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -239,11 +239,11 @@ files:
|
|
239
239
|
- ffi/bolt/lifecycle.rb
|
240
240
|
- ffi/bolt/list.rb
|
241
241
|
- ffi/bolt/log.rb
|
242
|
+
- ffi/bolt/socket_options.rb
|
242
243
|
- ffi/bolt/status.rb
|
243
244
|
- ffi/bolt/string.rb
|
244
245
|
- ffi/bolt/structure.rb
|
245
246
|
- ffi/bolt/value.rb
|
246
|
-
- ffi/bolt/values_private.rb
|
247
247
|
- ffi/neo4j/driver.rb
|
248
248
|
- ffi/neo4j/driver/access_mode.rb
|
249
249
|
- ffi/neo4j/driver/auth_tokens.rb
|
@@ -270,6 +270,7 @@ files:
|
|
270
270
|
- ffi/neo4j/driver/internal/messaging/v2/bolt_protocol_v2.rb
|
271
271
|
- ffi/neo4j/driver/internal/messaging/v3/bolt_protocol_v3.rb
|
272
272
|
- ffi/neo4j/driver/internal/network_session.rb
|
273
|
+
- ffi/neo4j/driver/internal/retry/exponential_backoff_retry_logic.rb
|
273
274
|
- ffi/neo4j/driver/internal/session_factory_impl.rb
|
274
275
|
- ffi/neo4j/driver/internal/summary/internal_result_summary.rb
|
275
276
|
- ffi/neo4j/driver/internal/summary/internal_server_info.rb
|
data/ffi/bolt/values_private.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Bolt
|
4
|
-
module ValuesPrivate
|
5
|
-
extend FFI::Library
|
6
|
-
|
7
|
-
typedef :pointer, :bolt_value
|
8
|
-
# typedef BoltData, :bolt_data
|
9
|
-
# typedef BoltExtendedValue, :bolt_extended_value
|
10
|
-
#
|
11
|
-
# class BoltValue < FFI::Struct
|
12
|
-
# layout :type, :int16,
|
13
|
-
# :subtype, :int16,
|
14
|
-
# :size, :int16,
|
15
|
-
# :data_size, :uint64,
|
16
|
-
# :data, :bolt_data
|
17
|
-
# end
|
18
|
-
#
|
19
|
-
# class BoltData < FFI::Union
|
20
|
-
# layout :as_char, [:char, 16],
|
21
|
-
# :as_uint32, [:uint32, 4],
|
22
|
-
# :as_int8, [:int8_t, 16],
|
23
|
-
# :as_int16, [:int16, 8],
|
24
|
-
# :as_int32, [:int32, 4],
|
25
|
-
# :as_int64, [:int64, 2],
|
26
|
-
# :as_double, [:double, 2],
|
27
|
-
# :extended, :bolt_extended_value
|
28
|
-
# end
|
29
|
-
|
30
|
-
class BoltExtendedValue < FFI::Union
|
31
|
-
layout :as_ptr, :pointer,
|
32
|
-
:as_char, :string,
|
33
|
-
:as_value, :bolt_value
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|