tarantool-client 0.1.0 → 0.2.0

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 (32) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -1
  3. data/README.md +79 -1
  4. data/lib/tarantool/client/batch_delete.rb +97 -0
  5. data/lib/tarantool/client/configuration.rb +20 -3
  6. data/lib/tarantool/client/diagnostics.rb +20 -0
  7. data/lib/tarantool/client/errors.rb +8 -1
  8. data/lib/tarantool/client/fork_safety.rb +2 -1
  9. data/lib/tarantool/client/instrumentation/log_instrumenter.rb +35 -0
  10. data/lib/tarantool/client/instrumentation/log_lines.rb +66 -0
  11. data/lib/tarantool/client/instrumentation/request_payload.rb +42 -0
  12. data/lib/tarantool/client/instrumentation.rb +51 -0
  13. data/lib/tarantool/client/instrumented.rb +42 -0
  14. data/lib/tarantool/client/named_requests.rb +67 -0
  15. data/lib/tarantool/client/operations.rb +53 -21
  16. data/lib/tarantool/client/pool/member.rb +4 -9
  17. data/lib/tarantool/client/pool/member_fork_safety.rb +10 -5
  18. data/lib/tarantool/client/pool/member_role.rb +36 -0
  19. data/lib/tarantool/client/row.rb +8 -0
  20. data/lib/tarantool/client/schema.rb +4 -3
  21. data/lib/tarantool/client/space_info.rb +2 -0
  22. data/lib/tarantool/client/stream.rb +20 -10
  23. data/lib/tarantool/client/transaction_schema.rb +12 -8
  24. data/lib/tarantool/client/transport/connection.rb +2 -0
  25. data/lib/tarantool/client/transport/link.rb +15 -15
  26. data/lib/tarantool/client/transport/link_events.rb +41 -0
  27. data/lib/tarantool/client/transport/link_state.rb +10 -0
  28. data/lib/tarantool/client/tuple_mapping.rb +65 -0
  29. data/lib/tarantool/client/tuple_shape.rb +25 -0
  30. data/lib/tarantool/client/version.rb +1 -1
  31. data/lib/tarantool/client.rb +23 -48
  32. metadata +15 -2
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tarantool
4
+ class Client
5
+ # Tuple <-> Hash by the space format, for SpaceInfo. Fields beyond the format are keyed by their 0-based position.
6
+ # @api private
7
+ module TupleMapping
8
+ # A position this far past the format would otherwise let a huge Integer key allocate a giant Array.
9
+ MAX_POSITIONS_BEYOND_FORMAT = 1024
10
+
11
+ def to_hash(tuple)
12
+ hash = fields.each_with_index.to_h { |field, position| [field.name.to_sym, tuple[position]] }
13
+ (fields.size...tuple.size).each { |position| hash[position] = tuple[position] }
14
+
15
+ hash
16
+ end
17
+
18
+ # A missing field goes out as nil; on 3.x the server applies the field default to nil as well.
19
+ def to_tuple(hash)
20
+ positions = fields.each_with_index.to_h { |field, position| [field.name, position] }
21
+ named_positions = {}
22
+
23
+ hash.each_with_object(Array.new(fields.size)) do |(key, value), tuple|
24
+ position = position_of(key, positions)
25
+ check_not_duplicate(key, position, named_positions)
26
+ tuple[position] = value
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def position_of(key, positions)
33
+ case key
34
+ when Symbol, String
35
+ positions.fetch(key.to_s) { raise UnknownFieldError, "space #{name} has no field '#{key}'" }
36
+ when Integer then position_beyond_format(key)
37
+ else raise ArgumentError, "unsupported key #{key.inspect} for space #{name}"
38
+ end
39
+ end
40
+
41
+ # `:a` and 'a' name the same field; the later one would otherwise win silently.
42
+ def check_not_duplicate(key, position, named_positions)
43
+ return unless key.is_a?(Symbol) || key.is_a?(String)
44
+ raise ArgumentError, "space #{name} field '#{fields[position].name}' given twice" if named_positions[position]
45
+
46
+ named_positions[position] = true
47
+ end
48
+
49
+ def position_beyond_format(position)
50
+ raise ArgumentError, "position #{position} is negative for space #{name}" if position.negative?
51
+
52
+ if position < fields.size
53
+ raise ArgumentError, "position #{position} is inside the format of space #{name}; use the field name"
54
+ end
55
+
56
+ if position >= fields.size + MAX_POSITIONS_BEYOND_FORMAT
57
+ raise ArgumentError, "position #{position} is more than #{MAX_POSITIONS_BEYOND_FORMAT} past the format " \
58
+ "of space #{name}"
59
+ end
60
+
61
+ position
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Tarantool
4
+ class Client
5
+ # What data operations return for each tuple, chosen with `as:`.
6
+ # @api private
7
+ module TupleShape
8
+ SHAPES = %i[tuple hash row].freeze
9
+
10
+ def self.check(shape)
11
+ return if SHAPES.include?(shape)
12
+
13
+ raise ArgumentError, "unknown as: #{shape.inspect}, expected one of #{SHAPES.join(', ')}"
14
+ end
15
+
16
+ def self.apply(shape, tuple, space_info)
17
+ case shape
18
+ when :hash then space_info.to_hash(tuple)
19
+ when :row then Row.new(tuple:, attributes: space_info.to_hash(tuple))
20
+ else tuple
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Tarantool
4
4
  class Client
