questdb 0.1.0-x86_64-linux-gnu → 0.1.1-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 +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +1 -1
- data/THIRD_PARTY_NOTICES.md +2 -2
- data/docs/configuration.md +3 -2
- data/docs/design.md +1 -1
- data/docs/python-parity.md +1 -1
- data/docs/releasing.md +1 -1
- data/lib/questdb/arrow.rb +2 -0
- data/lib/questdb/client.rb +6 -0
- data/lib/questdb/config.rb +19 -0
- data/lib/questdb/native/3.1/questdb_ingress.so +0 -0
- data/lib/questdb/native/3.2/questdb_ingress.so +0 -0
- data/lib/questdb/native/3.3/questdb_ingress.so +0 -0
- data/lib/questdb/native/3.4/questdb_ingress.so +0 -0
- data/lib/questdb/pooled_sender.rb +4 -1
- data/lib/questdb/sender.rb +10 -1
- data/lib/questdb/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 86d98cff01dab0521da8aa8be968dab6b2b990452b2a4b2beca80b23687b6eea
|
|
4
|
+
data.tar.gz: 92ea4d60de2dbafdc31cb0cb346822a51b1d9ea0defe01d7f3192eaae6878b07
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8936786af5a14b87096c17968e47467f90dc368df8ab086a34ce339899e69ce555b329f5c6cf82b6d1844e8e5544444de6d8bff30c0c7f3c54a438d636cd9f77
|
|
7
|
+
data.tar.gz: e31d326d70450569cd8d1fdcfe3b78bc4e89d247557af3972705a287abfd9227de3891f1b777984f90719ab557ce33d91c077426e9a29d3862d4160efd644718
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.1 (2026-10-21)
|
|
4
|
+
|
|
5
|
+
- Fixed precompiled gems that installed but failed on `require "questdb"`. The 0.1.0 native extensions were linked against the Ruby on the build machine: macOS gems referenced that machine's `libruby` path (or, for Ruby 3.1 on ARM64, symbols in its `ruby` executable), and Linux gems required `libruby.so`, so they failed with Rubies built without a shared library. Extensions now resolve Ruby's C API from the Ruby that loads them. The release pipeline checks every extension's linkage, because install tests on the build machines cannot detect this.
|
|
6
|
+
- Fixed `ack: :durable` without `request_durable_ack` enabled: `ingest_columns`, `ingest_arrow`, `flush(wait: true)`, and `wait` now raise `:invalid_api_call` before sending anything. Previously direct ingestion committed its rows and then raised an in-doubt error.
|
|
7
|
+
- Updated to `c-questdb-client` 7.2.0. The `query_timeout_ms` connect-string key sets a client-side timeout for queries on servers that support it; an expired query raises `QuestDB::Error` with code `:query_timeout`. Standalone WebSocket flushes now check the full frame-size limit before writing anything.
|
|
8
|
+
- Known issue: `c-questdb-client` 7.2.0 still locks rustls 0.23.41, rustls-webpki 0.103.13 and webpki-roots 1.0.8. In addition to the four rustls advisories listed for 0.1.0, RUSTSEC-2026-0381 (webpki-roots) and RUSTSEC-2026-0382 (rustls-webpki), both about trusting distrusted certificate intermediates, are temporarily ignored in `cargo audit` (`.cargo/audit.toml`) until a `c-questdb-client` patch release updates these crates.
|
|
9
|
+
|
|
3
10
|
## 0.1.0 (2026-10-19)
|
|
4
11
|
|
|
5
12
|
Initial pre-1.0 release for early adopters. Public APIs may evolve before 1.0; delivery and acknowledgement semantics are intentional compatibility commitments and changes will be documented here.
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# QuestDB Ruby client
|
|
2
2
|
|
|
3
|
-
`questdb` is an MRI Ruby client for QuestDB's native **QuestDB Wire Protocol (QWP)** over WebSocket. It is backed by the official `c-questdb-client` 7.
|
|
3
|
+
`questdb` is an MRI Ruby client for QuestDB's native **QuestDB Wire Protocol (QWP)** over WebSocket. It is backed by the official `c-questdb-client` 7.2.0 C ABI and does not implement the protocol, networking, TLS, authentication, failover, or store-and-forward in Ruby.
|
|
4
4
|
|
|
5
5
|
The client provides a deployment-level connection pool, pooled row and direct columnar ingestion, streaming SQL queries, safe Ruby dispatch for lifecycle/rejection callbacks, and standalone QWP WebSocket senders. Query batches and callback values are copied into ordinary Ruby objects before borrowed native data expires. Legacy ILP transports are intentionally not part of this client.
|
|
6
6
|
|
data/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -130,8 +130,8 @@ powerfmt | 0.2.0 | MIT OR Apache-2.0
|
|
|
130
130
|
ppv-lite86 | 0.2.21 | MIT OR Apache-2.0
|
|
131
131
|
proc-macro2 | 1.0.106 | MIT OR Apache-2.0
|
|
132
132
|
questdb-confstr | 0.1.1 | Apache-2.0
|
|
133
|
-
questdb-rs | 7.
|
|
134
|
-
questdb-rs-ffi | 7.
|
|
133
|
+
questdb-rs | 7.2.0 | Apache-2.0
|
|
134
|
+
questdb-rs-ffi | 7.2.0 | Apache-2.0 (upstream workspace license)
|
|
135
135
|
quote | 1.0.46 | MIT OR Apache-2.0
|
|
136
136
|
r-efi | 5.3.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later
|
|
137
137
|
r-efi | 6.0.0 | MIT OR Apache-2.0 OR LGPL-2.1-or-later
|
data/docs/configuration.md
CHANGED
|
@@ -20,15 +20,16 @@ The Ruby-only keywords `connection_listener`, `error_handler`, `connection_event
|
|
|
20
20
|
|
|
21
21
|
## Native settings
|
|
22
22
|
|
|
23
|
-
Settings are passed directly to `c-questdb-client` 7.
|
|
23
|
+
Settings are passed directly to `c-questdb-client` 7.2.0 so new native settings do not require a Ruby release. Important groups include:
|
|
24
24
|
|
|
25
25
|
- pool: `sender_pool_min`, `sender_pool_max`, `query_pool_min`, `query_pool_max`, `acquire_timeout_ms`, `idle_timeout_ms`, `pool_reap`, `lazy_connect`;
|
|
26
26
|
- reconnect/failover: `addr` (comma-separated endpoints), `target`, `zone`, `reconnect_initial_backoff_millis`, `reconnect_max_backoff_millis`, `reconnect_max_duration_millis`, `initial_connect_retry`;
|
|
27
27
|
- store-and-forward: `sf_dir`, `sender_id`, `sf_max_segment_bytes`, `sf_max_total_bytes`, `sf_append_deadline_millis`, `sf_durability`, `sf_sync_interval_millis`, `drain_orphans`, `max_background_drainers`;
|
|
28
28
|
- delivery: `request_durable_ack`, `close_flush_timeout_millis`, `max_buf_size`, `max_name_len`, `error_inbox_capacity`;
|
|
29
|
+
- query: `query_timeout_ms` (expiry raises `:query_timeout`; servers without per-query timeout support reject queries with `:unsupported_server`);
|
|
29
30
|
- authentication/TLS: `username`, `password`, `token`, `auth_timeout_ms`, `tls_verify`, `tls_ca`, `tls_roots`, `tls_roots_password`.
|
|
30
31
|
|
|
31
|
-
Consult the matching upstream 7.
|
|
32
|
+
Consult the matching upstream 7.2.0 connect-string reference for exact constraints. Unknown or invalid settings are rejected by the native parser.
|
|
32
33
|
|
|
33
34
|
Direct column ingestion borrows from the native direct sender pool and uses `acquire_timeout_ms`; initial direct connection retry is bounded by `reconnect_max_duration_millis`. `max_rows_per_batch` is a method argument rather than a connection setting. Direct writes do not use `sf_dir`, do not produce FSNs, and are unaffected by row auto-publish thresholds. `ack: :durable` still requires both `request_durable_ack=on` and server support.
|
|
34
35
|
|
data/docs/design.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Native boundary
|
|
4
4
|
|
|
5
|
-
The gem links statically to official `c-questdb-client` 7.
|
|
5
|
+
The gem links statically to official `c-questdb-client` 7.2.0. Ruby owns thin typed-data wrappers for:
|
|
6
6
|
|
|
7
7
|
- one ref-counted `questdb_db` pool state;
|
|
8
8
|
- short-lived borrowed `qwp_sender` leases;
|
data/docs/python-parity.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Python QWP parity
|
|
2
2
|
|
|
3
|
-
This client tracks the QWP surface of the QuestDB Python 5.0 client at the `c-questdb-client` 7.
|
|
3
|
+
This client tracks the QWP surface of the QuestDB Python 5.0 client at the `c-questdb-client` 7.2.0 ABI boundary. Parity means equivalent delivery, lifecycle, type, and error semantics expressed idiomatically in Ruby; it does not mean retaining Python's legacy ILP APIs.
|
|
4
4
|
|
|
5
5
|
| Python QWP capability | Ruby API | Status |
|
|
6
6
|
| --- | --- | --- |
|
data/docs/releasing.md
CHANGED
|
@@ -17,7 +17,7 @@ The versioned layout is required because MRI's native ABI and `libruby` soname m
|
|
|
17
17
|
|
|
18
18
|
1. Update `QuestDB::Ingress::VERSION` and change the changelog heading from `unreleased` to the intended release date.
|
|
19
19
|
2. Build from a clean candidate commit with recursively initialized submodules on approved QuestDB infrastructure.
|
|
20
|
-
3. Require Ruby and Rust dependency audits to pass without ignored advisories. Temporary exception for 0.1.0: the rustls advisories listed in `.cargo/audit.toml` are ignored until a patch release moves `c-questdb-client` to rustls 0.23.46 or newer; remove each ignore as soon as its fix ships. Enterprise-only durable-ACK qualification remains optional, matching the Python client's policy.
|
|
20
|
+
3. Require Ruby and Rust dependency audits to pass without ignored advisories. Temporary exception for 0.1.x on `c-questdb-client` 7.1.0 and 7.2.0: the rustls, rustls-webpki and webpki-roots advisories listed in `.cargo/audit.toml` are ignored until a patch release moves `c-questdb-client` to rustls 0.23.46, rustls-webpki 0.103.16 and webpki-roots 1.0.10 or newer; remove each ignore as soon as its fix ships. Enterprise-only durable-ACK qualification remains optional, matching the Python client's policy.
|
|
21
21
|
4. Run unit, integration, Arrow, native-memory, package, and bounded fault qualification. Shared-host benchmark results remain informational.
|
|
22
22
|
5. Produce all eight gems, then run `script/verify-gem` on each final file and inspect native linkage. Clean-install each native gem on MRI 3.1, 3.2, 3.3, and 3.4 on its matching platform.
|
|
23
23
|
6. Confirm the source gem installs with `CARGO_NET_OFFLINE=true` in a clean `GEM_HOME` and contains no generated extension or nested QuestDB server source.
|
data/lib/questdb/arrow.rb
CHANGED
|
@@ -170,6 +170,7 @@ module QuestDB
|
|
|
170
170
|
checkpoint_batches = Integer(checkpoint_batches)
|
|
171
171
|
raise ArgumentError, "checkpoint_batches must be positive" unless checkpoint_batches.positive?
|
|
172
172
|
@mutex.synchronize { ensure_open! }
|
|
173
|
+
ensure_durable_ack!(ack_level)
|
|
173
174
|
Arrow.with_stream(source) do |capsule|
|
|
174
175
|
@native.ingest_arrow(
|
|
175
176
|
capsule, table, at_kind, at_value, overrides, ack_level, checkpoint_batches
|
|
@@ -201,6 +202,7 @@ module QuestDB
|
|
|
201
202
|
checkpoint_batches = Integer(checkpoint_batches)
|
|
202
203
|
raise ArgumentError, "checkpoint_batches must be positive" unless checkpoint_batches.positive?
|
|
203
204
|
ensure_ws!
|
|
205
|
+
ensure_durable_ack!(ack_level)
|
|
204
206
|
Arrow.with_stream(source) do |capsule|
|
|
205
207
|
@native.ingest_arrow(
|
|
206
208
|
@configuration, capsule, table, at_kind, at_value,
|
data/lib/questdb/client.rb
CHANGED
|
@@ -84,6 +84,7 @@ module QuestDB
|
|
|
84
84
|
error_capacity = validate_capacity!(error_event_inbox_capacity, "error_event_inbox_capacity")
|
|
85
85
|
parsed_settings = effective_settings || Config.parse(configuration)
|
|
86
86
|
@auto_flush = parse_auto_flush(parsed_settings)
|
|
87
|
+
@durable_ack_requested = Config.durable_ack_requested?(parsed_settings)
|
|
87
88
|
@mutex = Mutex.new
|
|
88
89
|
@native = Ingress::Native::Client.new(
|
|
89
90
|
configuration, !connection_listener.nil?, connection_capacity,
|
|
@@ -160,6 +161,7 @@ module QuestDB
|
|
|
160
161
|
ack: ack
|
|
161
162
|
)
|
|
162
163
|
@mutex.synchronize { ensure_open! }
|
|
164
|
+
ensure_durable_ack!(prepared[6])
|
|
163
165
|
return self if prepared[4].zero?
|
|
164
166
|
|
|
165
167
|
@native.ingest_columns(*prepared)
|
|
@@ -249,6 +251,10 @@ module QuestDB
|
|
|
249
251
|
end
|
|
250
252
|
end
|
|
251
253
|
|
|
254
|
+
def ensure_durable_ack!(ack_level)
|
|
255
|
+
Config.ensure_durable_ack!(@durable_ack_requested, ack_level, PooledSender::ACK_LEVELS[:durable])
|
|
256
|
+
end
|
|
257
|
+
|
|
252
258
|
def ensure_open!
|
|
253
259
|
raise Ingress::IngressError.new("client is closed", code: :invalid_api_call) if closed?
|
|
254
260
|
end
|
data/lib/questdb/config.rb
CHANGED
|
@@ -117,6 +117,25 @@ module QuestDB
|
|
|
117
117
|
parts
|
|
118
118
|
end
|
|
119
119
|
|
|
120
|
+
# Whether request_durable_ack asks for durable ACKs. Only the absent and
|
|
121
|
+
# explicit off values are recognised, so future tier values pass through
|
|
122
|
+
# to the native client unchanged.
|
|
123
|
+
def durable_ack_requested?(settings)
|
|
124
|
+
value = settings["request_durable_ack"]
|
|
125
|
+
!value.nil? && !value.to_s.strip.match?(/\A(?:off|false)\z/i)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Fails before anything is sent when ack: :durable can never succeed,
|
|
129
|
+
# instead of after a direct batch or a row frame was already published.
|
|
130
|
+
def ensure_durable_ack!(requested, ack_level, durable_level)
|
|
131
|
+
return if requested || ack_level != durable_level
|
|
132
|
+
|
|
133
|
+
raise Ingress::IngressError.new(
|
|
134
|
+
"ack: :durable requires request_durable_ack=on in the configuration; nothing was sent",
|
|
135
|
+
code: :invalid_api_call
|
|
136
|
+
)
|
|
137
|
+
end
|
|
138
|
+
|
|
120
139
|
def setting_value(value, key = nil)
|
|
121
140
|
if key&.to_sym == :tls_verify && value == false
|
|
122
141
|
"unsafe_off"
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -61,6 +61,7 @@ module QuestDB
|
|
|
61
61
|
ensure_open!
|
|
62
62
|
level = normalize_ack(ack)
|
|
63
63
|
raise ArgumentError, "ack: :durable requires wait: true" if level == ACK_LEVELS[:durable] && !wait
|
|
64
|
+
@client.send(:ensure_durable_ack!, level)
|
|
64
65
|
return self if empty? && !wait
|
|
65
66
|
@native.flush(@buffer.send(:native), !!wait, level)
|
|
66
67
|
@batch_started = nil if @buffer.empty?
|
|
@@ -70,7 +71,9 @@ module QuestDB
|
|
|
70
71
|
def wait(timeout_millis: 0, ack: :ok)
|
|
71
72
|
ensure_open!
|
|
72
73
|
timeout = non_negative_integer(timeout_millis, "timeout_millis")
|
|
73
|
-
|
|
74
|
+
level = normalize_ack(ack)
|
|
75
|
+
@client.send(:ensure_durable_ack!, level)
|
|
76
|
+
@native.wait(level, timeout)
|
|
74
77
|
self
|
|
75
78
|
end
|
|
76
79
|
|
data/lib/questdb/sender.rb
CHANGED
|
@@ -121,6 +121,7 @@ module QuestDB
|
|
|
121
121
|
max_rows_per_batch: max_rows_per_batch, ack: ack
|
|
122
122
|
)
|
|
123
123
|
ensure_ws!
|
|
124
|
+
ensure_durable_ack!(prepared[6])
|
|
124
125
|
return self if prepared[4].zero?
|
|
125
126
|
@native.ingest_columns(@configuration, *prepared)
|
|
126
127
|
self
|
|
@@ -142,6 +143,7 @@ module QuestDB
|
|
|
142
143
|
raise ArgumentError, "clear: false requires an explicit buffer" if target.equal?(@buffer) && !clear
|
|
143
144
|
level = normalize_ack(ack)
|
|
144
145
|
raise ArgumentError, "ack: :durable requires wait: true" if level == ACK_LEVELS[:durable] && !wait
|
|
146
|
+
ensure_durable_ack!(level)
|
|
145
147
|
return self if target.empty? && !wait
|
|
146
148
|
@native.flush(target.send(:native), !clear)
|
|
147
149
|
@native.wait(level, 0) if wait
|
|
@@ -151,7 +153,9 @@ module QuestDB
|
|
|
151
153
|
|
|
152
154
|
def wait(timeout_millis: 0, ack: :ok)
|
|
153
155
|
ensure_ws!
|
|
154
|
-
|
|
156
|
+
level = normalize_ack(ack)
|
|
157
|
+
ensure_durable_ack!(level)
|
|
158
|
+
@native.wait(level, non_negative_integer(timeout_millis, "timeout_millis"))
|
|
155
159
|
self
|
|
156
160
|
end
|
|
157
161
|
|
|
@@ -339,6 +343,7 @@ module QuestDB
|
|
|
339
343
|
@dispatcher_threads = []
|
|
340
344
|
@native = @buffer = nil
|
|
341
345
|
@closed = false
|
|
346
|
+
@durable_ack_requested = Config.durable_ack_requested?(settings)
|
|
342
347
|
@init_buf_size = Integer(settings.fetch("init_buf_size", 65_536))
|
|
343
348
|
raise ArgumentError, "init_buf_size must be nonnegative" if @init_buf_size.negative?
|
|
344
349
|
raise RangeError, "init_buf_size exceeds 100 MiB" if @init_buf_size > Ingress::Buffer::MAX_PREALLOCATE_BYTES
|
|
@@ -394,6 +399,10 @@ module QuestDB
|
|
|
394
399
|
true
|
|
395
400
|
end
|
|
396
401
|
|
|
402
|
+
def ensure_durable_ack!(ack_level)
|
|
403
|
+
Config.ensure_durable_ack!(@durable_ack_requested, ack_level, ACK_LEVELS[:durable])
|
|
404
|
+
end
|
|
405
|
+
|
|
397
406
|
def normalize_ack(value)
|
|
398
407
|
value = value.to_sym if value.respond_to?(:to_sym)
|
|
399
408
|
ACK_LEVELS.fetch(value) { raise ArgumentError, "ack must be :ok or :durable" }
|
data/lib/questdb/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: questdb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: x86_64-linux-gnu
|
|
6
6
|
authors:
|
|
7
7
|
- QuestDB
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-10-
|
|
11
|
+
date: 2026-10-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: bigdecimal
|