virgodb 0.1.4-x86_64-linux-gnu → 0.3.0-x86_64-linux-gnu

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: cfde1ca05cc7f6d12cd117d9ff1ec6bd215a5914637f4641b2b01822077357ac
4
- data.tar.gz: 3c51aabe24cf49d04bc8fe1783c1b6533e830f57d1985bcdbc513067f250a4bf
3
+ metadata.gz: 6141e1a5628b4293f7d63bd3143fe20bcbff6f697df38fc67c1da4c2669369ab
4
+ data.tar.gz: 7814e9e3a10ba0ce4e5126e492d38410637aa818d21abb4352b6f038fc9b4139
5
5
  SHA512:
6
- metadata.gz: c43403672c327752e7710e1bdfce3bbca7710d4b20d27f42529ded6272776bd553ec5d40894a2bc53e4e92cf6ba9628eb6a28860b3fdd9e72a6dfb3674f582da
7
- data.tar.gz: 5162de79d2d0979f6677535250c612dd3c1e208266339a8120612fb0ae6a1a6fa8c0fe75ba2aaaf01c2289626d0e83155d41f7f9e24e3c72ed73547a312fad43
6
+ metadata.gz: '0468833ba6f07aed2d56ea13ca5d158a523fb64486ec4fa5259c969200f9c4e991fb96a545ad26a78c8531460855c59dcbcc0904be5a8d43f558dd2bb4f27255'
7
+ data.tar.gz: 475c654f9a604b83c05b0036c4cdffb478c339b05fb794a9cef445b5244584394cef90289bbb57cdc8f7c65a8918fb7a8111e545d9a790c6b375eca454929a47
data/lib/virgodb/table.rb CHANGED
@@ -11,6 +11,19 @@ module Virgodb
11
11
  # just failing the first write attempt. Mutex-guarded so concurrent
12
12
  # callers in one process can't race to double-initialize.
13
13
  #
14
+ # The SAME mutex also serializes every actual write_row/tombstone/flush!
15
+ # call, not just lazy Writer init -- the underlying Rust Writer wraps a
16
+ # plain RefCell, explicitly documented "not thread-safe by design," so
17
+ # two Ruby threads calling in concurrently would double-borrow it. This
18
+ # is safe today even without a Ruby-level lock, PURELY because none of
19
+ # those three Rust entry points release the GVL (crates/virgodb_ruby/
20
+ # src/writer.rs, tombstone.rs) -- MRI's GVL already fully serializes them.
21
+ # That's an implicit invariant a future perf change could silently break
22
+ # (the read path already got its own GVL-release fix once, see the root
23
+ # README's "Known issues" -- the write path getting the same treatment
24
+ # someday isn't a hypothetical). Locking explicitly here means this
25
+ # class's correctness no longer depends on that invariant holding.
26
+ #
14
27
  # This is the boilerplate every host app embedding virgodb needs around
15
28
  # the raw Writer/start_maintenance/query FFI -- written once here instead
16
29
  # of once per host app. Deliberately does NOT rescue write failures: a
@@ -35,8 +48,18 @@ module Virgodb
35
48
 
36
49
  attr_reader :manifest_path
37
50
 
51
+ # `writer` is called BEFORE entering `@mutex.synchronize` here (and in
52
+ # `tombstone`/`flush!` below), never from inside it -- `writer`'s own
53
+ # lazy-init path takes the SAME mutex when it actually needs to build
54
+ # one, and Ruby's Mutex is not reentrant (a thread re-locking a mutex
55
+ # it already holds raises ThreadError immediately, it doesn't queue).
56
+ # Safe regardless of ordering: `@writer` is assigned at most once and
57
+ # never reassigned afterward, so the reference `writer` returns stays
58
+ # valid and correct for the rest of this object's lifetime no matter
59
+ # what happens between fetching it and entering the lock below.
38
60
  def write_row(**attrs)
39
- writer.write_row(**attrs)
61
+ w = writer
62
+ @mutex.synchronize { w.write_row(**attrs) }
40
63
  end
41
64
 
42
65
  # `tombstone` alone doesn't touch the Writer -- calling `writer` first