5
- VERSION = '0.1.0'
5
+ VERSION = '0.2.0'
6
6
  end
7
7
  end
@@ -8,6 +8,7 @@ require_relative 'client/errors'
8
8
  require_relative 'client/configuration'
9
9
  require_relative 'client/backoff'
10
10
  require_relative 'client/thread_join'
11
+ require_relative 'client/diagnostics'
11
12
  require_relative 'client/protocol/constants'
12
13
  require_relative 'client/box_error'
13
14
  require_relative 'client/uuid'
@@ -40,6 +41,12 @@ require_relative 'client/protocol/requests/auth'
40
41
  require_relative 'client/protocol/requests/id'
41
42
  require_relative 'client/protocol/requests/watch'
42
43
  require_relative 'client/protocol/requests/unwatch'
44
+ require_relative 'client/instrumentation'
45
+ require_relative 'client/instrumentation/request_payload'
46
+ require_relative 'client/instrumentation/log_lines'
47
+ require_relative 'client/instrumentation/log_instrumenter'
48
+ require_relative 'client/instrumented'
49
+ require_relative 'client/named_requests'
43
50
  require_relative 'client/transport/future'
44
51
  require_relative 'client/transport/pending_requests'
45
52
  require_relative 'client/transport/stream_ids'
@@ -50,14 +57,19 @@ require_relative 'client/transport/frame_writer'
50
57
  require_relative 'client/transport/handshake'
51
58
  require_relative 'client/transport/connection'
52
59
  require_relative 'client/transport/link_state'
60
+ require_relative 'client/transport/link_events'
53
61
  require_relative 'client/transport/link'
54
62
  require_relative 'client/index_info'
63
+ require_relative 'client/tuple_mapping'
55
64
  require_relative 'client/space_info'
56
65
  require_relative 'client/schema'
57
66
  require_relative 'client/schema_cache'
58
67
  require_relative 'client/sql_result'
59
68
  require_relative 'client/prepared_statement'
69
+ require_relative 'client/batch_delete'
60
70
  require_relative 'client/transaction_schema'
71
+ require_relative 'client/row'
72
+ require_relative 'client/tuple_shape'
61
73
  require_relative 'client/operations'
62
74
  require_relative 'client/transaction_scope'
63
75
  require_relative 'client/stream'
@@ -68,6 +80,7 @@ require_relative 'client/watchers/dispatcher'
68
80
  require_relative 'client/fork_safety'
69
81
  require_relative 'client/pool'
70
82
  require_relative 'client/pool/member_fork_safety'
83
+ require_relative 'client/pool/member_role'
71
84
  require_relative 'client/pool/member'
72
85
 
73
86
  module Tarantool
@@ -75,6 +88,8 @@ module Tarantool
75
88
  class Client
76
89
  include Operations
77
90
  include ForkSafety
91
+ include Instrumented
92
+ include NamedRequests
78
93
 
79
94
  attr_reader :config
80
95
 
@@ -85,6 +100,7 @@ module Tarantool
85
100
 
86
101
  def initialize(uri = nil, **)
87
102
  @config = Configuration.build(uri, **)
103
+ @instrumentation = Instrumentation.build(@config)
88
104
  @pid = Process.pid
89
105
  @reinit_lock = Mutex.new
90
106
  open_link
@@ -107,7 +123,7 @@ module Tarantool
107
123
  TransactionScope.enter(self) do
108
124
  stream = Stream.new(
109
125
  connection: @link.connection, schema_cache: @schema_cache, id: @stream_ids.next,
110
- request_timeout: @config.request_timeout
126
+ request_timeout: @config.request_timeout, instrumentation: @instrumentation
111
127
  )
112
128
  stream.transaction(isolation:, timeout:, &block)
113
129
  end
@@ -139,7 +155,7 @@ module Tarantool
139
155
 
140
156
  def link_hooks = { on_connect: method(:load_schema), on_ready: method(:resubscribe), on_lost: method(:lost) }
141
157
 
142
- def perform(request, timeout: nil, schema_version: nil, on_push: nil)
158
+ def transmit(request, timeout: nil, schema_version: nil, on_push: nil)
143
159
  ensure_fork_safe
144
160
  @link.perform(request, timeout: timeout || @config.request_timeout, schema_version:, on_push:)
145
161
  end
@@ -164,53 +180,12 @@ module Tarantool
164
180
 
165
181
  # Name-resolved requests carry the schema version they were resolved against, so the
166
182
  # server rejects them (ER_WRONG_SCHEMA_VERSION) instead of running them on a stale id.
167
- def perform_on_space(space, index, timeout: nil, &)
168
- return perform(yield(space, index), timeout:) if space.is_a?(Integer) && index.is_a?(Integer)
183
+ # A request that needs the space format is resolved the same way even by ids, so the
184
+ # format and the version it carries come from one schema.
185
+ def transmit_on_space(space, index, timeout: nil, format: false, payload: nil, &)
186
+ return transmit(yield(space, index), timeout:) if !format && space.is_a?(Integer) && index.is_a?(Integer)
169
187
 
170
- perform_by_name(space, index, timeout || @config.request_timeout, &)
171
- end
172
-
173
- # Every schema fetch and attempt draws on one deadline, so the whole call stays within timeout.
174
- # Waiting for a connection first means the cache already holds its schema or a newer one.
175
- def perform_by_name(space, index, timeout, &)
176
- ensure_fork_safe
177
- deadline = Transport::Deadline.after(timeout)
178
- @link.connection(deadline)
179
-
180
- perform_with_schema_check(space, index, deadline, &)
181
- rescue TimeoutError
182
- raise TimeoutError, "no response within #{timeout}s"
183
- end
184
-
185
- def lookup_space(space, timeout)
186
- ensure_fork_safe
187
- timeout ||= @config.request_timeout
188
- deadline = Transport::Deadline.after(timeout)
189
- @link.connection(deadline)
190
-
191
- @schema_cache.space_info(space, deadline)
192
- rescue TimeoutError
193
- raise TimeoutError, "no response within #{timeout}s"
194
- end
195
-
196
- def perform_with_schema_check(space, index, deadline)
197
- schema, ids = @schema_cache.resolve(space, index, deadline)
198
-
199
- perform_resolved(schema, yield(*ids), deadline)
200
- rescue ServerError => e
201
- raise unless e.code == Protocol::Constants::ER_WRONG_SCHEMA_VERSION
202
-
203
- fresh = @schema_cache.reload(schema, timeout: Transport::Deadline.remaining(deadline))
204
- schema, ids = @schema_cache.resolve(space, index, deadline, fresh)
205
- perform_resolved(schema, yield(*ids), deadline)
206
- end
207
-
208
- # Only the connection the schema came from: once it is lost, another instance may reuse the
209
- # schema version with other ids, so the request fails with it rather than wait for a reconnect.
210
- def perform_resolved(schema, request, deadline)
211
- timeout = Transport::Deadline.remaining(deadline)
212
-
213
- schema.connection.perform(request, timeout:, schema_version: schema.version)
188
+ perform_by_name(space, index, timeout || @config.request_timeout, payload, &)
214
189
  end
