pub struct SSTableReader {
pub generation: u64,
pub compression_info: Option<Arc<CompressionInfo>>,
/* private fields */
}Expand description
SSTable reader for efficient data access
Fields§
§generation: u64SSTable generation number (for multi-generation merging)
compression_info: Option<Arc<CompressionInfo>>CompressionInfo metadata for chunked decompression (if compressed)
Implementations§
Source§impl SSTableReader
impl SSTableReader
Sourcepub fn scan_for_key_call_count() -> u64
pub fn scan_for_key_call_count() -> u64
Current value of the test-only scan_for_key invocation counter.
Issue #831: tests use this to assert that a BTI get() resolves entirely
through the Partitions.db trie and never falls through to the sequential
scan. See [SCAN_FOR_KEY_CALLS].
Source§impl SSTableReader
impl SSTableReader
Sourcepub async fn iterate_all_partitions_for_compaction(
&self,
schema: Option<&TableSchema>,
) -> Result<Vec<CompactionRow>>
pub async fn iterate_all_partitions_for_compaction( &self, schema: Option<&TableSchema>, ) -> Result<Vec<CompactionRow>>
Iterate all partitions with per-row timestamps, for use by the compaction merger.
Returns (RowKey, ScanRow, row_timestamp_micros) for every row in the SSTable.
Unlike iterate_all_partitions:
- Row tombstones are returned as
Value::Tombstone(RowTombstone)carrying the actual deletion timestamp extracted from the on-disk row header. - Cell tombstones within live rows are stored as
Value::Tombstone(CellTombstone)inside theValue::Map, also carrying the actual cell-level deletion timestamp. - The third tuple element is the decoded row-level write timestamp, so the merger can perform timestamp-accurate last-write-wins comparisons.
Normal user-facing reads use scan / get / iterate_all_partitions,
which apply tombstone filtering. Do NOT use this method for user-visible queries.
(Issue #505)
Sourcepub async fn distinct_partition_count(&self) -> Result<usize>
pub async fn distinct_partition_count(&self) -> Result<usize>
Count the distinct PARTITION keys decoded from Data.db (issue #970).
This is a partition-granular count — one per partition, never per row —
used by the SSTable verifier’s BTI cross-check (verify.rs). It must NOT
be confused with get_all_entries, whose RowKeys carry
clustering/column/static suffixes and therefore over-count a multi-row
partition as many distinct keys.
Both supported Data.db layouts surface the partition key (without any clustering suffix) via the compaction read path:
- BIG (
nb,V5CompressedLegacy+is_nb_format): [iterate_all_partitions_for_compaction] emits oneCompactionRowper row, each carrying the partition key (CompactionRow::key). Distinct keys == partition count. - BTI (
da): the compaction iterator’s non-stitching fallback would route throughiterate_all_partitions, whose keys are row-granular. Instead we stitch the data section and run the compaction parser directly, which emits the same partition-key-onlyCompactionRow::key.
No schema is required from the caller: the parser resolves it via the
reader’s header/registry (get_table_schema).
Sourcepub async fn distinct_partition_keys(&self) -> Result<Vec<Vec<u8>>>
pub async fn distinct_partition_keys(&self) -> Result<Vec<Vec<u8>>>
Return the set of distinct raw partition keys decoded from Data.db,
one entry per partition (NOT per row), in first-seen order.
Each key is the raw serialized partition key as stored on disk (e.g. the
4-byte big-endian value for pk int, 16 bytes for a UUID) — the same form
accepted by
encode_partition_key_for_bti_trie.
This is used by the verifier’s BTI Partitions.db cross-check to compare
partition-key IDENTITY (issue #1103), not just partition count.
The stitch/parse strategy mirrors Self::distinct_partition_count: we
route through stitch_all_chunks (not the generic
iterate_all_partitions_for_compaction) for BOTH BIG and BTI so the
incompressible/raw-chunk fallback is honoured (issue #970), and parse with
the compaction parser, which emits one CompactionRow per partition with
the partition key in key (partition-granular). No schema is required
from the caller: the parser resolves it via the reader’s header/registry.
Sourcepub async fn distinct_partition_keys_with_positions(
&self,
) -> Result<Vec<(u64, Vec<u8>)>>
pub async fn distinct_partition_keys_with_positions( &self, ) -> Result<Vec<(u64, Vec<u8>)>>
Return the distinct raw partition keys decoded from Data.db together with
the byte offset at which each partition begins in the DECOMPRESSED data
section (issue #1103).
Each tuple is (data_position, raw_partition_key), where data_position
is exactly the value a BTI Partitions.db leaf encodes as
BtiPartitionLocation::DataOffset
(and the data_position recovered from a RowsOffset entry via
resolve_rows_db_entry).
The verifier’s BTI cross-check resolves each leaf PAYLOAD back to its raw
partition key through this map so it catches a corruption that keeps the
emitted trie prefix but rewrites the payload to point at a DIFFERENT
partition (a same-count wrong-IDENTITY corruption the prefix-only compare
missed).
The stitch/parse strategy mirrors Self::distinct_partition_keys; only
the parser entry point differs (it threads the partition-start offset).
Sourcepub async fn partition_verify_scan(&self) -> Result<Vec<(Vec<u8>, Option<i32>)>>
pub async fn partition_verify_scan(&self) -> Result<Vec<(Vec<u8>, Option<i32>)>>
Verifier-facing scan (issue #1282): return, in on-disk order, every
distinct partition’s raw key together with its raw partition-level
localDeletionTime (when the partition carries a tombstone).
Each element is (raw_partition_key, partition_local_deletion_time) where
the LDT is Some(i32) only for a DELETED partition (a live partition
carries the DeletionTime.LIVE sentinel and yields None). The i32 is
exactly the value parse_partition_header_full decodes: for the legacy
signed nb form it is the genuine signed i32 BE; for the unsigned
oa/da form it is the wrapping as u32 as i32 representation of the
on-disk u32 (far-future values in [2^31, 2^32) therefore appear
negative and are LEGITIMATE — the caller must consult
SSTableReader::has_uint_deletion_time before interpreting a negative
value as corrupt).
The ordering is the on-disk partition order, so the verifier can assert ascending Murmur3 token order (Cassandra stores partitions in token order) without a separate scan.
Sourcepub async fn partition_clustering_verify_scan(
&self,
) -> Result<Vec<(usize, Vec<Vec<Value>>)>>
pub async fn partition_clustering_verify_scan( &self, ) -> Result<Vec<(usize, Vec<Vec<Value>>)>>
Verifier-facing scan (issue #1282, roborev follow-up): return, in on-disk order, every partition’s ordered list of decoded CLUSTERING-key tuples.
Each element is (partition_index, Vec<Vec<Value>>), where the inner
Vec<Value> is one clustering row’s values in schema clustering order, and
the outer Vec holds those rows in the exact ORDER they appear on disk. The
verifier compares consecutive tuples within a partition using the
authoritative per-column crate::types::comparator::ComparatorType plus
each clustering column’s ASC/DESC order, and flags a non-increasing step as
crate::storage::sstable::verify::VerifyErrorClass::OutOfOrderKeyOrRow —
the “row” half of that class (Cassandra’s Verifier rejects out-of-order
clustering rows too).
Rows carrying no clustering prefix (a static row, a partition-level tombstone carrier, or an unclustered table’s single row) are OMITTED from the per-partition list: they do not participate in clustering order.
Reuses the SAME on-disk-order emit-with-offset parser as
Self::partition_verify_scan, so the clustering values come from the
authoritative schema-aware decode, not a heuristic. Uses only non-gated CQL
comparators (no write-support dependency). When the table has no clustering
columns the returned list is empty (nothing to order).
Sourcepub async fn stream_all_partitions_for_compaction<F>(
&self,
schema: Option<&TableSchema>,
scan_cancel: &ScanCancel,
emit: F,
) -> Result<()>
pub async fn stream_all_partitions_for_compaction<F>( &self, schema: Option<&TableSchema>, scan_cancel: &ScanCancel, emit: F, ) -> Result<()>
Streaming compaction read (issue #827): yield (RowKey, ScanRow, ts)
entries via emit one partition at a time, so peak memory is bounded by
max_partition_size + one_chunk rather than by the total input size.
This is the incremental counterpart of
[iterate_all_partitions_for_compaction], which fully materialises the
decompressed data section and parses every entry into a Vec before
returning. The k-way merge producer (merge::producer_thread) uses this
to forward entries into its bounded channel directly, so a source’s
decompressed content is never fully resident.
§Sliding-window driver
The V5CompressedLegacy chunk-stitching path keeps a sliding
WindowCursor of decompressed
bytes. After refilling with each decompressed chunk it drains confirmed
partitions via parse_one_partition_with_timestamps, advancing the cursor
over the front after every Emitted (the reclaimed prefix is compacted once
per refill, not memmoved per partition — issue #1589), and stopping at
NeedMore to await the next chunk (a partition can straddle a chunk
boundary). At EOF a final drain pass runs with
at_final_chunk = true so the trailing (possibly truncated) partition is
terminal rather than requesting a refill that will never come.
Returning ControlFlow::Break from emit stops the scan early
(consumer dropped). Tombstone / timestamp semantics are byte-identical to
the Vec variant (Issue #505/#533).
scan_cancel is an explicit PER-CALL cancellation token (issue #2346),
not the reader’s own SSTableReader::scan_cancel field: a cached/shared
Arc<SSTableReader> (a future warm-handle registry) may drive two
concurrent calls to this method with two INDEPENDENT tokens, so
cancellation cannot live as mutable per-reader state (set_scan_cancel
requires &mut self, uncallable through a shared Arc). Callers that
want the reader’s own field’s semantics (the pre-#2346 default) pass
&self.scan_cancel explicitly — see
SSTableReader::iterate_all_partitions_cancellable for the analogous
non-compaction seam.
Source§impl SSTableReader
impl SSTableReader
Sourcepub async fn stream_all_partitions_for_query<F>(
&self,
schema: Option<&TableSchema>,
scan_cancel: &ScanCancel,
token_bound: Option<ScanTokenBound>,
emit: F,
) -> Result<()>
pub async fn stream_all_partitions_for_query<F>( &self, schema: Option<&TableSchema>, scan_cancel: &ScanCancel, token_bound: Option<ScanTokenBound>, emit: F, ) -> Result<()>
Query-serve partition enumeration for the WARM reader-based merge (issue
#2412 §C / #2413 Option A) — the analogue of
stream_all_partitions_for_compaction
that the flight do_get warm path drives (from_readers::drive_query_stream).
Routing MIRRORS stream_all_partitions_for_compaction’s per-mechanism emit
shape so the merger reconciles identically, but STREAMS the index (no
resident Vec, spec R4) and pushes the token range into the walk (#2413):
- Chunk-stitching (
nb) readers use the full-fidelity CompactionRow decoder (stream_partitions_summary_guided_compaction) — byte-identical todrain_compaction_window’s emit, so cross-generation LWW reconciliation is preserved. - Non-stitching (
V5_0Uncompressed) readers use the read-shadowing ScanRow decoder (stream_partitions_summary_guided) — byte-identical tostream_all_partitions_cancellable’s emit.
A FellBack (no usable summary/index) routes on to the full-ring
stream_all_partitions_for_compaction. Compaction consumers do NOT call
this (they use the path-based stream directly, no token range), so they keep
full-ring byte-parity walks unchanged (spec R3).
Source§impl SSTableReader
impl SSTableReader
Sourcepub async fn read_single_partition_for_compaction(
&self,
partition_key: &[u8],
schema: Option<&TableSchema>,
scan_cancel: &ScanCancel,
) -> Result<SinglePartitionCompaction>
pub async fn read_single_partition_for_compaction( &self, partition_key: &[u8], schema: Option<&TableSchema>, scan_cancel: &ScanCancel, ) -> Result<SinglePartitionCompaction>
Probe one SSTable for a single partition, returning its compaction rows via an authoritative seek — or a prune / scan-fallback signal (issue #2207).
This is the public core primitive the Flight point-read path composes into its existing k-way merge. Correctness spine (fail-open toward reading):
might_contain_partitionreports a definite negative →SinglePartitionCompaction::DefinitelyAbsent(pruned + counted).- No random-access index →
SinglePartitionCompaction::IndexUnavailable(the caller scans this SSTable). - Otherwise resolve the partition’s uncompressed offset (BTI trie / BIG
Index.db) and its authoritative end bound (successor offset / data
length), materialize ONLY the covering chunk window, and parse the one
partition with the SAME compaction parser the full scan uses →
SinglePartitionCompaction::Rows. A BIG Index.db miss (inconclusive, #1572) or an un-boundable last partition degrades toSinglePartitionCompaction::IndexUnavailable.
partition_key is the raw partition-key bytes (as
PartitionKey::to_bytes produces). schema is the authoritative table
schema (the parser needs column names).
scan_cancel is an explicit PER-CALL cancellation token (issue #2346),
mirroring SSTableReader::stream_all_partitions_for_compaction — not
the reader’s own scan_cancel field, so a shared/cached Arc<SSTableReader>
can serve two concurrent point-read probes with independent cancellation.
Source§impl SSTableReader
impl SSTableReader
Sourcepub async fn verify_presence_oracle_negative(
&self,
table_id: &TableId,
partition_key: &[u8],
) -> Result<bool>
pub async fn verify_presence_oracle_negative( &self, table_id: &TableId, partition_key: &[u8], ) -> Result<bool>
Authoritatively verify a presence-oracle “definitely absent” verdict for
partition_key in THIS SSTable (issue #2163, opt-in / default-off).
When the presence_verification switch is OFF (the default) this returns
Ok(false) WITHOUT scanning — zero cost. When ON, it runs an AUTHORITATIVE
sequential scan of this SSTable’s own Data.db for the key (never a heuristic
inference from byte patterns — the no-heuristics mandate). If the scan FINDS
the key, the oracle produced a false negative:
cqlite.read.bloom.false_negatives increments once with this SSTable’s
bounded cqlite.sstable.format, a warning is logged, and Ok(true) is
returned. A genuine absence returns Ok(false) and never emits. Under a
correct bloom/BTI-trie the counter stays 0.
Source§impl SSTableReader
impl SSTableReader
Sourcepub fn partition_key_out_of_range(&self, key: &[u8]) -> bool
pub fn partition_key_out_of_range(&self, key: &[u8]) -> bool
Return true iff key provably sorts OUTSIDE this SSTable’s authoritative
[first_key, last_key] partition-key bound, so the partition is definitely
absent and the caller may skip all presence work.
Returns false (cannot rule out) when no authoritative bound is available
(Summary.db absent, e.g. a BTI reader) or an endpoint is empty. See the
module docs for the no-false-miss contract: the comparison is in Cassandra
token order (Murmur3 token, unsigned-byte tiebreak), inclusive at both ends.
Precondition: the SSTable is sorted by Cassandra’s Murmur3Partitioner (the
only partitioner CQLite targets — Cassandra 5.0). See the module docs.
Source§impl SSTableReader
impl SSTableReader
Sourcepub async fn scan(
&self,
table_id: &TableId,
start_key: Option<&RowKey>,
end_key: Option<&RowKey>,
limit: Option<usize>,
schema: Option<&TableSchema>,
) -> Result<Vec<(RowKey, ScanRow)>>
pub async fn scan( &self, table_id: &TableId, start_key: Option<&RowKey>, end_key: Option<&RowKey>, limit: Option<usize>, schema: Option<&TableSchema>, ) -> Result<Vec<(RowKey, ScanRow)>>
Scan a range of keys
§Arguments
table_id- The table to scanstart_key- Optional start key for range scanend_key- Optional end key for range scanlimit- Optional limit on number of resultsschema- Optional table schema for schema-aware parsing. When provided, enables accurate type detection and avoids heuristic-based parsing. Strongly recommended for Cassandra 5.0+ formats.
Sourcepub async fn get_all_entries(&self) -> Result<Vec<(TableId, RowKey, ScanRow)>>
pub async fn get_all_entries(&self) -> Result<Vec<(TableId, RowKey, ScanRow)>>
Get all entries in the SSTable.
§Tombstone contract (Issue #505)
This is a user-facing accessor: row tombstones are filtered out via
Self::filter_tombstone and never appear in the returned entries. The
underlying parse_block path emits Value::Tombstone(RowTombstone) for
deleted rows, but those are suppressed here so callers see exactly the live
rows (matching the previous Value::Null suppression behaviour).
The compaction k-way merger must instead use
Self::iterate_all_partitions_for_compaction, which preserves
Value::Tombstone entries (with their authoritative deletion timestamps)
so that tombstone-shadowing semantics can be applied during the merge.
Sourcepub fn scan_stream(
self: Arc<Self>,
table_id: TableId,
start_key: Option<RowKey>,
end_key: Option<RowKey>,
schema: Option<TableSchema>,
buffer_size: usize,
) -> Receiver<Result<(RowKey, ScanRow)>>
pub fn scan_stream( self: Arc<Self>, table_id: TableId, start_key: Option<RowKey>, end_key: Option<RowKey>, schema: Option<TableSchema>, buffer_size: usize, ) -> Receiver<Result<(RowKey, ScanRow)>>
Streaming scan (issue #790): yield (RowKey, ScanRow) entries lazily
through a bounded channel instead of materializing the whole result in a
Vec. Live heap is bounded by buffer_size rows (plus the stitched
data-section buffer) rather than growing O(rows).
Entries are yielded in on-disk order — token order for a single SSTable —
matching the order of the materializing scan path. The
bounded channel applies backpressure: parsing pauses when the consumer
falls behind and stops entirely if the consumer is dropped.
In-flight bound (chunk-stitching SSTables): the windowed pipeline (issue
#1143) materializes one confirmed partition at a time and batches its rows to
amortize the cross-thread wake, so against a stalled consumer the resident
(RowKey, ScanRow) count is the SUM of three inherent terms, not one constant:
buffer_size (this channel) + max_partition_size (the one fully-materialized
confirmed partition — a pre-existing #1156 windowed-scan term, inherent to any
row-materializing partition scan) +
MAX_INFLIGHT_BATCH_ROWS
(the FIXED, BOUNDED amount the issue-#1143 batching subsystem may run ahead,
regardless of buffer_size, holding even for buffer_size == 1).
MAX_INFLIGHT_BATCH_ROWS bounds the batching subsystem alone, NOT the
max_partition_size materialization term. Non-stitching SSTables parse a
whole block before forwarding its rows, so the resident (RowKey, ScanRow)
count is bounded by buffer_size + (one parsed block's entries).
Sourcepub fn scan_stream_batched(
self: Arc<Self>,
table_id: TableId,
start_key: Option<RowKey>,
end_key: Option<RowKey>,
schema: Option<TableSchema>,
buffer_size: usize,
) -> Receiver<Result<Vec<(RowKey, ScanRow)>>>
pub fn scan_stream_batched( self: Arc<Self>, table_id: TableId, start_key: Option<RowKey>, end_key: Option<RowKey>, schema: Option<TableSchema>, buffer_size: usize, ) -> Receiver<Result<Vec<(RowKey, ScanRow)>>>
Batched streaming scan (issue #1592, Epic F/F2): the additive companion to
scan_stream whose channel item is a Vec BATCH of
(RowKey, ScanRow) entries rather than a single entry. Forwarding one batch
per channel send collapses the one-async-wake-per-row cost the internal
windowed pipeline (issue #1143) was designed to amortize but the public
forwarder then re-flattened away.
Order and content are identical to scan_stream:
flattening the batches yields exactly the per-row stream. Backpressure is
preserved — the channel is bounded (in BATCHES) and every send observes it.
Sourcepub async fn scan_with_cell_metadata(
&self,
table_id: &TableId,
start_key: Option<&RowKey>,
end_key: Option<&RowKey>,
limit: Option<usize>,
schema: Option<&TableSchema>,
) -> Result<Vec<(RowKey, ScanRow, HashMap<String, CellWriteMetadata>)>>
pub async fn scan_with_cell_metadata( &self, table_id: &TableId, start_key: Option<&RowKey>, end_key: Option<&RowKey>, limit: Option<usize>, schema: Option<&TableSchema>, ) -> Result<Vec<(RowKey, ScanRow, HashMap<String, CellWriteMetadata>)>>
Scan a range of keys AND return per-cell write metadata.
Used when ProjectionFlags::include_cell_metadata is set (issue #693).
Falls through to stitch_and_parse_all_chunks_with_metadata for
V5CompressedLegacy format (the common path for real SSTables).
Returns None as the metadata for non-V5 formats (they do not carry
per-cell timestamps in a way the parser currently surfaces).
Source§impl SSTableReader
impl SSTableReader
Sourcepub fn chunk_cache(&self) -> &Arc<DecompressedChunkCache>
pub fn chunk_cache(&self) -> &Arc<DecompressedChunkCache>
The shared decompressed-chunk cache this reader consults (issue #1567).
Exposed so callers/tests can observe cache residency and per-instance
hit/miss counts (parallelism-immune, unlike the process-global
decompress_call_count).
Sourcepub fn decompress_call_count() -> u64
pub fn decompress_call_count() -> u64
Process-global count of actual chunk decompressions at the wired read sites (issue #1567). Tests reset around a cold/warm read pair and assert a warm-read delta of 0 to prove the hit skipped decompression.
Sourcepub fn reset_decompress_calls()
pub fn reset_decompress_calls()
Reset the decompress-work counter to zero (test/instrumentation harness).
Sourcepub fn chunk_read_call_count() -> u64
pub fn chunk_read_call_count() -> u64
Process-global count of compressed-bytes reads at the BIG point-read site (issue #1567). A warm point read that hits the cache leaves this unchanged.
Sourcepub fn reset_chunk_read_calls()
pub fn reset_chunk_read_calls()
Reset the chunk-read counter to zero (test/instrumentation harness).
Sourcepub async fn get(
&self,
table_id: &TableId,
key: &RowKey,
) -> Result<Option<ScanRow>>
pub async fn get( &self, table_id: &TableId, key: &RowKey, ) -> Result<Option<ScanRow>>
Get a value by key from the SSTable.
Resolution-mode-agnostic entry point: callers that do not carry the
manager’s resolve_reader_list signal (e.g. the per-reader helpers in
partition_lookup, schema_aware_reader, and benchmarks) get the STRICT
table-consistency guard — fully_qualified_match = false reproduces exactly
today’s table_ids_match_strict behavior on the BTI point-lookup path, so
this is a behavior-preserving conservative default. The manager’s get()
calls SSTableReader::get_with_resolution with the authoritative signal so
an exact fully-qualified match can accept rows across a benign header-keyspace
divergence (issue #1321, mirroring the seek path #1284).
Sourcepub async fn get_with_resolution(
&self,
table_id: &TableId,
key: &RowKey,
fully_qualified_match: bool,
) -> Result<Option<ScanRow>>
pub async fn get_with_resolution( &self, table_id: &TableId, key: &RowKey, fully_qualified_match: bool, ) -> Result<Option<ScanRow>>
Get a value by key, threading the authoritative resolution mode
(fully_qualified_match) into the BTI point-lookup guard (issue #1321).
See SSTableReader::get for the resolution-mode contract. Only the BTI
(“da”) point-lookup path consults fully_qualified_match; the bloom/Index.db/
sequential fallbacks are unaffected by it.
Sourcepub async fn read_value_at_offset(
&self,
offset: u64,
size: u32,
) -> Result<Option<ScanRow>>
pub async fn read_value_at_offset( &self, offset: u64, size: u32, ) -> Result<Option<ScanRow>>
Read value at a specific offset with caching
Sourcepub async fn prepare_delta_scan(
&self,
) -> Result<(Vec<u8>, V5CompressedLegacyParser)>
pub async fn prepare_delta_scan( &self, ) -> Result<(Vec<u8>, V5CompressedLegacyParser)>
Prepare for a delta-scan pass: stitch all compressed chunks of the data section and return the decompressed buffer together with a pre-configured parser.
Uses its own per-scan cursor (issue #815), so it no longer needs the
caller to serialize against concurrent reads. This method is gated on the
delta-scan feature and is the only bridge between the SSTableReader
internals and the delta_scan module, which cannot access private
helpers directly.
The schema parameter is not used here — it is threaded through the
caller’s parse_block_emit_delta invocation instead. The parser is
built via build_v5_parser() which handles version-gates and UDT
registry without needing the schema at construction time.
Source§impl SSTableReader
impl SSTableReader
Sourcepub async fn get_health_metrics(&self) -> Result<SSTableReaderHealthMetrics>
pub async fn get_health_metrics(&self) -> Result<SSTableReaderHealthMetrics>
Get comprehensive reader health and performance metrics
Sourcepub async fn perform_integrity_check(&self) -> Result<IntegrityCheckResult>
pub async fn perform_integrity_check(&self) -> Result<IntegrityCheckResult>
Perform an integrity check on the SSTable.
Issue #1283: this is a THIN PROJECTION over verify::verify_sstable — the
single source of truth for SSTable integrity — not an independent check
pipeline. The legacy implementation walked only Data.db blocks, so a
corrupt Index.db / Digest.crc32 / Summary.db / Filter.db or
out-of-order keys (all of which the verifier FAILs) read back Healthy
here — a divergent verdict. We now run the authoritative verifier in
Full mode over the reader’s EXACT generation (self.file_path, not merely
its parent directory — roborev #1283) and map its VerifyReport onto the
legacy IntegrityCheckResult shape the (test-only) consumers expect.
Source§impl SSTableReader
impl SSTableReader
Sourcepub fn invalidate_key_cache_entries(&self) -> u64
pub fn invalidate_key_cache_entries(&self) -> u64
Drop ALL of this reader’s generation’s entries from the process-global key
cache (issue #2059 §C). Called when the generation is removed / compacted
away / evicted from the warm registry, so its locations are reclaimed
promptly and recorded on the distinct invalidations counter (never the
budget-driven evictions). Returns the number of entries dropped. A
#2383 rebind does NOT call this — the identity is unchanged across a rebind,
so entries survive. No-op when the identity is None.
pub so the flight WarmTableRegistry can invalidate on warm eviction
(issue #2059 §C) — the warm registry is the process’s largest reader pin, so
its evictions are the primary reclamation trigger for the global cache.
Sourcepub async fn lookup_partition_with_index(
&self,
partition_key: &[u8],
) -> Result<Option<(u64, u32)>>
pub async fn lookup_partition_with_index( &self, partition_key: &[u8], ) -> Result<Option<(u64, u32)>>
Enhanced partition lookup using Index.db reader with promoted index support.
partition_key must be the raw partition-key bytes as produced by
PartitionKey::to_bytes:
- Single-component keys — raw value bytes (UUID = 16 bytes, int = 4 BE bytes, etc.).
- Multi-component (composite) keys —
[len: u16 BE][value bytes][0x00]per component, including a trailing0x00after the final component.
The Index.db key_lookup map is keyed on these exact raw bytes (set when the BIG-format
parser was fixed in Issue #552). The old digest-based path (which caused every lookup
to miss) has been removed. On a miss the function returns Ok(None) so callers can
fall through to their existing sequential-scan fallback.
Sourcepub fn lookup_partition_via_bti_trie(
&self,
partition_key: &[u8],
) -> Result<Option<u64>>
pub fn lookup_partition_via_bti_trie( &self, partition_key: &[u8], ) -> Result<Option<u64>>
BTI (“da”) partition point lookup via the in-memory Partitions.db trie (issue #831, building on the verified #755 primitive).
Encodes partition_key into the BTI byte-comparable trie key and walks the
trie to resolve the partition’s location. Returns:
Ok(Some(offset))— the UNCOMPRESSED Data.db byte offset of the partition. INVARIANT: this offset indexes into the DECOMPRESSED data section, never raw file bytes. The offset is resolved in one of two ways:- NARROW partition (
BtiPartitionLocation::DataOffset) — the trie returns the Data.db offset directly. - WIDE partition (
BtiPartitionLocation::RowsOffset) — the trie returns a positive offset intoRows.db; we deserialize that partition’sTrieIndexEntryviaresolve_rows_db_entryand use its recovereddata_position(issue #909/#910). Both forms share the same uncompressed Data.db offset domain, so the caller treats them identically.
- NARROW partition (
Ok(None)— the reader is not BTI, or the key has no trie path (the partition is definitely absent from this SSTable).Err(_)— a structural trie parse error, or aRowsOffsetwas returned without an accompanyingRows.db(a structurally invalid BTI SSTable).
Because the BTI trie uses path compression, a returned offset may be a
candidate for a prefix-colliding key. The caller (bti_point_lookup)
MUST verify the partition-key bytes at the resolved offset equal the
queried key before returning rows.
Sourcepub fn lookup_partition_via_bti_trie_encoded(
&self,
partition_key: &[u8],
encoded: &[u8; 9],
) -> Result<Option<u64>>
pub fn lookup_partition_via_bti_trie_encoded( &self, partition_key: &[u8], encoded: &[u8; 9], ) -> Result<Option<u64>>
Resolve a BTI partition using a PRE-ENCODED byte-comparable key, so the
Murmur3 hash + encoding is computed once per read and reused across every
candidate SSTable’s prune instead of being recomputed per candidate
(issue #1575 / C4). encoded MUST equal
encode_partition_key_for_bti_trie(partition_key); the caller
(SSTableManager’s candidate prune) computes it exactly once. Semantics are
otherwise identical to [lookup_partition_via_bti_trie]: same same-key memo,
same presence-oracle observability, same resolved offset. A non-BTI reader
returns Ok(None) (the encoded argument is simply unused).
Sourcepub fn might_contain_partition(&self, partition_key: &[u8]) -> bool
pub fn might_contain_partition(&self, partition_key: &[u8]) -> bool
Cheap presence oracle: can this SSTable possibly contain partition_key?
Used to prune SSTables before a partition-targeted scan (the query engine’s
WHERE pk = ? fast path). Returning false MUST be definitive — the SSTable
is then skipped entirely without being parsed — so this only ever returns
false for an authoritative “absent” signal:
- BTI (“da”) readers have no bloom filter; the Partitions.db trie is the
authoritative present/absent oracle. A trie miss (
Ok(None)) is definitive absence. A trie hit may be a prefix-collision candidate, which is a safe false positive here (the partition scan re-verifies the key). Any trie parse error is treated conservatively as “maybe present”. - BIG-format readers consult the bloom filter, which never reports false
negatives:
might_contain == falseis definitive absence. With no bloom filter loaded we cannot prune, so we conservatively returntrue.
partition_key must be the raw partition-key bytes (same encoding the bloom
filter and Index.db/BTI trie are keyed on).
Sourcepub fn is_bti(&self) -> bool
pub fn is_bti(&self) -> bool
true when this reader was opened on a BTI (“da”) SSTable (its
Partitions.db trie is resident). Used by the candidate-prune loop to decide
whether to hoist the BTI key encoding once per read (issue #1575 / C4).
Sourcepub fn might_contain_partition_encoded(
&self,
partition_key: &[u8],
encoded: &[u8; 9],
) -> bool
pub fn might_contain_partition_encoded( &self, partition_key: &[u8], encoded: &[u8; 9], ) -> bool
Candidate-prune presence check using a PRE-ENCODED BTI byte-comparable key,
so a multi-generation WHERE pk = ? read hashes+encodes the key ONCE (the
encoding is identical for every candidate) instead of once per candidate
(issue #1575 / C4).
encoded MUST equal encode_partition_key_for_bti_trie(partition_key),
computed once by the caller. For a BTI reader this consults the trie with the
pre-encoded key (no re-hash); for a BIG reader (no Partitions.db) it falls
back to the raw-key [might_contain_partition] path (the bloom filter is
keyed on the raw key — there is no BTI encoding to hoist), so a mixed or
non-BTI candidate set stays correct. Semantics match
[might_contain_partition]: false is definitive absence; a BTI trie hit may
be a safe prefix-collision false positive re-verified by the partition scan;
any trie parse error is treated conservatively as “maybe present”.
Sourcepub async fn iterate_all_partitions(&self) -> Result<Vec<(RowKey, ScanRow)>>
pub async fn iterate_all_partitions(&self) -> Result<Vec<(RowKey, ScanRow)>>
Enhanced partition iteration using Summary.db reader
Note: Token-based range queries are not directly supported because Summary.db does not store token values (Issue #218). Instead, this iterates all summary entries and returns all partition data.
For token-based filtering, compute tokens from partition keys after retrieval.
§Issue #500: Sequential-scan fallback for writer-produced SSTables
The Summary.db → Index.db → Data.db lookup path depends on Index.db format compatibility between writer and reader (digest format vs. raw-key format). Locally written SSTables emit raw-key Index.db entries that the reader’s digest-based parser cannot resolve, so the lookup loop returns 0 entries even though Summary.db enumerates the partitions correctly.
When that happens we fall back to sequential_scan, which walks Data.db
directly. For V5CompressedLegacy NB SSTables (the format the writer emits),
sequential_scan uses the chunk-stitching path and returns every partition.
Sourcepub fn has_partition_index(&self) -> bool
pub fn has_partition_index(&self) -> bool
Whether this reader has a usable random-access partition index — a
loaded Index.db (BIG format; issue #2302 resolves CQLite-written Index.db
too) or a Partitions.db trie (BTI format) — that lets
iterate_all_partitions and point lookups
avoid a full sequential Data.db scan.
This is a capability probe: it must report true only for the exact
states that route to the non-sequential path. Both point lookups
(index_reader.lookup_partition / the BTI trie) and iterate_all_partitions
(the full-Index.db walk / BTI) gate on index_reader/bti_partitions_db
— never on summary_reader.
Issue #2302 (roborev job 1613): summary_reader is DELIBERATELY excluded. A
Summary.db only samples ~1-in-128 partitions and never gates the
random-access route, so a present-Summary / absent-Index BIG reader is
degraded — every read falls into sequential_scan and WARNs. Including
summary_reader here made the probe report “fast path available” for that
degraded reader (a fidelity lie). Narrowing to the truly-supporting state
keeps the probe honest.
Snapshot-completeness probe (issue #2295): a directory served with only
Data.db (its index siblings absent) still opens but reports false; a
complete component set loads Index.db (→ index_reader) and reports
true. All current callers (this module’s own tests + the #2295 snapshot
test) use this probe to mean “does the fast/index path apply,” so a single
narrowed predicate covers every caller without a split API.
Sourcepub async fn iterate_token_range(
&self,
_start_token: i64,
_end_token: i64,
) -> Result<Vec<(RowKey, ScanRow)>>
👎Deprecated since 0.1.0: Summary.db does not store tokens. Use iterate_all_partitions() and filter by computed tokens.
pub async fn iterate_token_range( &self, _start_token: i64, _end_token: i64, ) -> Result<Vec<(RowKey, ScanRow)>>
Summary.db does not store tokens. Use iterate_all_partitions() and filter by computed tokens.
Token range iteration (deprecated - tokens not stored in Summary.db)
This method is kept for API compatibility but simply delegates to
iterate_all_partitions() since Summary.db does not store token values.
Token filtering should be done by the caller after retrieval.
Sourcepub async fn get_timestamp_range(&self) -> Result<Option<(i64, i64)>>
pub async fn get_timestamp_range(&self) -> Result<Option<(i64, i64)>>
Get min/max timestamps from Statistics.db reader
Sourcepub async fn get_token_coverage(&self) -> Result<Option<(i64, i64)>>
👎Deprecated since 0.1.0: Summary.db does not store tokens. Compute tokens from partition keys using the partitioner.
pub async fn get_token_coverage(&self) -> Result<Option<(i64, i64)>>
Summary.db does not store tokens. Compute tokens from partition keys using the partitioner.
Get token coverage (deprecated - tokens not stored in Summary.db)
Note: As of Issue #218, Summary.db does not store token values. This method now returns None since token coverage cannot be determined from Summary.db alone. Token computation requires partition keys and the partitioner algorithm.
Source§impl SSTableReader
impl SSTableReader
Sourcepub async fn locate(&self, partition_key: &[u8]) -> Result<Option<(u64, u32)>>
pub async fn locate(&self, partition_key: &[u8]) -> Result<Option<(u64, u32)>>
Resolve partition_key to its uncompressed Data.db offset (and size, where
the format records one) through the one format-tagged façade.
Returns:
Ok(Some((offset, size)))— the partition’s uncompressed data-section offset.sizeis the Index.db-recorded partition size for BIG, and0for BTI (the trie records no size). Callers keep treatingsize == 0as “parse forward from the offset, do not range-read”.Ok(None)— absent: out of the authoritative[first_key, last_key]bound (C5), a BTI trie miss (definitive absence), or a BIGIndex.dbmiss (which the BIG point path treats as inconclusive and re-checks via a scan; see the carve-out above — an index miss is NOT a definitive absent).Err(_)— the same typedError::Corruptionthe underlying legacy path raises (a corrupt trie, aRowsOffsetwith noRows.db, etc.).
Resolution order:
- C5 range short-circuit (step 1): if
partition_keyprovably sorts outside this SSTable’s authoritative Summary bound, record oneRANGE_SHORT_CIRCUITSand return absence before any presence work. A no-op when no bound exists (BTI / no Summary). - Format dispatch: BTI (
da) walks thePartitions.dbtrie; BIG (nb/uncompressed) probes the raw-keyIndex.dbmap. Both reuse the B4 key-offset cache and their existing presence-counter emissions.
Source§impl SSTableReader
impl SSTableReader
Sourcepub async fn resolve_registry_schema(&mut self)
pub async fn resolve_registry_schema(&mut self)
Pre-resolve the registry schema into the sync cache (issue #1692, AG3).
Call this from an async wiring point (e.g. open_reader_with_schema, or a
DIRECT caller’s own async context), AFTER
set_schema_registry — or use the
combined attach_schema_registry.
It resolves the table schema from the async registry a single time —
properly awaited — and caches it in self.registry_schema, so the SYNC
schema-fallback tier of get_table_schema reads a plain field instead of
block_on-ing the async registry lock on a tokio worker thread.
Cold path only: when the SSTable header already carried a schema
(self.schema.is_some()) the sync path short-circuits before the registry
tier, so no resolution is performed. A registry miss leaves the cache
None; the sync path then falls through to header-column construction,
exactly as the previous block_on path did on a miss.
Source§impl SSTableReader
impl SSTableReader
Sourcepub async fn open(
path: &Path,
config: &Config,
platform: Arc<Platform>,
) -> Result<Self>
pub async fn open( path: &Path, config: &Config, platform: Arc<Platform>, ) -> Result<Self>
Open an SSTable file for reading.
Instrumented (epic #1031 / #1034): wraps the open in a
sstable.reader.open span, increments the SSTABLES_OPEN gauge on
success, and records an error on the reader subsystem when open fails.
Sourcepub async fn open_cancellable(
path: &Path,
config: &Config,
platform: Arc<Platform>,
cancel: ScanCancel,
) -> Result<Self>
pub async fn open_cancellable( path: &Path, config: &Config, platform: Arc<Platform>, cancel: ScanCancel, ) -> Result<Self>
Cancel-aware open (issue #2383 fix C).
Threads a synchronous ScanCancel
into the Index.db partition-index parse so a client-disconnect cancel
aborts a 1.58M-entry parse within one poll interval instead of pinning a
tokio worker to completion. Non-cancellable callers use open
(a default never-cancel flag). Returns Error::Cancelled on a mid-parse
trip.
Sourcepub async fn open_with_cache(
path: &Path,
config: &Config,
platform: Arc<Platform>,
cache: Arc<DecompressedChunkCache>,
) -> Result<Self>
pub async fn open_with_cache( path: &Path, config: &Config, platform: Arc<Platform>, cache: Arc<DecompressedChunkCache>, ) -> Result<Self>
Open an SSTable file for reading, sharing the provided
DecompressedChunkCache.
Identical to open except the reader stores cache (an
Arc clone) instead of minting its own, so every reader a manager opens
for one dataset consults the same bytes-bounded chunk cache (issue #1567).
Sourcepub async fn open_with_cache_cancellable(
path: &Path,
config: &Config,
platform: Arc<Platform>,
cache: Arc<DecompressedChunkCache>,
cancel: ScanCancel,
) -> Result<Self>
pub async fn open_with_cache_cancellable( path: &Path, config: &Config, platform: Arc<Platform>, cache: Arc<DecompressedChunkCache>, cancel: ScanCancel, ) -> Result<Self>
Cancel-aware open_with_cache (issue #2383 fix C):
same as it but threads cancel into the Index.db parse. See
open_cancellable.
Sourcepub fn set_schema_registry(
&mut self,
schema_registry: Arc<RwLock<SchemaRegistry>>,
)
👎Deprecated: attaches the registry but CANNOT pre-resolve the sync schema-fallback cache (would need a forbidden block_on, issue #1692); registry schemas will not be available to a subsequent sync get_table_schema. Use the async attach_schema_registry (which pre-resolves), or call resolve_registry_schema after this from an async context, for registry-schema-aware reads.
pub fn set_schema_registry( &mut self, schema_registry: Arc<RwLock<SchemaRegistry>>, )
attaches the registry but CANNOT pre-resolve the sync schema-fallback cache (would need a forbidden block_on, issue #1692); registry schemas will not be available to a subsequent sync get_table_schema. Use the async attach_schema_registry (which pre-resolves), or call resolve_registry_schema after this from an async context, for registry-schema-aware reads.
Set the schema registry for schema-driven operations.
This is a SYNC method (&mut self, non-async), so it CANNOT await the
async registry to pre-resolve the sync schema-fallback cache
(registry_schema) — doing so would require a
block_on, which #1692 (AG3) forbids on a tokio worker thread. It
therefore only stores the registry and INVALIDATES any previously
pre-resolved cache (the new registry may resolve a different schema).
Direct callers that intend to trigger a SYNC parse (whose schema-fallback
tier reads registry_schema) must, from an async context, either call the
combined attach_schema_registry instead,
or call resolve_registry_schema after
this. Otherwise the sync path deliberately falls through to
header-column construction (or None). The crate’s own async wiring path
(open_reader_with_schema) already does this.
Sourcepub async fn attach_schema_registry(
&mut self,
schema_registry: Arc<RwLock<SchemaRegistry>>,
)
pub async fn attach_schema_registry( &mut self, schema_registry: Arc<RwLock<SchemaRegistry>>, )
Attach a schema registry AND pre-resolve it into the sync fallback cache
(issue #1692). Async wiring convenience for DIRECT callers: it combines
set_schema_registry with
resolve_registry_schema so the sync
get_table_schema fallback tier is populated without any block_on.
Prefer this over the bare sync set_schema_registry whenever you attach a
registry to a reader you will parse synchronously.
Sourcepub fn set_udt_registry(&mut self, registry: UdtRegistry)
pub fn set_udt_registry(&mut self, registry: UdtRegistry)
Set the UDT registry for UDT-aware parsing in collections
This enables proper parsing of UDTs inside collections (List, Set, Map) by providing the UDT field definitions needed for nested type resolution.
Sourcepub fn has_udt_registry(&self) -> bool
pub fn has_udt_registry(&self) -> bool
Whether a UDT registry has been wired onto this reader (issue #2310, WS1
#2345). A warm-handle cache that hands SHARED Arc<SSTableReader>s to the
reader-based k-way merge seam (KWayMerger::new_from_readers, which takes
NO udt_registry parameter) MUST open each reader WITH its registry
already resolved before wrapping it in Arc; this getter lets that caller
PROVE the registry is present rather than silently decoding a frozen/nested
UDT cell as Blob (the #1234 data-loss class).
Sourcepub fn file_path(&self) -> PathBuf
pub fn file_path(&self) -> PathBuf
The Data.db path this reader was opened from (issue #2310). The warm
registry keys parsed state on the file’s inode-stable generation identity,
so it needs the backing path to stat device+inode without re-listing the
directory.
Sourcepub fn file_size(&self) -> u64
pub fn file_size(&self) -> u64
The Data.db size in bytes captured at open. Used by the warm registry’s
authoritative rebind identity check (issue #2383): a rebind is accepted
only when the live candidate’s (device, inode) AND size match this
reader’s, else it fails closed to a full re-open.
Sourcepub fn rebind_path(&self, new_path: &Path)
pub fn rebind_path(&self, new_path: &Path)
Rebind this reader’s lazy-scan path to new_path WITHOUT re-opening or
re-parsing (issue #2383, the #2356 “rebind-by-inode” direction).
The caller MUST have already established that new_path is the SAME
on-disk generation as this reader by AUTHORITATIVE identity — matching
(device, inode) and size (issue #28 no-heuristics; Cassandra snapshot
files are hardlinks to the immutable SSTable, so a same-inode candidate is
byte-identical). The already-open point/scan handles reference the shared
inode and stay valid across the swap; only Self::new_scan_cursor’s
per-scan File::open reads this path, so pointing it at a LIVE hardlink
restores the #2352 ENOENT protection with zero parse cost.
§In-flight isolation invariant (roborev-1654 HIGH, adjudicated)
Mutating this shared Arc<SSTableReader>’s path while a concurrent request
scans it does NOT break the in-flight request’s isolation, because the
rebind is byte-transparent and monotone-toward-live:
- Same immutable inode. The sole caller (
warm::registry::rebind) fires only afterrebuild::rebind_matchesproves(device, inode, generation)+ size equality; every rebind target is a hardlink to the SAME immutable SSTable inode, so any path this reader carries yields byte-identical data (issue #28 no-heuristics). - Atomic swap.
file_pathis anarc_swap::ArcSwap<PathBuf>; a concurrentfile_path()sees either the old or the new wholePathBuf, never a torn value. - Dead → live only. A rebind happens only when the current path is
dead and an identity-matching LIVE hardlink exists, so an in-flight
request’s NEXT
new_scan_cursorFile::openis strictly MORE likely to succeed after the rebind than before (pre-rebind it would ENOENT). - No semantics off the path. No
file_path()consumer re-derives keyspace/table/schema from the path afteropen; the path is used ONLY forFile::open(bytes) and all parsed read state (header, compression info, CRC, index, generation) lives on&self, fixed at open — the swapped path changes which hardlink is read, never what the bytes mean. This covers BOTH theData.dbpath AND the lazyIndex.dbpath (below): both are same-inode hardlink siblings in the rebound snapshot dir.
§Rebinding the lazy Index.db path too (issue #2356 roborev)
Repointing ONLY the Data.db path reintroduced the #2352 ENOENT class for
the dominant point-read shape: a BIG reader opened lazily over a usable
Summary.db (#2412 §A) keeps its own Index.db path, and every DEFERRED
Index.db open (ensure_materialized, the Summary-guided bounded-interval
point probe) reads THAT path. If the original snapshot dir is torn down
before the reader ever materialized, a not-yet-materialized reader would
File::open the dead path and ENOENT. So the rebind ALSO repoints the
IndexReader’s path to the Index.db hardlink sibling of new_path,
keeping every Index.db consumer (deferred-materialize, point-interval, and
the streaming walk’s current_index_db_path Data.db-sibling derivation) on
the live generation.
Sourcepub fn endpoint_tokens(&self) -> Option<(i64, i64)>
pub fn endpoint_tokens(&self) -> Option<(i64, i64)>
The immutable [first_key_token, last_key_token] endpoints cached at open
(issue #1576), or None when Summary.db was absent/empty. The Flight
warm registry (issue #2310) uses this to token-prune a WARM reader set with
ZERO extra I/O — a warm hit re-reads no Summary.db, preserving the
“zero Index/Summary/Statistics/bloom parse” property even for a
token-filtered scan. SSTables store partitions in token order, so the
first endpoint is the min token and the last the max.
Sourcepub fn index_is_materialized(&self) -> bool
pub fn index_is_materialized(&self) -> bool
Whether this reader’s Index.db partition map is CURRENTLY fully
resident (issue #2412 §D — the Flight warm registry’s memory accounting).
A BIG reader opened lazily over a usable Summary.db (design §A) reports
false until some consumer’s ensure_materialized-driven full parse
(a full/compaction scan whose Summary-guided streaming walk FellBack)
actually happens; the Summary-guided point/scan paths (§B/§C) never
trigger it. An eagerly-opened reader (the Summary.db-absent FellBack
case, §A1) reports true immediately — its Index.db was fully parsed
at open. No Index.db at all (absent component) reports true (there is
no resident cost to represent either way).
Sourcepub fn set_scan_cancel(&mut self, cancel: ScanCancel)
pub fn set_scan_cancel(&mut self, cancel: ScanCancel)
Wire a cooperative-cancellation token into this reader’s long-running scans (issue #2264).
The compaction streaming read and its sequential-scan fallback poll this
token at a bounded interval, so a cancelled Flight do_get abandons an
otherwise uninterruptible full-Data.db walk within milliseconds instead of
waiting out the coarse ~1–2 min transport backstop. Idempotent; the last
token set wins. Readers never wired keep the default never-cancel flag.
Sourcepub async fn stats(&self) -> Result<&SSTableReaderStats>
pub async fn stats(&self) -> Result<&SSTableReaderStats>
Get reader statistics
Sourcepub fn calculate_header_size(&self) -> usize
pub fn calculate_header_size(&self) -> usize
Calculate header size based on format and actual header content
Sourcepub fn cassandra_version(&self) -> CassandraVersion
pub fn cassandra_version(&self) -> CassandraVersion
Get the Cassandra version from the SSTable header
Sourcepub fn has_uint_deletion_time(&self) -> bool
pub fn has_uint_deletion_time(&self) -> bool
Whether the on-disk DeletionTime uses the Cassandra 5.0 UNSIGNED
localDeletionTime serializer (oa/da, hasUIntDeletionTime) rather
than the legacy SIGNED i32 form (nb).
The verifier uses this to interpret a raw partition-level
localDeletionTime: on the legacy signed form a NEGATIVE value (that is
not the i32::MAX live sentinel) is unambiguously corrupt, whereas on the
unsigned form a value in [2^31, 2^32) is a legitimate far-future
deletion time and must NOT be flagged (no heuristics — the format decides).
Authority: BigFormat.java:409 (hasUIntDeletionTime), DeletionTime.java.
Sourcepub fn format_version(&self) -> Result<String>
pub fn format_version(&self) -> Result<String>
Get the SSTable format version string
Sourcepub fn header(&self) -> &SSTableHeader
pub fn header(&self) -> &SSTableHeader
Get a reference to the SSTable header
Sourcepub fn schema(&self) -> Option<&TableSchema>
pub fn schema(&self) -> Option<&TableSchema>
Get the table schema extracted from the SSTable header
Returns None for legacy formats or if schema extraction failed.
Sourcepub fn effective_schema(&self) -> Option<TableSchema>
pub fn effective_schema(&self) -> Option<TableSchema>
The effective table schema the read/verify paths decode with — the same
four-tier resolution partition_clustering_verify_scan uses (issue #1282).
Returned owned because the registry-fallback tier constructs a schema; the
verifier needs it to drive the authoritative clustering comparator
(crate::storage::write_engine::mutation::ClusteringKey::compare).
Sourcepub fn extract_write_time_from_entry(&self, _key: &RowKey, row: &ScanRow) -> i64
pub fn extract_write_time_from_entry(&self, _key: &RowKey, row: &ScanRow) -> i64
Extract write time from entry metadata