cqlite-core 0.13.0

Core engine for CQLite — read Apache Cassandra 5.0 SSTables locally without a cluster
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
//! Metric catalog — the single source of truth for CQLite metric names + units.
//!
//! Downstream observability issues (epic #1031) MUST import the constants from
//! this module instead of hard-coding metric-name strings. Centralising the
//! names here keeps the emitted telemetry consistent across subsystems and lets
//! us evolve names in exactly one place.
//!
//! # Naming conventions
//!
//! Names follow the OpenTelemetry metric semantic conventions:
//! - dot-separated, lower-snake namespaces, rooted under `cqlite.`
//! - units use the UCUM annotations OTel recommends (`{row}`, `s`, `By`, `1`,
//!   `{error}`, …); see [`unit`].
//! - counters describe a monotonically increasing total; their name reflects the
//!   thing being counted (rows, bytes, errors).
//! - histograms describe a distribution (durations, sizes).
//! - gauges describe a current value (in-flight, cache occupancy).
//!
//! # Attribute cardinality
//!
//! Every metric is documented with the *bounded* attribute set it may carry.
//! NEVER attach unbounded values (raw error messages, partition keys, full
//! queries) as attributes — those explode cardinality and cost. Bounded
//! attribute keys live in [`attr`].

/// Recommended UCUM units for the catalog metrics.
///
/// Use these constants when constructing instruments so the unit string stays
/// consistent with the metric definitions below.
pub mod unit {
    /// Dimensionless count / ratio.
    pub const DIMENSIONLESS: &str = "1";
    /// A count of rows (UCUM annotation).
    pub const ROWS: &str = "{row}";
    /// A count of partitions.
    pub const PARTITIONS: &str = "{partition}";
    /// A count of SSTables.
    pub const SSTABLES: &str = "{sstable}";
    /// A count of errors.
    pub const ERRORS: &str = "{error}";
    /// Bytes.
    pub const BYTES: &str = "By";
    /// Seconds (OTel prefers base-unit seconds for durations).
    pub const SECONDS: &str = "s";
}

/// Bounded attribute keys for catalog metrics.
///
/// These are the ONLY attribute keys downstream code should attach to the
/// catalog metrics. Each is documented with its allowed value space so the
/// cardinality stays bounded.
pub mod attr {
    /// Low-cardinality error category. Values come from
    /// [`crate::observability::ErrorCategory::as_str`] (≈10 distinct values).
    pub const ERROR_CATEGORY: &str = "cqlite.error.category";
    /// Subsystem that produced an event, e.g. `"reader"`, `"query"`,
    /// `"compaction"`. Callers pass a `&'static str`, so the value space is
    /// bounded by the code itself.
    pub const SUBSYSTEM: &str = "cqlite.subsystem";
    /// SSTable on-disk format family, e.g. `"big"` or `"bti"`. Bounded.
    pub const SSTABLE_FORMAT: &str = "cqlite.sstable.format";
    /// Compression algorithm, e.g. `"lz4"`, `"snappy"`, `"none"`. Bounded.
    pub const COMPRESSION: &str = "cqlite.compression";
    /// Outcome of a lookup/check, exactly `"hit"` or `"miss"`. Bounded to two
    /// values; used by partition-lookup and bloom-check counters so a single
    /// metric carries both arms for ratio dashboards.
    pub const RESULT: &str = "cqlite.result";
    /// Read-path access route for a partition lookup, e.g. `"index"` (BIG
    /// Index.db) or `"bti_trie"` (BTI Partitions.db). Bounded by the code.
    /// Distinct from [`ACCESS_PATH`], which is the query-engine SELECT access
    /// path (#1035); this is the storage-layer lookup route (#1034).
    pub const LOOKUP_ROUTE: &str = "cqlite.read.lookup_route";
    /// Access path a `SELECT` chose for its SSTable-scan step (issue #1035).
    ///
    /// Values come from [`crate::query::access_path::AccessPath::label`] — a
    /// closed set such as `"full_scan"`, `"partition_lookup"`,
    /// `"multi_partition_lookup"`, `"clustering_slice"`,
    /// `"fallback_full_scan"`. Bounded by the `AccessPath` enum itself, so it is
    /// safe as a metric dimension and span attribute. NEVER carries key values.
    pub const ACCESS_PATH: &str = "cqlite.query.access_path";
    /// Query plan family chosen by the planner / executor, e.g. `"table_scan"`,
    /// `"point_lookup"`, `"index_scan"`, `"range_scan"`, `"aggregation"`
    /// (issue #1035). Bounded by the executor's plan-type taxonomy.
    pub const PLAN_TYPE: &str = "cqlite.query.plan_type";
    /// Arrow Flight RPC method name (issue #1041), e.g. `"do_get"`,
    /// `"get_flight_info"`, `"get_schema"`, `"handshake"`. Bounded by the
    /// `FlightService` trait's fixed set of methods — never a request payload.
    pub const RPC_METHOD: &str = "cqlite.rpc.method";
    /// Arrow Flight RPC outcome (issue #1041), exactly `"ok"` or `"error"`.
    /// Bounded to two values so a single metric series carries both arms for
    /// success/error-rate dashboards.
    pub const RPC_STATUS: &str = "cqlite.rpc.status";
}

/// `cqlite.read.rows` — counter `{row}`.
///
/// Total rows materialised by the read path. Bounded attributes:
/// [`attr::SSTABLE_FORMAT`].
pub const READ_ROWS: &str = "cqlite.read.rows";

/// `cqlite.read.bytes` — counter `By`.
///
/// Total bytes read from Data.db (post-decompression). Bounded attributes:
/// [`attr::SSTABLE_FORMAT`], [`attr::COMPRESSION`].
pub const READ_BYTES: &str = "cqlite.read.bytes";

/// `cqlite.read.partitions` — counter `{partition}`.
///
/// Total partitions scanned. Bounded attributes: [`attr::SSTABLE_FORMAT`].
pub const READ_PARTITIONS: &str = "cqlite.read.partitions";

/// `cqlite.read.duration` — histogram `s`.
///
/// Distribution of single read/scan operation durations in seconds. Bounded
/// attributes: [`attr::SSTABLE_FORMAT`].
pub const READ_DURATION: &str = "cqlite.read.duration";

/// `cqlite.storage.open.sstables` — counter `{sstable}`.
///
/// SSTables discovered and opened by a single [`StorageEngine`] open, summed
/// across opens over the process lifetime. No high-cardinality attributes.
pub const STORAGE_OPEN_SSTABLES: &str = "cqlite.storage.open.sstables";

/// `cqlite.storage.open.bytes` — counter `By`.
///
/// Total on-disk Data.db bytes across the SSTables discovered by a
/// [`StorageEngine`] open. No high-cardinality attributes.
pub const STORAGE_OPEN_BYTES: &str = "cqlite.storage.open.bytes";

/// `cqlite.storage.open.tables` — counter `1`.
///
/// Total logical tables represented by the SSTables discovered at
/// [`StorageEngine`] open. No high-cardinality attributes.
pub const STORAGE_OPEN_TABLES: &str = "cqlite.storage.open.tables";

/// `cqlite.read.partition_lookup.total` — counter `1`.
///
/// Total partition point lookups attempted on the read path, one increment per
/// lookup. Bounded attributes: [`attr::RESULT`] (`hit`/`miss`),
/// [`attr::ACCESS_PATH`] (`index`/`bti_trie`), and [`attr::SSTABLE_FORMAT`].
/// Carrying `result` as an attribute (instead of separate metric names) lets a
/// dashboard compute hit ratio from one series.
pub const READ_PARTITION_LOOKUP: &str = "cqlite.read.partition_lookup.total";

/// `cqlite.read.bloom.checks` — counter `1`.
///
/// Total bloom-filter / BTI-trie present-or-absent checks on the read path, one
/// increment per check. Bounded attributes: [`attr::RESULT`] (`hit` = maybe
/// present, `miss` = definitely absent) and [`attr::SSTABLE_FORMAT`]. The
/// miss-rate of this metric is the pruning effectiveness; pairing it with
/// [`READ_PARTITION_LOOKUP`] reveals the bloom false-positive rate.
pub const READ_BLOOM_CHECKS: &str = "cqlite.read.bloom.checks";

/// `cqlite.read.scan.window_refill` — counter `1`.
///
/// Incremented once each time the user-facing windowed streaming scan
/// (issue #1143, `run_scan_stream_windowed`) stops at `ParseStep::NeedMore`
/// because the trailing partition straddles a compression-chunk boundary and
/// the driver must await the next decompressed chunk before re-parsing. A
/// non-zero value proves the sliding-window stitch boundary path was actually
/// exercised (a multi-chunk SSTable with a straddling partition); it stays
/// zero for single-chunk SSTables. No high-cardinality attributes.
pub const READ_SCAN_WINDOW_REFILL: &str = "cqlite.read.scan.window_refill";

/// `cqlite.query.duration` — histogram `s`.
///
/// Distribution of end-to-end query execution durations in seconds. Bounded
/// attributes: [`attr::SUBSYSTEM`]. NEVER attach the query text.
pub const QUERY_DURATION: &str = "cqlite.query.duration";

/// `cqlite.query.rows` — counter `{row}`.
///
/// Total rows returned to callers by the query engine. Bounded attributes:
/// [`attr::ACCESS_PATH`], [`attr::PLAN_TYPE`]. No high-cardinality attributes.
pub const QUERY_ROWS: &str = "cqlite.query.rows";

/// `cqlite.query.rows_scanned` — counter `{row}`.
///
/// Total rows materialised/examined by the SELECT scan step before predicate
/// filtering, projection, and `LIMIT` (issue #1035). The gap between this and
/// [`QUERY_ROWS`] is the read-amplification of a query — large for a
/// `full_scan`, ~1 for a `partition_lookup`. Bounded attributes:
/// [`attr::ACCESS_PATH`]. Emitted by the modern `SelectExecutor` only.
pub const QUERY_ROWS_SCANNED: &str = "cqlite.query.rows_scanned";

/// `cqlite.sstables.open` — gauge `{sstable}`.
///
/// Number of SSTables currently held open. Bounded attributes:
/// [`attr::SSTABLE_FORMAT`].
pub const SSTABLES_OPEN: &str = "cqlite.sstables.open";

/// `cqlite.compaction.duration` — histogram `s`.
///
/// Distribution of compaction run durations in seconds. No high-cardinality
/// attributes.
pub const COMPACTION_DURATION: &str = "cqlite.compaction.duration";

// ---------------------------------------------------------------------------
// Write path (issue #1036) — emitted from the `write-support` write engine.
// ---------------------------------------------------------------------------

/// `cqlite.write.mutations` — counter `{row}`.
///
/// Total mutations accepted by the write path (one per `write`/`write_async`
/// call that successfully inserts into the memtable). No high-cardinality
/// attributes.
pub const WRITE_MUTATIONS: &str = "cqlite.write.mutations";

/// `cqlite.memtable.size_bytes` — gauge `By`.
///
/// Current approximate in-memory size of the active memtable in bytes. No
/// high-cardinality attributes.
pub const MEMTABLE_SIZE_BYTES: &str = "cqlite.memtable.size_bytes";

/// `cqlite.memtable.rows` — gauge `{row}`.
///
/// Current number of buffered rows in the active memtable. No high-cardinality
/// attributes.
pub const MEMTABLE_ROWS: &str = "cqlite.memtable.rows";

/// `cqlite.wal.sync.duration` — histogram `s`.
///
/// Distribution of WAL `fsync` durations in seconds. No high-cardinality
/// attributes.
pub const WAL_SYNC_DURATION: &str = "cqlite.wal.sync.duration";

/// `cqlite.flush.duration` — histogram `s`.
///
/// Distribution of memtable→SSTable flush durations in seconds. No
/// high-cardinality attributes.
pub const FLUSH_DURATION: &str = "cqlite.flush.duration";

/// `cqlite.flush.rows` — counter `{row}`.
///
/// Total rows flushed from the memtable to L0 SSTables. No high-cardinality
/// attributes.
pub const FLUSH_ROWS: &str = "cqlite.flush.rows";

/// `cqlite.flush.bytes` — counter `By`.
///
/// Total Data.db bytes produced by memtable flushes. No high-cardinality
/// attributes.
pub const FLUSH_BYTES: &str = "cqlite.flush.bytes";

/// `cqlite.flush.sstables` — counter `{sstable}`.
///
/// Total L0 SSTables created by memtable flushes. No high-cardinality
/// attributes.
pub const FLUSH_SSTABLES: &str = "cqlite.flush.sstables";

/// `cqlite.write.partitions` — counter `{partition}`.
///
/// Total partitions written by the SSTable writer (flush + compaction output).
/// No high-cardinality attributes.
pub const WRITE_PARTITIONS: &str = "cqlite.write.partitions";

/// `cqlite.write.bytes` — counter `By`.
///
/// Total Data.db bytes produced by the SSTable writer across all output
/// components' Data.db. No high-cardinality attributes.
pub const WRITE_BYTES: &str = "cqlite.write.bytes";

/// `cqlite.compression.ratio` — histogram `1`.
///
/// Per-chunk compression ratio (compressed bytes / uncompressed bytes; ≤1.0
/// means the chunk shrank). Bounded attributes: [`attr::COMPRESSION`].
pub const COMPRESSION_RATIO: &str = "cqlite.compression.ratio";

// ---------------------------------------------------------------------------
// Compaction & maintenance (issue #1037) — emitted from the write engine's
// STCS compaction/k-way-merge path (write-support).
// ---------------------------------------------------------------------------

/// `cqlite.compaction.rows_merged` — counter `{row}`.
///
/// Total rows emitted by the k-way merge across all compactions. Combined with
/// [`COMPACTION_DURATION`] this yields rows-merged-per-second throughput. No
/// high-cardinality attributes.
pub const COMPACTION_ROWS_MERGED: &str = "cqlite.compaction.rows_merged";

/// `cqlite.compaction.bytes_written` — counter `By`.
///
/// Total bytes written to compaction output SSTables (all components). No
/// high-cardinality attributes.
pub const COMPACTION_BYTES_WRITTEN: &str = "cqlite.compaction.bytes_written";

/// `cqlite.compaction.sstables_in` — counter `{sstable}`.
///
/// Total input SSTables consumed by compactions. No high-cardinality
/// attributes.
pub const COMPACTION_SSTABLES_IN: &str = "cqlite.compaction.sstables_in";

/// `cqlite.compaction.sstables_out` — counter `{sstable}`.
///
/// Total output SSTables produced by compactions. No high-cardinality
/// attributes.
pub const COMPACTION_SSTABLES_OUT: &str = "cqlite.compaction.sstables_out";

/// `cqlite.compaction.tombstones_purged` — counter `{tombstone}`.
///
/// Total tombstones GENUINELY PURGED (gc_grace / overlap-safe) during compaction,
/// summed across cell tombstones, whole-row tombstones, range-tombstone markers,
/// and complex-deletion (collection/UDT) markers. Counted only at the actual
/// purge decision points in the merge/reconcile logic; ordinary last-write-wins
/// reconciliation collapse is NOT counted. No high-cardinality attributes.
pub const COMPACTION_TOMBSTONES_PURGED: &str = "cqlite.compaction.tombstones_purged";

/// `cqlite.compaction.lag` — gauge `{sstable}`.
///
/// Current L0 SSTables pending compaction (compaction lag). No high-cardinality
/// attributes.
pub const COMPACTION_LAG: &str = "cqlite.compaction.lag";

/// `cqlite.compaction.finalize.duration` — histogram `s`.
///
/// Distribution of compaction finalize (atomic rename / publication-barrier)
/// durations in seconds. No high-cardinality attributes.
pub const COMPACTION_FINALIZE_DURATION: &str = "cqlite.compaction.finalize.duration";

/// `cqlite.compaction.budget.requested` — histogram `s`.
///
/// Distribution of maintenance budget requested per `maintenance_step` call, in
/// seconds. No high-cardinality attributes.
pub const COMPACTION_BUDGET_REQUESTED: &str = "cqlite.compaction.budget.requested";

/// `cqlite.compaction.budget.consumed` — histogram `s`.
///
/// Distribution of maintenance budget actually consumed per `maintenance_step`
/// call, in seconds (compare against [`COMPACTION_BUDGET_REQUESTED`] to track
/// the ~10% tolerance honored by the scheduler). No high-cardinality
/// attributes.
pub const COMPACTION_BUDGET_CONSUMED: &str = "cqlite.compaction.budget.consumed";

/// `cqlite.errors.total` — counter `{error}`.
///
/// Total errors observed, the canonical error-rate signal (issue #1038).
/// Bounded attributes: [`attr::ERROR_CATEGORY`] and [`attr::SUBSYSTEM`] ONLY.
/// The raw error message is never attached.
pub const ERRORS_TOTAL: &str = "cqlite.errors.total";

// ---------------------------------------------------------------------------
// Arrow Flight gRPC service (issue #1041) — emitted from `cqlite-flight`.
// ---------------------------------------------------------------------------

/// `cqlite.rpc.requests` — counter `1`.
///
/// Total Arrow Flight RPC requests served, one increment per completed RPC.
/// Bounded attributes: [`attr::RPC_METHOD`] (fixed `FlightService` method set)
/// and [`attr::RPC_STATUS`] (`ok`/`error`) so a dashboard computes per-method
/// error rate from one series. NEVER carries request payloads or ticket data.
pub const RPC_REQUESTS: &str = "cqlite.rpc.requests";

/// `cqlite.rpc.duration` — histogram `s`.
///
/// Distribution of Arrow Flight RPC handler durations in seconds (handler entry
/// to response/stream construction). Bounded attributes: [`attr::RPC_METHOD`],
/// [`attr::RPC_STATUS`].
pub const RPC_DURATION: &str = "cqlite.rpc.duration";

/// `cqlite.rpc.in_flight` — gauge `1`.
///
/// Number of Arrow Flight RPCs currently being handled (incremented on entry,
/// decremented on completion). Bounded attributes: [`attr::RPC_METHOD`].
pub const RPC_IN_FLIGHT: &str = "cqlite.rpc.in_flight";

/// `cqlite.rpc.rows` — counter `{row}`.
///
/// Total rows returned to clients by `do_get` (summed across emitted record
/// batches). Bounded attributes: [`attr::RPC_METHOD`].
pub const RPC_ROWS: &str = "cqlite.rpc.rows";

/// `cqlite.rpc.bytes` — counter `By`.
///
/// Total record-batch payload bytes streamed to clients by `do_get` (in-memory
/// Arrow batch size, pre-IPC-framing). Bounded attributes: [`attr::RPC_METHOD`].
pub const RPC_BYTES: &str = "cqlite.rpc.bytes";

/// All catalog metric names, for tests and registration sanity checks.
pub const ALL_METRICS: &[&str] = &[
    READ_ROWS,
    READ_BYTES,
    READ_PARTITIONS,
    READ_DURATION,
    READ_PARTITION_LOOKUP,
    READ_BLOOM_CHECKS,
    STORAGE_OPEN_SSTABLES,
    STORAGE_OPEN_BYTES,
    STORAGE_OPEN_TABLES,
    QUERY_DURATION,
    QUERY_ROWS,
    QUERY_ROWS_SCANNED,
    SSTABLES_OPEN,
    COMPACTION_DURATION,
    ERRORS_TOTAL,
    // Write path (#1036)
    WRITE_MUTATIONS,
    MEMTABLE_SIZE_BYTES,
    MEMTABLE_ROWS,
    WAL_SYNC_DURATION,
    FLUSH_DURATION,
    FLUSH_ROWS,
    FLUSH_BYTES,
    FLUSH_SSTABLES,
    WRITE_PARTITIONS,
    WRITE_BYTES,
    COMPRESSION_RATIO,
    // Compaction & maintenance (#1037)
    COMPACTION_ROWS_MERGED,
    COMPACTION_BYTES_WRITTEN,
    COMPACTION_SSTABLES_IN,
    COMPACTION_SSTABLES_OUT,
    COMPACTION_TOMBSTONES_PURGED,
    COMPACTION_LAG,
    COMPACTION_FINALIZE_DURATION,
    COMPACTION_BUDGET_REQUESTED,
    COMPACTION_BUDGET_CONSUMED,
    // Arrow Flight gRPC service (#1041)
    RPC_REQUESTS,
    RPC_DURATION,
    RPC_IN_FLIGHT,
    RPC_ROWS,
    RPC_BYTES,
];

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn metric_names_are_namespaced_and_unique() {
        let mut seen = std::collections::HashSet::new();
        for name in ALL_METRICS {
            assert!(
                name.starts_with("cqlite."),
                "metric {name} must be rooted under cqlite."
            );
            assert!(seen.insert(*name), "duplicate metric name {name}");
        }
        assert_eq!(seen.len(), ALL_METRICS.len());
    }

    #[test]
    fn attribute_keys_are_namespaced() {
        for key in [
            attr::ERROR_CATEGORY,
            attr::SUBSYSTEM,
            attr::SSTABLE_FORMAT,
            attr::COMPRESSION,
            attr::RESULT,
            attr::LOOKUP_ROUTE,
            attr::ACCESS_PATH,
            attr::PLAN_TYPE,
            attr::RPC_METHOD,
            attr::RPC_STATUS,
        ] {
            assert!(key.starts_with("cqlite."), "attr {key} must be namespaced");
        }
    }
}