slatedb 0.1.1 → 0.3.1

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.
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SlateDb
4
- class Database
4
+ class Database # rubocop:disable Metrics/ClassLength
5
5
  private_class_method :new
6
6
 
7
7
  class << self
@@ -9,6 +9,9 @@ module SlateDb
9
9
  #
10
10
  # @param path [String] The path identifier for the database
11
11
  # @param url [String, nil] Optional object store URL (e.g., "s3://bucket/path")
12
+ # @param merge_operator [Symbol, String, Proc, nil] Optional merge operator.
13
+ # Can be a symbol/string ("string_concat" or "concat") or a Proc/lambda
14
+ # that takes (key, existing_value, new_value) and returns the merged value.
12
15
  # @yield [db] If a block is given, yields the database and ensures it's closed
13
16
  # @return [Database] The opened database (or block result if block given)
14
17
  #
@@ -25,8 +28,29 @@ module SlateDb
25
28
  # @example Open with S3 backend
26
29
  # db = SlateDb::Database.open("/tmp/mydb", url: "s3://mybucket/path")
27
30
  #
28
- def open(path, url: nil)
29
- db = _open(path, url)
31
+ # @example Open with a custom merge operator (Proc)
32
+ # # Custom merge that adds numbers
33
+ # db = SlateDb::Database.open("/tmp/mydb", merge_operator: ->(key, existing, new_val) {
34
+ # existing_num = existing ? existing.to_i : 0
35
+ # (existing_num + new_val.to_i).to_s
36
+ # })
37
+ # db.merge("counter", "5")
38
+ # db.merge("counter", "3")
39
+ # db.get("counter") # => "8"
40
+ #
41
+ def open(path, url: nil, merge_operator: nil)
42
+ opts = {}
43
+
44
+ case merge_operator
45
+ when Symbol, String
46
+ opts[:merge_operator] = merge_operator.to_s
47
+ when Proc
48
+ # Store the proc to prevent GC and pass to Rust
49
+ @_merge_operator_proc = merge_operator
50
+ opts[:merge_operator_proc] = merge_operator
51
+ end
52
+
53
+ db = _open(path, url, opts)
30
54
 
31
55
  if block_given?
32
56
  begin
@@ -71,12 +95,44 @@ module SlateDb
71
95
  end
72
96
  end
73
97
 
98
+ # Get a key-value pair with SlateDB metadata.
99
+ #
100
+ # @param key [String] The key to look up
101
+ # @param durability_filter [String, nil] Filter by durability level ("remote" or "memory")
102
+ # @param dirty [Boolean, nil] Whether to include uncommitted data
103
+ # @param cache_blocks [Boolean, nil] Whether to cache blocks
104
+ # @return [Hash, nil] A hash with :key, :value, :seq, :create_ts, and :expire_ts, or nil if not found
105
+ #
106
+ # @example Inspect metadata
107
+ # entry = db.get_key_value("mykey")
108
+ # entry[:value] # => "myvalue"
109
+ # entry[:seq] # => sequence number
110
+ #
111
+ def get_key_value(key, durability_filter: nil, dirty: nil, cache_blocks: nil)
112
+ opts = {}
113
+ opts[:durability_filter] = durability_filter.to_s if durability_filter
114
+ opts[:dirty] = dirty unless dirty.nil?
115
+ opts[:cache_blocks] = cache_blocks unless cache_blocks.nil?
116
+
117
+ if opts.empty?
118
+ _get_key_value(key)
119
+ else
120
+ _get_key_value_with_options(key, opts)
121
+ end
122
+ end
123
+
124
+ alias get_entry get_key_value
125
+
74
126
  # Store a key-value pair.
75
127
  #
76
128
  # @param key [String] The key to store
77
129
  # @param value [String] The value to store
78
130
  # @param ttl [Integer, nil] Time-to-live in milliseconds
79
131
  # @param await_durable [Boolean] Whether to wait for durability (default: true)
