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
//! Durable index snapshots (R4.2).
//!
//! ART trees and standard HNSW graphs are memory-only structures that were
//! historically rebuilt from a full table scan on every open (94.5s for the
//! 678k x 768-dim incident dataset). This module defines the on-disk snapshot
//! format that lets a reopen skip that rebuild entirely.
//!
//! # Design: checkpoint snapshots with crash-invalidated validity markers
//!
//! Two artifact kinds are persisted, each with a *validity marker* written in
//! the same RocksDB `WriteBatch`:
//!
//! - **ART entries** — `idxsnap:{table}` holds every `(encoded_key, row_ids)`
//! pair of every ART index registered on the table ([`ArtTableSnapshot`]),
//! `idxsnapv:{table}` is its marker.
//! - **HNSW graphs** — the graph itself is dumped through `hnsw_rs`'s
//! `file_dump` into `{data_dir}/hnsw_snapshots/{basename}.hnsw.{graph,data}`,
//! and `vecsnap:{index}` holds the row-id mappings + construction params
//! ([`VectorGraphSidecar`]); `vecsnapv:{index}` is its marker.
//!
//! # Checkpoint moments (chosen + documented)
//!
//! Snapshots are written at **clean shutdown** (`EmbeddedDatabase` drop/close
//! with no open transaction) and on the explicit
//! `StorageEngine::persist_index_snapshots()` checkpoint API. They are *not*
//! written per-mutation: the DML hot path stays untouched except for one
//! relaxed atomic load.
//!
//! # Crash correctness (no WAL-tail replay needed)
//!
//! The invariant is: *a validity marker exists if and only if the snapshot
//! exactly matches the index-relevant on-disk state*. It is maintained by
//! deleting all markers (durably, via RocksDB delete) on the **first**
//! index-mutating operation after a checkpoint — the index managers carry a
//! `snapshot_clean` flag and an invalidation hook wired by the engine. Index
//! registration at open also counts as a mutation, so markers are effectively
//! consumed by the open that reads them.
//!
//! Consequently:
//! - clean shutdown → reopen: markers valid, snapshot loaded, **no scan**;
//! - crash after post-checkpoint mutations: markers already deleted before
//! the first mutation hit the data keyspace → next open falls back to the
//! full scan rebuild (the pre-R4.2 behaviour, always correct);
//! - crash before any mutation: snapshot still matches state, loading it is
//! correct whether or not the marker survived.
//!
//! This trades "fast open after a crash" (rare) for zero hot-path overhead
//! and no logical-WAL coupling; WAL-tail replay into the index layer can be
//! layered on later without changing the format.
//!
//! # Versioning / migration
//!
//! [`ArtTableSnapshot::key_encoding_version`] records the ART key encoding the
//! entries were produced with ([`ART_KEY_ENCODING_VERSION`], v2 = the R4.4
//! order-preserving encoding: sign-flipped big-endian integers, total-order
//! floats, escaped composite separators). A snapshot whose versions do not
//! match the running binary is ignored and the table is rebuilt from rows —
//! pre-R4.2 data directories simply have no snapshots and migrate by
//! rebuilding once, exactly as every open did before.
use ;
/// Snapshot container format version.
pub const INDEX_SNAPSHOT_FORMAT_VERSION: u32 = 1;
/// ART key encoding version the snapshot entries were written with.
///
/// v1: raw `to_be_bytes` integers (not order-preserving for negatives).
/// v2: order-preserving encoding (R4.4) — sign-flipped big-endian integers,
/// total-order floats, `0x00 -> 0x00 0xFF` escaping inside composite
/// string/byte values.
pub const ART_KEY_ENCODING_VERSION: u32 = 2;
/// RocksDB key prefixes for snapshot blobs and their validity markers.
pub const ART_SNAPSHOT_PREFIX: &str = "idxsnap:";
pub const ART_SNAPSHOT_MARKER_PREFIX: &str = "idxsnapv:";
pub const VEC_SNAPSHOT_PREFIX: &str = "vecsnap:";
pub const VEC_SNAPSHOT_MARKER_PREFIX: &str = "vecsnapv:";
/// Subdirectory of the data dir holding `hnsw_rs` graph dumps.
pub const HNSW_SNAPSHOT_DIR: &str = "hnsw_snapshots";
/// Validity marker payload. Written in the same `WriteBatch` as the snapshot
/// blob; its presence (with matching versions) is what makes a snapshot
/// trustworthy at open.
/// One ART index's serialized entries: `(encoded key, row ids)` pairs in
/// iteration order. Multi-value (non-unique) keys carry all row ids.
/// All ART indexes of one table, snapshotted together so the open path can
/// take a single load-or-scan decision per table.
/// Sidecar for a dumped standard HNSW graph: everything needed to
/// reconstruct the in-memory wrapper around the reloaded `hnsw_rs` graph.
/// What `persist_index_snapshots` wrote (diagnostics / tests).
/// How the last open populated the in-memory indexes (diagnostics / tests).