215
190
  end
216
191
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tarantool-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Starlexxx
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-10-16 00:00:00.000000000 Z
11
+ date: 2026-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -52,17 +52,26 @@ files:
52
52
  - README.md
53
53
  - lib/tarantool/client.rb
54
54
  - lib/tarantool/client/backoff.rb
55
+ - lib/tarantool/client/batch_delete.rb
55
56
  - lib/tarantool/client/box_error.rb
56
57
  - lib/tarantool/client/callback_guard.rb
57
58
  - lib/tarantool/client/configuration.rb
59
+ - lib/tarantool/client/diagnostics.rb
58
60
  - lib/tarantool/client/errors.rb
59
61
  - lib/tarantool/client/fork_safety.rb
60
62
  - lib/tarantool/client/index_info.rb
63
+ - lib/tarantool/client/instrumentation.rb
64
+ - lib/tarantool/client/instrumentation/log_instrumenter.rb
65
+ - lib/tarantool/client/instrumentation/log_lines.rb
66
+ - lib/tarantool/client/instrumentation/request_payload.rb
67
+ - lib/tarantool/client/instrumented.rb
61
68
  - lib/tarantool/client/interval.rb
69
+ - lib/tarantool/client/named_requests.rb
62
70
  - lib/tarantool/client/operations.rb
63
71
  - lib/tarantool/client/pool.rb
64
72
  - lib/tarantool/client/pool/member.rb
65
73
  - lib/tarantool/client/pool/member_fork_safety.rb
74
+ - lib/tarantool/client/pool/member_role.rb
66
75
  - lib/tarantool/client/prepared_statement.rb
67
76
  - lib/tarantool/client/protocol/auth/chap_sha1.rb
68
77
  - lib/tarantool/client/protocol/codec.rb
@@ -93,6 +102,7 @@ files:
93
102
  - lib/tarantool/client/protocol/requests/upsert.rb
94
103
  - lib/tarantool/client/protocol/requests/watch.rb
95
104
  - lib/tarantool/client/protocol/response.rb
105
+ - lib/tarantool/client/row.rb
96
106
  - lib/tarantool/client/schema.rb
97
107
  - lib/tarantool/client/schema_cache.rb
98
108
  - lib/tarantool/client/space_info.rb
@@ -107,11 +117,14 @@ files:
107
117
  - lib/tarantool/client/transport/future.rb
108
118
  - lib/tarantool/client/transport/handshake.rb
109
119
  - lib/tarantool/client/transport/link.rb
120
+ - lib/tarantool/client/transport/link_events.rb
110
121
  - lib/tarantool/client/transport/link_state.rb
111
122
  - lib/tarantool/client/transport/pending_requests.rb
112
123
  - lib/tarantool/client/transport/stream_ids.rb
113
124
  - lib/tarantool/client/transport/timed_lock.rb
114
125
  - lib/tarantool/client/transport/timed_read.rb
126
+ - lib/tarantool/client/tuple_mapping.rb
127
+ - lib/tarantool/client/tuple_shape.rb
115
128
  - lib/tarantool/client/uuid.rb
116
129
  - lib/tarantool/client/version.rb
117
130
  - lib/tarantool/client/watcher.rb