132
+ # @param seqnum [Integer, nil] User-supplied sequence number for this write.
133
+ # When provided (and non-zero), it is used instead of the internally
134
+ # generated sequence number. It must be strictly greater than the current
135
+ # maximum sequence number or the write fails. (Requires SlateDB >= 0.13.0)
80
136
  # @return [void]
81
137
  #
82
138
  # @example Basic put
@@ -88,10 +144,14 @@ module SlateDb
88
144
  # @example Put without waiting for durability
89
145
  # db.put("mykey", "myvalue", await_durable: false)
90
146
  #
91
- def put(key, value, ttl: nil, await_durable: nil)
147
+ # @example Put with an explicit sequence number
148
+ # db.put("mykey", "myvalue", seqnum: 42)
149
+ #
150
+ def put(key, value, ttl: nil, await_durable: nil, seqnum: nil)
92
151
  opts = {}
93
152
  opts[:ttl] = ttl if ttl
94
153
  opts[:await_durable] = await_durable unless await_durable.nil?
154
+ opts[:seqnum] = seqnum if seqnum
95
155
 
96
156
  if opts.empty?
97
157
  _put(key, value)
@@ -104,6 +164,8 @@ module SlateDb
104
164
  #
105
165
  # @param key [String] The key to delete
106
166
  # @param await_durable [Boolean] Whether to wait for durability (default: true)
167
+ # @param seqnum [Integer, nil] User-supplied sequence number for this write.
168
+ # See {#put} for semantics. (Requires SlateDB >= 0.13.0)
107
169
  # @return [void]
108
170
  #
109
171
  # @example Basic delete
@@ -112,11 +174,15 @@ module SlateDb
112
174
  # @example Delete without waiting for durability
113
175
  # db.delete("mykey", await_durable: false)
114
176
  #
115
- def delete(key, await_durable: nil)
116
- if await_durable.nil?
177
+ def delete(key, await_durable: nil, seqnum: nil)
178
+ opts = {}
179
+ opts[:await_durable] = await_durable unless await_durable.nil?
180
+ opts[:seqnum] = seqnum if seqnum
181
+
182
+ if opts.empty?
117
183
  _delete(key)
118
184
  else
119
- _delete_with_options(key, { await_durable: await_durable })
185
+ _delete_with_options(key, opts)
120
186
  end
121
187
  end
122
188
 
@@ -129,6 +195,7 @@ module SlateDb
129
195
  # @param read_ahead_bytes [Integer, nil] Number of bytes to read ahead
130
196
  # @param cache_blocks [Boolean, nil] Whether to cache blocks
131
197
  # @param max_fetch_tasks [Integer, nil] Maximum number of fetch tasks
198
+ # @param order [Symbol, String, nil] Iteration order (:asc/:ascending or :desc/:descending)
132
199
  # @return [Iterator] An iterator over key-value pairs
133
200
  #
134
201
  # @example Basic scan
@@ -147,13 +214,15 @@ module SlateDb
147
214
  # end
148
215
  #
149
216
  def scan(start_key, end_key = nil, durability_filter: nil, dirty: nil,
150
- read_ahead_bytes: nil, cache_blocks: nil, max_fetch_tasks: nil, &)
151
- opts = {}
152
- opts[:durability_filter] = durability_filter.to_s if durability_filter
153
- opts[:dirty] = dirty unless dirty.nil?
154
- opts[:read_ahead_bytes] = read_ahead_bytes if read_ahead_bytes
155
- opts[:cache_blocks] = cache_blocks unless cache_blocks.nil?
156
- opts[:max_fetch_tasks] = max_fetch_tasks if max_fetch_tasks
217
+ read_ahead_bytes: nil, cache_blocks: nil, max_fetch_tasks: nil, order: nil, &)
218
+ opts = scan_options(
219
+ durability_filter: durability_filter,
220
+ dirty: dirty,
221
+ read_ahead_bytes: read_ahead_bytes,
222
+ cache_blocks: cache_blocks,
223
+ max_fetch_tasks: max_fetch_tasks,
224
+ order: order
225
+ )
157
226
 
158
227
  iter = if opts.empty?
159
228
  _scan(start_key, end_key)
@@ -168,10 +237,66 @@ module SlateDb
168
237
  end
169
238
  end
170
239
 
240
+ # Scan all keys with a given prefix.
241
+ #
242
+ # @param prefix [String] The key prefix to scan
243
+ # @param durability_filter [String, nil] Filter by durability level ("remote" or "memory")
244
+ # @param dirty [Boolean, nil] Whether to include uncommitted data
245
+ # @param read_ahead_bytes [Integer, nil] Number of bytes to read ahead
246
+ # @param cache_blocks [Boolean, nil] Whether to cache blocks
247
+ # @param max_fetch_tasks [Integer, nil] Maximum number of fetch tasks
248
+ # @param order [Symbol, String, nil] Iteration order (:asc/:ascending or :desc/:descending)
249
+ # @return [Iterator] An iterator over key-value pairs
250
+ #
251
+ # @example Scan all user keys
252
+ # db.scan_prefix("user:") do |key, value|
253
+ # puts "#{key}: #{value}"
254
+ # end
255
+ #
256
+ def scan_prefix(prefix, durability_filter: nil, dirty: nil,
257
+ read_ahead_bytes: nil, cache_blocks: nil, max_fetch_tasks: nil, order: nil, &)
258
+ opts = scan_options(
259
+ durability_filter: durability_filter,
260
+ dirty: dirty,
261
+ read_ahead_bytes: read_ahead_bytes,
262
+ cache_blocks: cache_blocks,
263
+ max_fetch_tasks: max_fetch_tasks,
264
+ order: order
265
+ )
266
+
267
+ iter = if opts.empty?
268
+ _scan_prefix(prefix)
269
+ else
270
+ _scan_prefix_with_options(prefix, opts)
271
+ end
272
+
273
+ if block_given?
274
+ iter.each(&)
275
+ else
276
+ iter
277
+ end
278
+ end
279
+
280
+ def scan_options(durability_filter:, dirty:, read_ahead_bytes:, cache_blocks:,
281
+ max_fetch_tasks:, order:)
282
+ opts = {}
283
+ opts[:durability_filter] = durability_filter.to_s if durability_filter
284
+ opts[:dirty] = dirty unless dirty.nil?
285
+ opts[:read_ahead_bytes] = read_ahead_bytes if read_ahead_bytes
286
+ opts[:cache_blocks] = cache_blocks unless cache_blocks.nil?
287
+ opts[:max_fetch_tasks] = max_fetch_tasks if max_fetch_tasks
288
+ opts[:order] = order.to_s if order
289
+ opts
290
+ end
291
+
292
+ private :scan_options
293
+
171
294
  # Write a batch of operations atomically.
172
295
  #
173
296
  # @param batch [WriteBatch] The batch to write
174
297
  # @param await_durable [Boolean] Whether to wait for durability (default: true)
298
+ # @param seqnum [Integer, nil] User-supplied sequence number applied to the
299
+ # batch. See {#put} for semantics. (Requires SlateDB >= 0.13.0)
175
300
  # @return [void]
176
301
  #
177
302
  # @example Write a batch
@@ -187,17 +312,51 @@ module SlateDb
187
312
  # b.put("key2", "value2")
188
313
  # end
189
314
  #
190
- def write(batch, await_durable: nil)
191
- if await_durable.nil?
315
+ def write(batch, await_durable: nil, seqnum: nil)
316
+ opts = {}
317
+ opts[:await_durable] = await_durable unless await_durable.nil?
318
+ opts[:seqnum] = seqnum if seqnum
319
+
320
+ if opts.empty?
192
321
  _write(batch)
193
322
  else
