neo4j-ruby-driver 0.1.9 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f25d2880441e748a0152d2f23afa956891bde3fd90e9c15886e21784ef0da61b
4
- data.tar.gz: 82edb2e592d1fd45f895290d49f5f7d6533193e2406d7eadae89d0344082f986
3
+ metadata.gz: 7c7d38011d191d0e26b26b02e6b147672b27806cd66d9ebb416d326bc8ed0406
4
+ data.tar.gz: 8bef825331172211356c41ba82a7b5d5c927f939d6e0a716fb6a7f0a18cfb947
5
5
  SHA512:
6
- metadata.gz: 29f37f4671b277a5b276f43bd4549a998f0ca17fbd80cfd41128d98e84fa9d64d594cfd271600e27f5fa9d20d0f33dab08c7c378790921143d5a0cf6441a9d2f
7
- data.tar.gz: d2012cb64c0336afbfdf7e15b18f38295f35f1d4c88da068ce73abf8743bd672a800f984962040c70c9f6c64b65bbfc5d3e09a5eb881c60e5c714894ee019d2e
6
+ metadata.gz: 2ceec98de33377a4dea166d894ba5c6179ff7510744c6efbaa07fbb5dfbd237c0b08278587f9ebf410f601a41e006d395cbcc3f0ebd9114439348bc24bab25d3
7
+ data.tar.gz: 16c1bb6ba7b910c86f8dc605980c393a8a4f10e0fa57eeeeb109b6fea25efaacb5125f14518666960fc0050ea82a3a64d2c8bd260db79a084522af4a701fc0f4
@@ -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 :set_user_agent, :BoltConfig_set_user_agent, %i[pointer string], :int32
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
@@ -19,6 +19,8 @@ if RUBY_PLATFORM.match?(/java/)
19
19
  module V3
20
20
  end
21
21
  end
22
+ module Retry
23
+ end
22
24
  module Summary
23
25
  end
24
26
  module Util
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Neo4j
4
4
  module Driver
5
- class Config
5
+ class Config < Hash
6
6
  class TrustStrategy
7
7
  class << self
8
8
  def trust_all_certificates; end
@@ -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
- # config ||= Config.default_config
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
- create_driver(uri.scheme, connector, logger, routing_settings).tap(&:verify_connectivity)
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
- # case key
32
- # end
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,8 +5,6 @@ module Neo4j
5
5
  module Internal
6
6
  module Handlers
7
7
  class PullAllResponseHandler < ResponseHandler
8
- include ErrorHandling
9
-
10
8
  delegate :bolt_connection, to: :connection
11
9
  delegate :statement_keys, to: :run_handler
12
10
  attr_reader :connection
@@ -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 Exceptions::ClientException.new(@failure[:code], @failure[:message])
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
- # @retry_logic = retry_logic
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
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Neo4j
4
4
  module Driver
5
- VERSION = '0.1.9'
5
+ VERSION = '0.1.10'
6
6
  end
7
7
  end
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.9
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-28 00:00:00.000000000 Z
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
@@ -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