1use std::sync::Arc;
10
11use parking_lot::Mutex;
12use uqa_core::memory::{Budgeted, BudgetedVec};
13
14use crate::catalog::new_nonzero_catalog_identity;
15use crate::diskann_index::format::DiskANNGeneration;
16use crate::mvcc::{DatabaseId, IdentifierRequest, ResourceLease, WeakResourceLease};
17use crate::read_control::StorageReadControl;
18use crate::{
19 KeyValueStore, StorageBackendError, StorageBackendResult, StorageSessionAffinity,
20 StorageTransactionModel,
21};
22
23use super::{KeyValueMutation, KeyValueRead};
24
25mod build;
26pub(super) mod conformance;
27pub mod identifiers;
28mod identity;
29mod keys;
30mod maintenance;
31mod ownership;
32mod pruning;
33pub mod publication;
34mod reclamation;
35mod source;
36mod staging;
37pub(in crate::key_value) mod state;
38
39pub(super) const READ_PREFIX: &[u8] = keys::ROOT;
40
41use keys::{database_key, Keys};
42use state::{data_identity, fixed};
43
44pub use identity::KeyValueDiskANNMappingMaintenance;
45pub use maintenance::{
46 DiskANNMaintenanceStatus, DiskANNMaintenanceStep, KeyValueDiskANNMaintenance,
47};
48pub use pruning::KeyValueDiskANNPruner;
49pub use source::KeyValueDiskANNSource;
50pub use staging::KeyValueDiskANNStage;
51pub use state::DiskANNStageStatus;
52
53struct Owner {
54 store: Arc<dyn KeyValueStore>,
55 database: DatabaseId,
56 affinity: StorageSessionAffinity,
57 writer: Mutex<Option<ResourceLease>>,
58 leases: Mutex<BudgetedVec<WeakResourceLease>>,
59}
60
61#[derive(Clone)]
63pub struct KeyValueDiskANNStore {
64 owner: Arc<Budgeted<Owner>>,
65}
66
67impl KeyValueDiskANNStore {
68 pub fn allocate_bound_stage(
70 &self,
71 scope: &crate::diskann_index::catalog::DiskANNIndexScope,
72 control: &StorageReadControl,
73 ) -> StorageBackendResult<KeyValueDiskANNStage> {
74 self.allocate_catalog_stage(scope, control)
75 }
76
77 pub fn connect(
78 source: &Arc<dyn KeyValueStore>,
79 control: &StorageReadControl,
80 ) -> StorageBackendResult<Self> {
81 control.check()?;
82 let database = versioned_database(&**source)?;
83 let store = source.open_session_with_cancellation(control.cancellation())?;
84 let affinity = validate_session(&**source, &*store)?;
85 if store.in_transaction()
86 || store.identifier_allocator().is_none()
87 || store.resource_leases().is_none()
88 {
89 return Err(invalid(
90 "staging requires an inactive session, durable identifiers and resource leases",
91 ));
92 }
93 let retained = store.open_retained_read_session(control.cancellation())?;
95 validate_session(&*store, &*retained)?;
96 drop(retained);
97 let owner = Budgeted::new(
98 Owner {
99 store,
100 database,
101 affinity,
102 writer: Mutex::new(None),
103 leases: Mutex::new(BudgetedVec::new(control.memory())),
104 },
105 control.memory().empty_reservation(),
106 )
107 .into_shared()?;
108 Ok(Self { owner })
109 }
110
111 pub fn initialize(&self, control: &StorageReadControl) -> StorageBackendResult<[u8; 16]> {
113 let mut selected = None;
114 self.mutate(control, &mut |read, batch| {
115 selected = read_data_identity(read, control)?;
116 if selected.is_none() {
117 let identity = new_nonzero_catalog_identity("DiskANN", "data")?;
118 let mut bytes = [1; 17];
119 bytes[1..].copy_from_slice(&identity);
120 batch.put(&database_key(), &bytes)?;
121 selected = Some(identity);
122 }
123 Ok(())
124 })?;
125 selected.ok_or_else(|| invalid("initialization did not select a data identity"))
126 }
127
128 pub fn data_identity(&self, control: &StorageReadControl) -> StorageBackendResult<[u8; 16]> {
129 let _writer = self.owner.writer.lock();
130 self.idle(control)?;
131 stored_data_identity(&*self.owner.store, control)
132 }
133
134 pub fn allocate_stage(
136 &self,
137 table: u64,
138 index: u64,
139 control: &StorageReadControl,
140 ) -> StorageBackendResult<KeyValueDiskANNStage> {
141 let _writer = self.owner.writer.lock();
142 self.idle(control)?;
143 let database = stored_data_identity(&*self.owner.store, control)?;
144 let provisional = DiskANNGeneration::new(database, table, index, 1)?;
145 let keys = Keys::new(provisional);
146 let allocator = self
147 .owner
148 .store
149 .identifier_allocator()
150 .ok_or_else(|| invalid("durable generation identifiers are unavailable"))?;
151 let minimum = match allocator.identifier_watermark(keys.legacy_allocation_namespace())? {
154 Some(previous) => previous.checked_add(1).ok_or_else(|| {
155 crate::mvcc::VersionError::IdentifiersExhausted.into_storage_error()
156 })?,
157 None => 1,
158 };
159 let allocation = allocator.allocate_identifiers(
160 identifiers::NAMESPACE,
161 IdentifierRequest::Reserve {
162 minimum,
163 maximum: u64::MAX,
164 count: std::num::NonZeroU64::MIN,
165 },
166 )?;
167 let generation = DiskANNGeneration::new(database, table, index, allocation.watermark())?;
168 Ok(KeyValueDiskANNStage::reserved(
169 self.clone(),
170 generation,
171 self.reserve_owner(control)?,
172 ))
173 }
174
175 pub fn resume_stage(
177 &self,
178 generation: DiskANNGeneration,
179 control: &StorageReadControl,
180 ) -> StorageBackendResult<KeyValueDiskANNStage> {
181 KeyValueDiskANNStage::resume(self.clone(), generation, control)
182 }
183
184 pub fn open_source(
185 &self,
186 generation: DiskANNGeneration,
187 control: &StorageReadControl,
188 ) -> StorageBackendResult<Arc<KeyValueDiskANNSource>> {
189 KeyValueDiskANNSource::capture(self, generation, DiskANNStageStatus::Sealed, control)
190 }
191
192 pub fn commit_pending(&self) -> StorageBackendResult<()> {
194 let mut writer = self.owner.writer.lock();
195 let result = if self.owner.store.in_transaction() {
196 self.owner.store.commit_transaction()
197 } else {
198 Ok(())
199 };
200 if !self.owner.store.in_transaction() {
201 *writer = None;
202 }
203 result
204 }
205
206 pub fn rollback_pending(&self) -> StorageBackendResult<()> {
208 let mut writer = self.owner.writer.lock();
209 let result = if self.owner.store.in_transaction() {
210 self.owner.store.rollback_transaction()
211 } else {
212 Ok(())
213 };
214 if !self.owner.store.in_transaction() {
215 *writer = None;
216 }
217 result
218 }
219
220 fn idle(&self, control: &StorageReadControl) -> StorageBackendResult<()> {
221 control.check()?;
222 if versioned_database(&*self.owner.store)? != self.owner.database
223 || self.owner.store.transaction_affinity().as_ref() != Some(&self.owner.affinity)
224 {
225 return Err(invalid("staging session changed its database or affinity"));
226 }
227 if self.owner.store.in_transaction() {
228 return Err(invalid(
229 "resolve the retained staging attempt before another operation",
230 ));
231 }
232 Ok(())
233 }
234
235 fn mutate(
236 &self,
237 control: &StorageReadControl,
238 operation: &mut KeyValueMutation<'_>,
239 ) -> StorageBackendResult<()> {
240 self.mutate_owned(None, control, operation)
241 }
242
243 fn mutate_owned(
244 &self,
245 lease: Option<&ResourceLease>,
246 control: &StorageReadControl,
247 operation: &mut KeyValueMutation<'_>,
248 ) -> StorageBackendResult<()> {
249 let mut writer = self.owner.writer.lock();
250 self.idle(control)?;
251 *writer = lease.cloned();
252 let result = self.owner.store.with_mutation(&mut |read, batch| {
253 operation(read, batch)?;
254 control.check()
255 });
256 if !self.owner.store.in_transaction() {
257 *writer = None;
258 }
259 result
260 }
261}
262
263fn versioned_database(store: &dyn KeyValueStore) -> StorageBackendResult<DatabaseId> {
264 match store.transaction_model() {
265 StorageTransactionModel::VersionedConcurrent { database }
266 if store.transaction_affinity().is_some() =>
267 {
268 Ok(database)
269 }
270 _ => Err(invalid(
271 "versioned session and retention capabilities are required",
272 )),
273 }
274}
275
276fn validate_session(
277 source: &dyn KeyValueStore,
278 retained: &dyn KeyValueStore,
279) -> StorageBackendResult<StorageSessionAffinity> {
280 if versioned_database(source)? != versioned_database(retained)?
281 || source.storage_identity()? != retained.storage_identity()?
282 || source.transaction_affinity() == retained.transaction_affinity()
283 {
284 return Err(invalid(
285 "independent session has a different provider or shared affinity",
286 ));
287 }
288 retained
289 .transaction_affinity()
290 .ok_or_else(|| invalid("session affinity is missing"))
291}
292
293fn stored_data_identity(
294 store: &dyn KeyValueStore,
295 control: &StorageReadControl,
296) -> StorageBackendResult<[u8; 16]> {
297 fixed(control, |visit| {
298 store.visit_value_bounded(&database_key(), 17, control, visit)
299 })?
300 .map(data_identity)
301 .transpose()?
302 .ok_or_else(|| invalid("persistent data identity is missing"))
303}
304
305fn read_data_identity(
306 read: &dyn KeyValueRead,
307 control: &StorageReadControl,
308) -> StorageBackendResult<Option<[u8; 16]>> {
309 fixed(control, |visit| {
310 read.visit_value_bounded(&database_key(), 17, control, visit)
311 })?
312 .map(data_identity)
313 .transpose()
314}
315
316fn invalid(message: &str) -> StorageBackendError {
317 StorageBackendError::Other(format!("invalid DiskANN Key/Value generation: {message}"))
318}