194
- _write_with_options(batch, { await_durable: await_durable })
323
+ _write_with_options(batch, opts)
324
+ end
325
+ end
326
+
327
+ # Merge a value into the database.
328
+ #
329
+ # @param key [String] The key to merge into
330
+ # @param value [String] The merge operand to apply
331
+ # @param ttl [Integer, nil] Time-to-live in milliseconds
332
+ # @param await_durable [Boolean] Whether to wait for durability (default: true)
333
+ # @param seqnum [Integer, nil] User-supplied sequence number for this write.
334
+ # See {#put} for semantics. (Requires SlateDB >= 0.13.0)
335
+ # @return [void]
336
+ #
337
+ # @example Merge with string concatenation operator
338
+ # db = SlateDb::Database.open("/tmp/mydb", merge_operator: :string_concat)
339
+ # db.merge("key", "part1")
340
+ # db.merge("key", "part2")
341
+ #
342
+ def merge(key, value, ttl: nil, await_durable: nil, seqnum: nil)
343
+ opts = {}
344
+ opts[:ttl] = ttl if ttl
345
+ opts[:await_durable] = await_durable unless await_durable.nil?
346
+ opts[:seqnum] = seqnum if seqnum
347
+
348
+ if opts.empty?
349
+ _merge(key, value)
350
+ else
351
+ _merge_with_options(key, value, opts)
195
352
  end
196
353
  end
197
354
 
198
355
  # Create and write a batch using a block.
199
356
  #
200
357
  # @param await_durable [Boolean] Whether to wait for durability (default: true)
358
+ # @param seqnum [Integer, nil] User-supplied sequence number applied to the
359
+ # batch. See {#put} for semantics. (Requires SlateDB >= 0.13.0)
201
360
  # @yield [batch] Yields a WriteBatch to the block
202
361
  # @return [void]
203
362
  #
@@ -208,10 +367,10 @@ module SlateDb
208
367
  # b.delete("old_key")
209
368
  # end
210
369
  #
211
- def batch(await_durable: nil)
370
+ def batch(await_durable: nil, seqnum: nil)
212
371
  b = WriteBatch.new
213
372
  yield b
214
- write(b, await_durable: await_durable)
373
+ write(b, await_durable: await_durable, seqnum: seqnum)
215
374
  end
216
375
 
217
376
  # Begin a new transaction.
@@ -308,5 +467,32 @@ module SlateDb
308
467
  snap
309
468
  end
310
469
  end
470
+
471
+ # Create a checkpoint of the database.
472
+ #
473
+ # @param lifetime [Integer, nil] Checkpoint lifetime in milliseconds
474
+ # @param name [String, nil] Optional name for the checkpoint
475
+ # @return [Hash] Hash with :id (UUID string) and :manifest_id (integer)
476
+ #
477
+ # @example Create a named checkpoint
478
+ # checkpoint = db.create_checkpoint(name: "before-migration")
479
+ # puts "Checkpoint ID: #{checkpoint[:id]}"
480
+ #
481
+ # @example Create a checkpoint with lifetime
482
+ # checkpoint = db.create_checkpoint(lifetime: 3600_000) # 1 hour
483
+ #
484
+ def create_checkpoint(lifetime: nil, name: nil)
485
+ opts = {}
486
+ opts[:lifetime] = lifetime if lifetime
487
+ opts[:name] = name if name
488
+ _create_checkpoint(opts)
489
+ end
490
+
491
+ # Get database metrics registry.
492
+ #
493
+ # @return [Metrics] Metrics registry
494
+ def metrics
495
+ _metrics
496
+ end
311
497
  end
312
498
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SlateDb
4
+ class Metrics
5
+ # Get a metric value by name.
6
+ #
7
+ # @param name [String] Metric name
8
+ # @return [Integer, nil] Current value or nil if not found
9
+ def [](name)
10
+ get(name)
11
+ end
12
+
13
+ # Convert all metrics to a hash.
14
+ #
15
+ # @return [Hash] Map of metric name to value
16
+ def to_h
17
+ names.to_h { |metric_name| [metric_name, get(metric_name)] }
18
+ end
19
+ end
20
+ end
@@ -11,6 +11,14 @@ module SlateDb
11
11
  # @param manifest_poll_interval [Integer, nil] Poll interval in milliseconds
