Class: SlateDb::Database

Inherits:
Object
  • Object
show all
Defined in:
lib/slatedb/database.rb

Overview

rubocop:disable Metrics/ClassLength

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.open(path, url: nil, merge_operator: nil) {|db| ... } ⇒ Database

Open a database at the given path.

Examples:

Open a database

db = SlateDb::Database.open("/tmp/mydb")
db.put("key", "value")
db.close

Open with block (auto-close)

SlateDb::Database.open("/tmp/mydb") do |db|
  db.put("key", "value")
end # automatically closed

Open with S3 backend

db = SlateDb::Database.open("/tmp/mydb", url: "s3://mybucket/path")

Open with a custom merge operator (Proc)

# Custom merge that adds numbers
db = SlateDb::Database.open("/tmp/mydb", merge_operator: ->(key, existing, new_val) {
  existing_num = existing ? existing.to_i : 0
  (existing_num + new_val.to_i).to_s
})
db.merge("counter", "5")
db.merge("counter", "3")
db.get("counter") # => "8"

Parameters:

  • path (String)

    The path identifier for the database

  • url (String, nil) (defaults to: nil)

    Optional object store URL (e.g., “s3://bucket/path”)

  • merge_operator (Symbol, String, Proc, nil) (defaults to: nil)

    Optional merge operator. Can be a symbol/string (“string_concat” or “concat”) or a Proc/lambda that takes (key, existing_value, new_value) and returns the merged value.

Yields:

  • (db)

    If a block is given, yields the database and ensures it’s closed

Returns:

  • (Database)

    The opened database (or block result if block given)



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/slatedb/database.rb', line 41

def open(path, url: nil, merge_operator: nil)
  opts = {}

  case merge_operator
  when Symbol, String
    opts[:merge_operator] = merge_operator.to_s
  when Proc
    # Store the proc to prevent GC and pass to Rust
    @_merge_operator_proc = merge_operator
    opts[:merge_operator_proc] = merge_operator
  end

  db = _open(path, url, opts)

  if block_given?
    begin
      yield db
    ensure
      begin
        db.close
      rescue StandardError
        nil
      end
    end
  else
    db
  end
end

Instance Method Details

#batch(await_durable: nil, seqnum: nil) {|batch| ... } ⇒ void

This method returns an undefined value.

Create and write a batch using a block.

Examples:

db.batch do |b|
  b.put("key1", "value1")
  b.put("key2", "value2")
  b.delete("old_key")
end

Parameters:

  • await_durable (Boolean) (defaults to: nil)

    Whether to wait for durability (default: true)

  • seqnum (Integer, nil) (defaults to: nil)

    User-supplied sequence number applied to the batch. See #put for semantics. (Requires SlateDB >= 0.13.0)

Yields:

  • (batch)

    Yields a WriteBatch to the block



370
371
372
373
374
# File 'lib/slatedb/database.rb', line 370

def batch(await_durable: nil, seqnum: nil)
  b = WriteBatch.new
  yield b
  write(b, await_durable: await_durable, seqnum: seqnum)
end

#begin_transaction(isolation: nil) {|txn| ... } ⇒ Transaction, Object

Begin a new transaction.

Examples:

Manual transaction management

txn = db.begin_transaction
txn.put("key", "value")
txn.commit

Block-based transaction (auto-commit)

db.transaction do |txn|
  txn.put("key", "value")
  txn.get("other_key")
end # automatically committed

Serializable isolation

db.transaction(isolation: :serializable) do |txn|
  val = txn.get("counter")
  txn.put("counter", (val.to_i + 1).to_s)
end

Parameters:

  • isolation (Symbol, String) (defaults to: nil)

    Isolation level (:snapshot or :serializable)

Yields:

  • (txn)

    If a block is given, yields the transaction and auto-commits/rollbacks

Returns:

  • (Transaction, Object)

    The transaction (or block result if block given)



399
400
401
402
# File 'lib/slatedb/database.rb', line 399

def begin_transaction(isolation: nil)
  isolation_str = isolation&.to_s
  _begin_transaction(isolation_str)
end

#create_checkpoint(lifetime: nil, name: nil) ⇒ Hash

Create a checkpoint of the database.

Examples:

Create a named checkpoint

checkpoint = db.create_checkpoint(name: "before-migration")
puts "Checkpoint ID: #{checkpoint[:id]}"

Create a checkpoint with lifetime

checkpoint = db.create_checkpoint(lifetime: 3600_000) # 1 hour

Parameters:

  • lifetime (Integer, nil) (defaults to: nil)

    Checkpoint lifetime in milliseconds

  • name (String, nil) (defaults to: nil)

    Optional name for the checkpoint

Returns:

  • (Hash)

    Hash with :id (UUID string) and :manifest_id (integer)



484
485
486
487
488
489
# File 'lib/slatedb/database.rb', line 484

def create_checkpoint(lifetime: nil, name: nil)
  opts = {}
  opts[:lifetime] = lifetime if lifetime
  opts[:name] = name if name
  _create_checkpoint(opts)
end

#delete(key, await_durable: nil, seqnum: nil) ⇒ void

This method returns an undefined value.

Delete a key.

Examples:

Basic delete

db.delete("mykey")

Delete without waiting for durability

db.delete("mykey", await_durable: false)

Parameters:

  • key (String)

    The key to delete

  • await_durable (Boolean) (defaults to: nil)

    Whether to wait for durability (default: true)

  • seqnum (Integer, nil) (defaults to: nil)

    User-supplied sequence number for this write. See #put for semantics. (Requires SlateDB >= 0.13.0)



177
178
179
180
181
182
183
184
185
186
187
# File 'lib/slatedb/database.rb', line 177

def delete(key, await_durable: nil, seqnum: nil)
  opts = {}
  opts[:await_durable] = await_durable unless await_durable.nil?
  opts[:seqnum] = seqnum if seqnum

  if opts.empty?
    _delete(key)
  else
    _delete_with_options(key, opts)
  end
end

#get(key, durability_filter: nil, dirty: nil, cache_blocks: nil) ⇒ String?

Get a value by key.

Examples:

Basic get

value = db.get("mykey")

Get with options

value = db.get("mykey", durability_filter: "memory", dirty: true)

Parameters:

  • key (String)

    The key to look up

  • durability_filter (String, nil) (defaults to: nil)

    Filter by durability level (“remote” or “memory”)

  • dirty (Boolean, nil) (defaults to: nil)

    Whether to include uncommitted data

  • cache_blocks (Boolean, nil) (defaults to: nil)

    Whether to cache blocks

Returns:

  • (String, nil)

    The value, or nil if not found



85
86
87
88
89
90
91
92
93
94
95
96
# File 'lib/slatedb/database.rb', line 85

def get(key, durability_filter: nil, dirty: nil, cache_blocks: nil)
  opts = {}
  opts[:durability_filter] = durability_filter.to_s if durability_filter
  opts[:dirty] = dirty unless dirty.nil?
  opts[:cache_blocks] = cache_blocks unless cache_blocks.nil?

  if opts.empty?
    _get(key)
  else
    _get_with_options(key, opts)
  end
end

#get_key_value(key, durability_filter: nil, dirty: nil, cache_blocks: nil) ⇒ Hash? Also known as: get_entry

Get a key-value pair with SlateDB metadata.

Examples:

Inspect metadata

entry = db.get_key_value("mykey")
entry[:value] # => "myvalue"
entry[:seq]   # => sequence number

Parameters:

  • key (String)

    The key to look up

  • durability_filter (String, nil) (defaults to: nil)

    Filter by durability level (“remote” or “memory”)

  • dirty (Boolean, nil) (defaults to: nil)

    Whether to include uncommitted data

  • cache_blocks (Boolean, nil) (defaults to: nil)

    Whether to cache blocks

Returns:

  • (Hash, nil)

    A hash with :key, :value, :seq, :create_ts, and :expire_ts, or nil if not found



111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/slatedb/database.rb', line 111

def get_key_value(key, durability_filter: nil, dirty: nil, cache_blocks: nil)
  opts = {}
  opts[:durability_filter] = durability_filter.to_s if durability_filter
  opts[:dirty] = dirty unless dirty.nil?
  opts[:cache_blocks] = cache_blocks unless cache_blocks.nil?

  if opts.empty?
    _get_key_value(key)
  else
    _get_key_value_with_options(key, opts)
  end
end

#merge(key, value, ttl: nil, await_durable: nil, seqnum: nil) ⇒ void

This method returns an undefined value.

Merge a value into the database.

Examples:

Merge with string concatenation operator

db = SlateDb::Database.open("/tmp/mydb", merge_operator: :string_concat)
db.merge("key", "part1")
db.merge("key", "part2")

Parameters:

  • key (String)

    The key to merge into

  • value (String)

    The merge operand to apply

  • ttl (Integer, nil) (defaults to: nil)

    Time-to-live in milliseconds

  • await_durable (Boolean) (defaults to: nil)

    Whether to wait for durability (default: true)

  • seqnum (Integer, nil) (defaults to: nil)

    User-supplied sequence number for this write. See #put for semantics. (Requires SlateDB >= 0.13.0)



342
343
344
345
346
347
348
349
350
351
352
353
# File 'lib/slatedb/database.rb', line 342

def merge(key, value, ttl: nil, await_durable: nil, seqnum: nil)
  opts = {}
  opts[:ttl] = ttl if ttl
  opts[:await_durable] = await_durable unless await_durable.nil?
  opts[:seqnum] = seqnum if seqnum

  if opts.empty?
    _merge(key, value)
  else
    _merge_with_options(key, value, opts)
  end
end

#metricsMetrics

Get database metrics registry.

Returns:



494
495
496
# File 'lib/slatedb/database.rb', line 494

def metrics
  _metrics
end

#put(key, value, ttl: nil, await_durable: nil, seqnum: nil) ⇒ void

This method returns an undefined value.

Store a key-value pair.

Examples:

Basic put

db.put("mykey", "myvalue")

Put with TTL

db.put("mykey", "myvalue", ttl: 60_000) # expires in 60 seconds

Put without waiting for durability

db.put("mykey", "myvalue", await_durable: false)

Put with an explicit sequence number

db.put("mykey", "myvalue", seqnum: 42)

Parameters:

  • key (String)

    The key to store

  • value (String)

    The value to store

  • ttl (Integer, nil) (defaults to: nil)

    Time-to-live in milliseconds

  • await_durable (Boolean) (defaults to: nil)

    Whether to wait for durability (default: true)

  • seqnum (Integer, nil) (defaults to: nil)

    User-supplied sequence number for this write. When provided (and non-zero), it is used instead of the internally generated sequence number. It must be strictly greater than the current maximum sequence number or the write fails. (Requires SlateDB >= 0.13.0)



150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/slatedb/database.rb', line 150

def put(key, value, ttl: nil, await_durable: nil, seqnum: nil)
  opts = {}
  opts[:ttl] = ttl if ttl
  opts[:await_durable] = await_durable unless await_durable.nil?
  opts[:seqnum] = seqnum if seqnum

  if opts.empty?
    _put(key, value)
  else
    _put_with_options(key, value, opts)
  end
end

#scan(start_key, end_key = nil, durability_filter: nil, dirty: nil, read_ahead_bytes: nil, cache_blocks: nil, max_fetch_tasks: nil, order: nil) ⇒ Iterator

Scan a range of keys.

Examples:

Basic scan

iter = db.scan("a")
while entry = iter.next_entry
  key, value = entry
  puts "#{key}: #{value}"
end

Scan with range

iter = db.scan("a", "z")

Scan with block

db.scan("user:") do |key, value|
  puts "#{key}: #{value}"
end

Parameters:

  • start_key (String)

    The start key (inclusive)

  • end_key (String, nil) (defaults to: nil)

    The end key (exclusive). If nil, scans to end.

  • durability_filter (String, nil) (defaults to: nil)

    Filter by durability level (“remote” or “memory”)

  • dirty (Boolean, nil) (defaults to: nil)

    Whether to include uncommitted data

  • read_ahead_bytes (Integer, nil) (defaults to: nil)

    Number of bytes to read ahead

  • cache_blocks (Boolean, nil) (defaults to: nil)

    Whether to cache blocks

  • max_fetch_tasks (Integer, nil) (defaults to: nil)

    Maximum number of fetch tasks

  • order (Symbol, String, nil) (defaults to: nil)

    Iteration order (:asc/:ascending or :desc/:descending)

Returns:

  • (Iterator)

    An iterator over key-value pairs



216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/slatedb/database.rb', line 216

def scan(start_key, end_key = nil, durability_filter: nil, dirty: nil,
         read_ahead_bytes: nil, cache_blocks: nil, max_fetch_tasks: nil, order: nil, &)
  opts = scan_options(
    durability_filter: durability_filter,
    dirty: dirty,
    read_ahead_bytes: read_ahead_bytes,
    cache_blocks: cache_blocks,
    max_fetch_tasks: max_fetch_tasks,
    order: order
  )

  iter = if opts.empty?
           _scan(start_key, end_key)
         else
           _scan_with_options(start_key, end_key, opts)
         end

  if block_given?
    iter.each(&)
  else
    iter
  end
end

#scan_prefix(prefix, durability_filter: nil, dirty: nil, read_ahead_bytes: nil, cache_blocks: nil, max_fetch_tasks: nil, order: nil) ⇒ Iterator

Scan all keys with a given prefix.

Examples:

Scan all user keys

db.scan_prefix("user:") do |key, value|
  puts "#{key}: #{value}"
end

Parameters:

  • prefix (String)

    The key prefix to scan

  • durability_filter (String, nil) (defaults to: nil)

    Filter by durability level (“remote” or “memory”)

  • dirty (Boolean, nil) (defaults to: nil)

    Whether to include uncommitted data

  • read_ahead_bytes (Integer, nil) (defaults to: nil)

    Number of bytes to read ahead

  • cache_blocks (Boolean, nil) (defaults to: nil)

    Whether to cache blocks

  • max_fetch_tasks (Integer, nil) (defaults to: nil)

    Maximum number of fetch tasks

  • order (Symbol, String, nil) (defaults to: nil)

    Iteration order (:asc/:ascending or :desc/:descending)

Returns:

  • (Iterator)

    An iterator over key-value pairs



256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/slatedb/database.rb', line 256

def scan_prefix(prefix, durability_filter: nil, dirty: nil,
                read_ahead_bytes: nil, cache_blocks: nil, max_fetch_tasks: nil, order: nil, &)
  opts = scan_options(
    durability_filter: durability_filter,
    dirty: dirty,
    read_ahead_bytes: read_ahead_bytes,
    cache_blocks: cache_blocks,
    max_fetch_tasks: max_fetch_tasks,
    order: order
  )

  iter = if opts.empty?
           _scan_prefix(prefix)
         else
           _scan_prefix_with_options(prefix, opts)
         end

  if block_given?
    iter.each(&)
  else
    iter
  end
end

#snapshot {|snapshot| ... } ⇒ Snapshot, Object

Create a snapshot for consistent reads.

Examples:

Manual snapshot management

snapshot = db.snapshot
snapshot.get("key")
snapshot.close

Block-based snapshot (auto-close)

db.snapshot do |snap|
  snap.get("key1")
  snap.get("key2")
end # automatically closed

Yields:

  • (snapshot)

    If a block is given, yields the snapshot and auto-closes

Returns:

  • (Snapshot, Object)

    The snapshot (or block result if block given)



453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
# File 'lib/slatedb/database.rb', line 453

def snapshot
  snap = _snapshot

  if block_given?
    begin
      yield snap
    ensure
      begin
        snap.close
      rescue StandardError
        nil
      end
    end
  else
    snap
  end
end

#transaction(isolation: nil) {|txn| ... } ⇒ Object

Execute a block within a transaction.

The transaction is automatically committed if the block succeeds, or rolled back if an exception is raised.

Examples:

result = db.transaction do |txn|
  old_val = txn.get("counter") || "0"
  new_val = (old_val.to_i + 1).to_s
  txn.put("counter", new_val)
  new_val
end

Parameters:

  • isolation (Symbol, String) (defaults to: nil)

    Isolation level (:snapshot or :serializable)

Yields:

  • (txn)

    Yields the transaction to the block

Returns:

  • (Object)

    The result of the block



421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
# File 'lib/slatedb/database.rb', line 421

def transaction(isolation: nil)
  txn = begin_transaction(isolation: isolation)
  begin
    result = yield txn
    txn.commit
    result
  rescue StandardError
    begin
      txn.rollback
    rescue StandardError
      nil
    end
    raise
  end
end

#write(batch, await_durable: nil, seqnum: nil) ⇒ void

This method returns an undefined value.

Write a batch of operations atomically.

Examples:

Write a batch

batch = SlateDb::WriteBatch.new
batch.put("key1", "value1")
batch.put("key2", "value2")
batch.delete("key3")
db.write(batch)

Using batch block helper

db.batch do |b|
  b.put("key1", "value1")
  b.put("key2", "value2")
end

Parameters:

  • batch (WriteBatch)

    The batch to write

  • await_durable (Boolean) (defaults to: nil)

    Whether to wait for durability (default: true)

  • seqnum (Integer, nil) (defaults to: nil)

    User-supplied sequence number applied to the batch. See #put for semantics. (Requires SlateDB >= 0.13.0)



315
316
317
318
319
320
321
322
323
324
325
# File 'lib/slatedb/database.rb', line 315

def write(batch, await_durable: nil, seqnum: nil)
  opts = {}
  opts[:await_durable] = await_durable unless await_durable.nil?
  opts[:seqnum] = seqnum if seqnum

  if opts.empty?
    _write(batch)
  else
    _write_with_options(batch, opts)
  end
end