use super::{identity::validate_mapping, invalid, publication, KeyValueDiskANNSource};
use crate::diskann_index::{
catalog::DiskANNIndexScope,
changes::{DiskANNChangeJournal, DiskANNChangeRead, DiskANNPruneRequest, DiskANNPruneResult},
maintenance::{DiskANNStatisticsPage, DiskANNStatisticsRequest},
pages::DiskANNOriginReader,
};
use crate::key_value::KeyValueRead;
use crate::{read_control::StorageReadControl, KeyValueBatch, StorageBackendResult};
use std::sync::Arc;
pub struct KeyValueDiskANNPruner {
source: Arc<KeyValueDiskANNSource>,
origins: DiskANNOriginReader,
control: StorageReadControl,
}
impl KeyValueDiskANNPruner {
pub fn statistics(
&self,
journal: &dyn DiskANNChangeRead,
request: DiskANNStatisticsRequest,
control: &StorageReadControl,
) -> StorageBackendResult<DiskANNStatisticsPage> {
self.control.check()?;
let page =
crate::diskann_index::maintenance::measure(&self.origins, journal, request, control)?;
self.control.check()?;
Ok(page)
}
pub fn open(
source: Arc<KeyValueDiskANNSource>,
maximum: usize,
control: &StorageReadControl,
) -> StorageBackendResult<Self> {
let origins = DiskANNOriginReader::open(source.clone(), maximum, control)?;
Ok(Self {
source,
origins,
control: control.clone(),
})
}
pub fn require_selected(
&self,
scope: &DiskANNIndexScope,
dimensions: u32,
parameters: crate::vector_index::DiskANNIndexParams,
read: &dyn KeyValueRead,
batch: &mut dyn KeyValueBatch,
control: &StorageReadControl,
) -> StorageBackendResult<()> {
self.control.check()?;
control.check()?;
let input = self.origins.manifest().input();
if input.dimensions != dimensions || input.parameters != parameters {
return Err(invalid(
"selected generation differs from the current index configuration",
));
}
let generation = input.generation;
self.source.check(control)?;
validate_mapping(scope, generation, &*self.source.read, control)?;
if publication::selected_generation(scope, read, control)? != Some(generation) {
return Err(invalid("journal pruning requires the selected generation"));
}
let key = publication::head_key(scope);
if read
.record_revision(&key)?
.is_none_or(|revision| revision.has_private_changes())
{
return Err(invalid("journal pruning requires a committed head"));
}
batch.require_unchanged(&key)?;
self.control.check()?;
control.check()
}
pub fn prune(
&self,
journal: &mut dyn DiskANNChangeJournal,
request: DiskANNPruneRequest,
control: &StorageReadControl,
) -> StorageBackendResult<DiskANNPruneResult> {
self.control.check()?;
let result =
crate::diskann_index::changes::prune(&self.origins, journal, request, control)?;
self.control.check()?;
Ok(result)
}
}