12
12
  # @param checkpoint_lifetime [Integer, nil] Checkpoint lifetime in milliseconds
13
13
  # @param max_memtable_bytes [Integer, nil] Maximum memtable size in bytes
14
+ # @param cache_root [String, nil] Root folder for the reader's local on-disk
15
+ # object-store cache. Setting this enables the cached object store; when it is
16
+ # not set the cache (and `max_open_file_handles`) has no effect.
17
+ # @param max_open_file_handles [Integer, nil] Maximum number of file handles to keep
18
+ # open in the reader's file-handle cache. When the limit is reached, the least
19
+ # recently used handle is closed (default: 1000). Only takes effect when
20
+ # `cache_root` is set. (Requires SlateDB >= 0.13.0)
21
+ # @param merge_operator [Symbol, String, nil] Optional merge operator ("string_concat" or "concat")
14
22
  # @yield [reader] If a block is given, yields the reader and ensures it's closed
15
23
  # @return [Reader] The opened reader (or block result if block given)
16
24
  #
@@ -27,13 +35,22 @@ module SlateDb
27
35
  # @example Open at a specific checkpoint
28
36
  # reader = SlateDb::Reader.open("/tmp/mydb", checkpoint_id: "uuid-here")
29
37
  #
38
+ # @example Enable the on-disk cache and cap its open file handles
39
+ # reader = SlateDb::Reader.open("/tmp/mydb",
40
+ # cache_root: "/var/cache/slatedb",
41
+ # max_open_file_handles: 256)
42
+ #
30
43
  def open(path, url: nil, checkpoint_id: nil,
31
44
  manifest_poll_interval: nil, checkpoint_lifetime: nil,
32
- max_memtable_bytes: nil)
45
+ max_memtable_bytes: nil, cache_root: nil, max_open_file_handles: nil,
46
+ merge_operator: nil)
33
47
  opts = {}
34
48
  opts[:manifest_poll_interval] = manifest_poll_interval if manifest_poll_interval
35
49
  opts[:checkpoint_lifetime] = checkpoint_lifetime if checkpoint_lifetime
36
50
  opts[:max_memtable_bytes] = max_memtable_bytes if max_memtable_bytes
51
+ opts[:cache_root] = cache_root if cache_root
52
+ opts[:max_open_file_handles] = max_open_file_handles if max_open_file_handles
53
+ opts[:merge_operator] = merge_operator.to_s if merge_operator
37
54
 
38
55
  reader = _open(path, url, checkpoint_id, opts)
39
56
 
@@ -101,5 +118,37 @@ module SlateDb
101
118
  iter
102
119
  end
103
120
  end
121
+
122
+ # Scan all keys with a given prefix.
123
+ #
124
+ # @param prefix [String] The key prefix to scan
125
+ # @param durability_filter [String, nil] Filter by durability level
126
+ # @param dirty [Boolean, nil] Whether to include uncommitted data
127
+ # @param read_ahead_bytes [Integer, nil] Number of bytes to read ahead
128
+ # @param cache_blocks [Boolean, nil] Whether to cache blocks
129
+ # @param max_fetch_tasks [Integer, nil] Maximum number of fetch tasks
130
+ # @return [Iterator] An iterator over key-value pairs
131
+ #
132
+ def scan_prefix(prefix, durability_filter: nil, dirty: nil,
133
+ read_ahead_bytes: nil, cache_blocks: nil, max_fetch_tasks: nil, &)
134
+ opts = {}
135
+ opts[:durability_filter] = durability_filter.to_s if durability_filter
136
+ opts[:dirty] = dirty unless dirty.nil?
137
+ opts[:read_ahead_bytes] = read_ahead_bytes if read_ahead_bytes
138
+ opts[:cache_blocks] = cache_blocks unless cache_blocks.nil?
139
+ opts[:max_fetch_tasks] = max_fetch_tasks if max_fetch_tasks
140
+
141
+ iter = if opts.empty?
142
+ _scan_prefix(prefix)
143
+ else
144
+ _scan_prefix_with_options(prefix, opts)
145
+ end
146
+
147
+ if block_given?
148
+ iter.each(&)
149
+ else
150
+ iter
151
+ end
152
+ end
104
153
  end
