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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 552b130661804f60d094bdbc1cdcabfd24c06e26c08c6aa740b3e9af0e32e5b1
4
- data.tar.gz: 642ac1943fc3d7e280e771b3853b0ad64dfce9a27c0a94b4eb7cbc85b174dec1
3
+ metadata.gz: 86d98cff01dab0521da8aa8be968dab6b2b990452b2a4b2beca80b23687b6eea
4
+ data.tar.gz: 92ea4d60de2dbafdc31cb0cb346822a51b1d9ea0defe01d7f3192eaae6878b07
5
5
  SHA512:
6
- metadata.gz: 543c087ac0505a2cc2f5496152c7c53729867b09de9f4b25b0ed234204cc4047112f8cb572bbe7ea8700452774c84bae42c101c277fe02bd190f405a43a20e02
7
- data.tar.gz: 88c31e5c3bdfbb0ce431fed1a22cb9a81a77bfca64053b9ee845111e53e91b3a969820db0592ef3b3178c19224b7cb0934d0de27bae576deec8b2338d95bbd73
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.1.0 C ABI and does not implement the protocol, networking, TLS, authentication, failover, or store-and-forward in Ruby.
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
 
@@ -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.1.0 | Apache-2.0
134
- questdb-rs-ffi | 7.1.0 | Apache-2.0 (upstream workspace license)
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
@@ -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.1.0 so new native settings do not require a Ruby release. Important groups include:
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.1.0 connect-string reference for exact constraints. Unknown or invalid settings are rejected by the native parser.
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.1.0. Ruby owns thin typed-data wrappers for:
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;
@@ -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.1.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.
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,
@@ -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
@@ -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"
@@ -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
- @native.wait(normalize_ack(ack), timeout)
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
 
@@ -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
- @native.wait(normalize_ack(ack), non_negative_integer(timeout_millis, "timeout_millis"))
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" }
@@ -21,7 +21,7 @@
21
21
  #
22
22
  module QuestDB
23
23
  module Ingress
24
- VERSION = "0.1.0"
25
- C_CLIENT_VERSION = "7.1.0"
24
+ VERSION = "0.1.1"
25
+ C_CLIENT_VERSION = "7.2.0"
26
26
  end
27
27
  end
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.0
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-20 00:00:00.000000000 Z
11
+ date: 2026-10-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal