pub(in crate::key_value) mod journal;
mod lifecycle;
mod live;
mod maintenance;
pub(in crate::key_value) mod populations;
mod retained;
#[cfg(test)]
mod tests;
pub use live::KeyValueDiskANNHandle;
pub use populations::KeyValueDiskANNPopulationRecords;
pub use retained::{DiskANNCanonicalVectorVisitor, RetainedDiskANNCanonical};
use std::sync::Arc;
use super::KeyValueVectorIndex;
use crate::diskann_index::format::{
DiskANNCanonicalOrigin as Record, DiskANNChangeIdentity, DiskANNVectorVersion,
CANONICAL_ORIGIN_BYTES as BYTES,
};
use crate::key_value::{codec, KeyValueRead};
use crate::mvcc::VersionError;
use crate::read_control::StorageReadControl;
use crate::{KeyValueStore, RelationIdentity, StorageBackendResult};
use uqa_core::DocId;
pub(in crate::key_value) const ROOT: &[u8] = b"\0uqa-diskann-canonical-v1\0";
pub struct KeyValueDiskANNCanonical {
index: KeyValueVectorIndex,
}
impl KeyValueDiskANNCanonical {
pub fn new(
store: Arc<dyn KeyValueStore>,
table: impl Into<String>,
field: impl Into<String>,
dimensions: u32,
) -> StorageBackendResult<Self> {
if dimensions == 0 || !store.transaction_model().is_versioned() {
return Err(invalid(
"canonical origins require dimensions and a versioned store",
));
}
Ok(Self {
index: KeyValueVectorIndex::new(store, table, field, dimensions),
})
}
pub fn replace(
&self,
document: DocId,
vectors: &[Vec<f32>],
control: &StorageReadControl,
) -> StorageBackendResult<DiskANNVectorVersion> {
self.replace_guarded(document, vectors, control, &mut |_, _| Ok(()))
}
fn replace_guarded(
&self,
document: DocId,
vectors: &[Vec<f32>],
control: &StorageReadControl,
guard: &mut crate::key_value::KeyValueMutation<'_>,
) -> StorageBackendResult<DiskANNVectorVersion> {
control.check()?;
for vector in vectors {
crate::vector_index::validate_vector_values_controlled(
self.index.dimensions,
vector,
Some(control),
)?;
}
let count = codec::usize_to_u64(vectors.len(), "canonical vector count")?;
codec::validate_vector_ordinal_count(count)?;
let _workspace = control.memory().reserve(self.workspace_bytes(true)?)?;
let key = key(&self.index.table, &self.index.field, document)?;
let mut version = None;
self.index
.store
.with_versioned_mutation(&mut |origin, read, batch| {
control.check()?;
guard(read, batch)?;
let current = DiskANNVectorVersion::new(origin.transaction(), origin.revision())?;
let record = Record::new(current, self.index.dimensions, count)?;
self.index.stage_replace_values(batch, document, vectors)?;
batch.replace_diskann_origin(&key, &record.encode())?;
batch.put(
&journal::key(
&self.index.table,
&self.index.field,
DiskANNChangeIdentity::new(document, current),
)?,
&record.encode(),
)?;
control.check()?;
version = Some(current);
Ok(())
})?;
version.ok_or_else(|| invalid("canonical mutation did not execute"))
}
pub fn retain(
&self,
control: &StorageReadControl,
) -> StorageBackendResult<RetainedDiskANNCanonical> {
self.retain_selected(None, control)
}
pub fn retain_for_index(
&self,
index: &RelationIdentity,
control: &StorageReadControl,
) -> StorageBackendResult<RetainedDiskANNCanonical> {
self.retain_selected(Some(index), control)
}
fn retain_selected(
&self,
index: Option<&RelationIdentity>,
control: &StorageReadControl,
) -> StorageBackendResult<RetainedDiskANNCanonical> {
control.check()?;
let _workspace = control.memory().reserve(self.workspace_bytes(false)?)?;
let vectors = codec::vector_field_prefix(&self.index.table, &self.index.field)?;
let origins = prefix(&self.index.table, &self.index.field)?;
let changes = journal::prefix(&self.index.table, &self.index.field)?;
let populations = populations::field_prefixes(&self.index.table, &self.index.field)?;
let marker = super::guards::reference_key(&vectors, control)?;
let mut selected = None;
self.index
.store
.with_read_view(&mut |read: &dyn KeyValueRead| {
let binding = index
.map(|index| {
crate::key_value::catalog::diskann::Binding::capture(
read,
&self.index.table,
&self.index.field,
self.index.dimensions,
index,
control,
)
})
.transpose()?;
let mut prefixes = uqa_core::memory::BudgetedVec::new(control.memory());
prefixes.extend_from_slice(&[&*vectors, &*origins, &*changes, &*marker])?;
prefixes.extend_from_slice(&[&populations[0], &populations[1]])?;
if let Some(binding) = &binding {
prefixes.extend_from_slice(&binding.prefixes())?;
prefixes.push(crate::key_value::diskann::READ_PREFIX)?;
}
let mut source = RetainedDiskANNCanonical::new(
read.retain(&prefixes)?,
&vectors,
&origins,
&changes,
self.index.dimensions,
control,
)?;
drop(prefixes);
source.binding = binding;
selected = Some(source);
Ok(())
})?;
selected.ok_or_else(|| invalid("canonical read did not execute"))
}
fn workspace_bytes(&self, writing: bool) -> StorageBackendResult<usize> {
self.index
.table
.len()
.checked_add(self.index.field.len())
.and_then(|bytes| bytes.checked_add(ROOT.len() + 128))
.and_then(|bytes| bytes.checked_mul(8))
.and_then(|bytes| {
bytes.checked_add(
usize::try_from(if writing { self.index.dimensions } else { 0 })
.ok()?
.checked_mul(4)?,
)
})
.ok_or_else(|| uqa_core::memory::MemoryError::SizeOverflow.into())
}
}
pub(in crate::key_value) fn table_prefix(table: &str) -> StorageBackendResult<Vec<u8>> {
Ok(namespaced(&codec::vector_key_prefix(table)?))
}
pub(in crate::key_value) fn prefix(table: &str, field: &str) -> StorageBackendResult<Vec<u8>> {
Ok(namespaced(&codec::vector_field_prefix(table, field)?))
}
fn namespaced(canonical: &[u8]) -> Vec<u8> {
let mut prefix = Vec::with_capacity(ROOT.len() + canonical.len());
prefix.extend_from_slice(ROOT);
prefix.extend_from_slice(canonical);
prefix
}
pub(super) fn key(table: &str, field: &str, document: DocId) -> StorageBackendResult<Vec<u8>> {
let mut key = prefix(table, field)?;
key.extend_from_slice(&document.to_be_bytes());
Ok(key)
}
fn invalid(message: &'static str) -> crate::StorageBackendError {
VersionError::InvalidEncoding(message).into_storage_error()
}