105
154
  end
@@ -50,5 +50,37 @@ module SlateDb
50
50
  iter
51
51
  end
52
52
  end
53
+
54
+ # Scan all keys with a given prefix from the snapshot.
55
+ #
56
+ # @param prefix [String] The key prefix to scan
57
+ # @param durability_filter [String, nil] Filter by durability level
58
+ # @param dirty [Boolean, nil] Whether to include uncommitted data
59
+ # @param read_ahead_bytes [Integer, nil] Number of bytes to read ahead
60
+ # @param cache_blocks [Boolean, nil] Whether to cache blocks
61
+ # @param max_fetch_tasks [Integer, nil] Maximum number of fetch tasks
62
+ # @return [Iterator] An iterator over key-value pairs
63
+ #
64
+ def scan_prefix(prefix, durability_filter: nil, dirty: nil,
65
+ read_ahead_bytes: nil, cache_blocks: nil, max_fetch_tasks: nil, &)
66
+ opts = {}
67
+ opts[:durability_filter] = durability_filter.to_s if durability_filter
68
+ opts[:dirty] = dirty unless dirty.nil?
69
+ opts[:read_ahead_bytes] = read_ahead_bytes if read_ahead_bytes
70
+ opts[:cache_blocks] = cache_blocks unless cache_blocks.nil?
71
+ opts[:max_fetch_tasks] = max_fetch_tasks if max_fetch_tasks
72
+
73
+ iter = if opts.empty?
74
+ _scan_prefix(prefix)
75
+ else
76
+ _scan_prefix_with_options(prefix, opts)
77
+ end
78
+
79
+ if block_given?
80
+ iter.each(&)
81
+ else
82
+ iter
83
+ end
84
+ end
53
85
  end
54
86
  end
@@ -47,6 +47,21 @@ module SlateDb
47
47
  _delete(key)
48
48
  end
49
49
 
50
+ # Merge a value within the transaction.
51
+ #
52
+ # @param key [String] The key to merge into
53
+ # @param value [String] The merge operand to apply
54
+ # @param ttl [Integer, nil] Time-to-live in milliseconds
55
+ # @return [void]
56
+ #
57
+ def merge(key, value, ttl: nil)
58
+ if ttl
59
+ _merge_with_options(key, value, { ttl: ttl })
60
+ else
61
+ _merge(key, value)
62
+ end
63
+ end
64
+
50
65
  # Scan a range of keys within the transaction.
51
66
  #
52
67
  # @param start_key [String] The start key (inclusive)
@@ -74,5 +89,86 @@ module SlateDb
74
89
  iter
75
90
  end
76
91
  end