@@ -45,19 +68,60 @@ module Virgodb
45
68
  # recorded for it to eventually act on.
46
69
  def tombstone(column:, value:)
47
70
  writer
48
- Virgodb.tombstone(@manifest_path, @sql_table_name, column.to_s, value.to_s)
71
+ @mutex.synchronize { Virgodb.tombstone(@manifest_path, @sql_table_name, column.to_s, value.to_s) }
49
72
  end
50
73
 
51
74
  # Returns parsed JSON rows (Array of String-keyed Hash). `sql` must
52
75
  # refer to the table by its real SQL name (the `sql_table_name` this
53
76
  # instance was built with), not any shorthand a caller's own registry
54
- # might use for it.
77
+ # might use for it. Not mutex-guarded -- a read never touches the
78
+ # Writer's RefCell state at all (Virgodb.query is a separate, already
79
+ # GVL-released, independently-safe-for-concurrency FFI entry point).
55
80
  def query(sql, partition_keys: nil)
56
81
  JSON.parse(Virgodb.query(@manifest_path, @sql_table_name, sql, partition_keys))
57
82
  end
58
83
 
59
84
  def flush!
60
- writer.flush!
85
+ w = writer
86
+ @mutex.synchronize { w.flush! }
87
+ end
88
+
89
+ # This table's current maintenance status (a String-keyed Hash with
90
+ # "holder"/"lease_expires_at"/"last_attempt_at"/"last_success_at"/
91
+ # "last_error"), or nil if maintenance has never even attempted a lease
92
+ # claim yet (e.g. this Table hasn't done its first write, so
93
+ # start_maintenance was never called -- see `ensure_maintenance_started`).
94
+ # A real answer to "is this table's background maintenance actually
95
+ # working," not just "is the thread alive" -- meant to be polled by a
96
+ # host app's own health check, not by this class itself. Not
97
+ # mutex-guarded, same reasoning as `query` above: a read never touches
98
+ # the Writer's RefCell state.
99
+ def maintenance_health
100
+ json = Virgodb.maintenance_health(@manifest_path, @sql_table_name)
101
+ json && JSON.parse(json)
102
+ end
103
+
104
+ # Claims this table's maintenance lease as `holder` for the next
105
+ # `lease_duration_secs` seconds, locking out the real maintenance
106
+ # thread (its own claim attempts fail against an unexpired lease the
107
+ # same way two maintenance threads would contend) -- meant for a
108
+ # caller like a backup job that needs a window where compaction/vacuum
109
+ # won't rewrite or delete this table's data directory. Returns `false`
110
+ # (not an error) if someone else's lease is still active; callers
111
+ # should retry rather than treat that as failure. Doesn't call `writer`
112
+ # first (unlike `tombstone` above) -- claiming a lease has nothing to
113
+ # do with this table's Writer, and a caller like a backup job may never
114
+ # otherwise touch this table's Writer in this process at all. Not
115
+ # mutex-guarded, same reasoning as `query`/`maintenance_health` above.
116
+ def claim_maintenance_lease(holder:, lease_duration_secs:)
117
+ Virgodb.claim_maintenance_lease(@manifest_path, @sql_table_name, holder.to_s, lease_duration_secs)
118
+ end
119
+
120
+ # Releases a lease claimed via `claim_maintenance_lease` above, so the
121
+ # real maintenance thread can resume on its next poll rather than
122
+ # waiting out the full lease duration.
123
+ def release_maintenance_lease(holder:)
124
+ Virgodb.release_maintenance_lease(@manifest_path, @sql_table_name, holder.to_s)
61
125
  end
62
126
 
63
127
  private
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Virgodb
4
- VERSION = "0.1.4"
4
+ VERSION = "0.3.0"
5
5
  end
Binary file
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: virgodb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.3.0
5
5
  platform: x86_64-linux-gnu
6
6
  authors:
7
7
  - virgodb
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-08-16 00:00:00.000000000 Z
11
+ date: 2026-09-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: |
14
14
  Embeds virgodb (an OLAP engine backed by Parquet + DataFusion) directly into a Ruby process