uqa_storage/diskann_index/build/
capture.rs1use std::path::Path;
10
11use super::{
12 invalid, DiskANNBuildInput, DiskANNBuildSink, DiskANNGenerationOptions, DiskANNMergedGraph,
13 DiskANNTemporaryBudget,
14};
15use crate::diskann_index::{
16 format::{
17 DiskANNBuildCoverage, DiskANNChangeIdentity, DiskANNGeneration, DiskANNManifest,
18 DiskANNOriginSummary,
19 },
20 DiskANNCanonicalRead,
21};
22use crate::{read_control::StorageReadControl, StorageBackendResult};
23
24mod origins;
25#[cfg(test)]
26mod tests;
27
28pub struct DiskANNBuildCapture<S> {
30 input: DiskANNBuildInput,
31 source: S,
32 origins: origins::Origins,
33}
34
35impl<S: DiskANNCanonicalRead> DiskANNBuildCapture<S> {
36 pub fn capture(
37 generation: DiskANNGeneration,
38 source: S,
39 directory: &Path,
40 temporary: &DiskANNTemporaryBudget,
41 control: &StorageReadControl,
42 ) -> StorageBackendResult<Self> {
43 source.check_control(control)?;
44 let mut origins = origins::Origins::new(directory, temporary, control)?;
45 let input = DiskANNBuildInput::capture(
46 generation,
47 source.dimensions(),
48 directory,
49 temporary,
50 control,
51 |visit| origins.capture(&source, control, visit),
52 )?;
53 source.check_control(control)?;
54 Ok(Self {
55 input,
56 source,
57 origins,
58 })
59 }
60
61 pub fn input(&self) -> &DiskANNBuildInput {
62 &self.input
63 }
64
65 pub fn write_generation(
67 &self,
68 graph: &DiskANNMergedGraph,
69 options: DiskANNGenerationOptions,
70 sink: &mut dyn DiskANNBuildSink,
71 ) -> StorageBackendResult<DiskANNManifest> {
72 let control = &self.input.control;
73 self.source.check_control(control)?;
74 control.check_value_size(DiskANNManifest::MAX_ENCODED_BYTES, options.max_record_bytes)?;
75 self.origins.check_record_limit(
76 self.input.coverage().generation(),
77 self.input.dimensions(),
78 options.max_record_bytes,
79 control,
80 )?;
81 let manifest = self.input.write_generation(graph, options, sink)?;
82 self.origins.write(
83 self.input.coverage().generation(),
84 self.input.dimensions(),
85 sink,
86 options.max_record_bytes,
87 control,
88 )?;
89 self.source.check_control(control)?;
90 manifest.with_origins(self.origins.summary())
91 }
92
93 pub fn finish(
95 self,
96 manifest: &DiskANNManifest,
97 control: &StorageReadControl,
98 ) -> StorageBackendResult<DiskANNCanonicalCoverage<S>> {
99 self.input.control.check()?;
100 self.source.check_control(control)?;
101 let actual = manifest.input();
102 if actual.coverage != self.input.coverage()
103 || actual.nodes != self.input.node_count()
104 || actual.side_vectors != self.input.side_count()
105 || manifest
106 .origins()
107 .is_some_and(|origins| origins != self.origins.summary())
108 {
109 return Err(invalid(
110 "manifest does not match the retained build capture",
111 ));
112 }
113 control.check()?;
114 Ok(DiskANNCanonicalCoverage {
115 source: self.source,
116 fingerprint: self.input.coverage(),
117 control: self.input.control.clone(),
118 origins: self.origins.summary(),
119 manifest: *manifest,
120 })
121 }
122}
123
124pub struct DiskANNCanonicalCoverage<S> {
126 source: S,
127 fingerprint: DiskANNBuildCoverage,
128 control: StorageReadControl,
129 origins: DiskANNOriginSummary,
130 manifest: DiskANNManifest,
131}
132
133impl<S: DiskANNCanonicalRead> DiskANNCanonicalCoverage<S> {
134 pub fn check_control(&self, control: &StorageReadControl) -> StorageBackendResult<()> {
136 self.control.check()?;
137 self.source.check_control(control)
138 }
139
140 pub fn manifest(&self) -> &DiskANNManifest {
142 &self.manifest
143 }
144
145 pub fn fingerprint(&self) -> DiskANNBuildCoverage {
146 self.fingerprint
147 }
148
149 pub fn origins(&self) -> DiskANNOriginSummary {
151 self.origins
152 }
153
154 pub fn source(&self) -> &S {
156 &self.source
157 }
158
159 pub fn contains(
161 &self,
162 change: DiskANNChangeIdentity,
163 control: &StorageReadControl,
164 ) -> StorageBackendResult<bool> {
165 self.control.check()?;
166 self.source.check_control(control)?;
167 let actual = self.source.origin(change.document(), control)?;
168 self.control.check()?;
169 self.source.check_control(control)?;
170 Ok(actual == Some(change.version()))
171 }
172}