92
+
93
+ # Scan all keys with a given prefix within the transaction.
94
+ #
95
+ # @param prefix [String] The key prefix to scan
96
+ # @param durability_filter [String, nil] Filter by durability level
97
+ # @param dirty [Boolean, nil] Whether to include uncommitted data
98
+ # @param read_ahead_bytes [Integer, nil] Number of bytes to read ahead
99
+ # @param cache_blocks [Boolean, nil] Whether to cache blocks
100
+ # @param max_fetch_tasks [Integer, nil] Maximum number of fetch tasks
101
+ # @return [Iterator] An iterator over key-value pairs
102
+ #
103
+ def scan_prefix(prefix, durability_filter: nil, dirty: nil,
104
+ read_ahead_bytes: nil, cache_blocks: nil, max_fetch_tasks: nil, &)
105
+ opts = {}
106
+ opts[:durability_filter] = durability_filter.to_s if durability_filter
107
+ opts[:dirty] = dirty unless dirty.nil?
108
+ opts[:read_ahead_bytes] = read_ahead_bytes if read_ahead_bytes
109
+ opts[:cache_blocks] = cache_blocks unless cache_blocks.nil?
110
+ opts[:max_fetch_tasks] = max_fetch_tasks if max_fetch_tasks
111
+
112
+ iter = if opts.empty?
113
+ _scan_prefix(prefix)
114
+ else
115
+ _scan_prefix_with_options(prefix, opts)
116
+ end
117
+
118
+ if block_given?
119
+ iter.each(&)
120
+ else
121
+ iter
122
+ end
123
+ end
124
+
125
+ # Mark keys as read for conflict detection.
126
+ #
127
+ # This explicitly tracks reads for conflict checking in serializable isolation,
128
+ # allowing selective read-write conflict detection even when keys weren't
129
+ # actually read via get().
130
+ #
131
+ # @param keys [Array<String>] The keys to mark as read
132
+ # @return [void]
133
+ #
134
+ # @example Mark keys for conflict detection
135
+ # db.transaction(isolation: :serializable) do |txn|
136
+ # txn.mark_read(["key1", "key2"])
137
+ # # These keys will now be checked for conflicts on commit
138
+ # txn.put("key3", "value")
139
+ # end
140
+ #
141
+ def mark_read(keys)
142
+ _mark_read(Array(keys))
143
+ end
144
+
145
+ # Commit the transaction.
146
+ #
147
+ # @param await_durable [Boolean, nil] Whether to wait for durability (default: true)
148
+ # @param seqnum [Integer, nil] User-supplied sequence number for the commit.
149
+ # When provided (and non-zero), it is used instead of the internally
150
+ # generated sequence number and must be strictly greater than the current
151
+ # maximum sequence number. (Requires SlateDB >= 0.13.0)
152
+ # @return [void]
153
+ #
154
+ # @example Commit a transaction
155
+ # txn = db.begin_transaction
156
+ # txn.put("key", "value")
157
+ # txn.commit
158
+ #
159
+ # @example Commit with an explicit sequence number
160
+ # txn.commit(seqnum: 99)
161
+ #
162
+ def commit(await_durable: nil, seqnum: nil)
163
+ opts = {}
164
+ opts[:await_durable] = await_durable unless await_durable.nil?
165
+ opts[:seqnum] = seqnum if seqnum
166
+
167
+ if opts.empty?
168
+ _commit
169
+ else
170
+ _commit_with_options(opts)
171
+ end
172
+ end
77
173
  end
78
174
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module SlateDb
4
- VERSION = "0.1.1"
4
+ VERSION = "0.3.1"
5
5
  end
@@ -34,5 +34,25 @@ module SlateDb
34
34
  _delete(key)
35
35
  self
36
36
  end
37
+
38
+ # Add a merge operation to the batch.
39
+ #
40
+ # @param key [String] The key to merge into
41
+ # @param value [String] The merge operand to apply
42
+ # @param ttl [Integer, nil] Time-to-live in milliseconds
43
+ # @return [self] Returns self for method chaining
44
+ #
45
+ # @example
46
+ # batch.merge("key", "part1")
47
+ # batch.merge("key", "part2", ttl: 30_000)
48
+ #
49
+ def merge(key, value, ttl: nil)
50
+ if ttl
51
+ _merge_with_options(key, value, { ttl: ttl })
52
+ else
53
+ _merge(key, value)
54
+ end
55
+ self
56
+ end
37
57
  end
38
58
  end
data/lib/slatedb.rb CHANGED
@@ -18,3 +18,4 @@ require_relative "slatedb/transaction"
18
18
  require_relative "slatedb/snapshot"
19
19
  require_relative "slatedb/reader"
20
20
  require_relative "slatedb/admin"
21
+ require_relative "slatedb/metrics"