pub struct SnapshotEngine { /* private fields */ }Expand description
A ref-free engine for deterministic immutable collection roots.
The engine writes and reads Git objects but never creates a commit, updates a ref, consults repository history, or reads the clock. Callers are responsible for retaining returned roots, for example through an external content store or the named collection adapter.
Implementations§
Source§impl SnapshotEngine
impl SnapshotEngine
Sourcepub fn open(path: impl AsRef<Path>) -> Result<Self>
pub fn open(path: impl AsRef<Path>) -> Result<Self>
Opens an existing bare or non-bare Git object database without reading a collection ref.
Examples found in repository?
61fn mutate(
62 input: &Path,
63 repository: &Path,
64 build_report: &Path,
65 fraction: f64,
66 output: &Path,
67) -> Result<(), Box<dyn std::error::Error>> {
68 let spec = read_spec(input)?;
69 if !(0.0..=1.0).contains(&fraction) || fraction == 0.0 {
70 return Err("mutation fraction must be greater than zero and at most one".into());
71 }
72 let vectors = read_vectors(&spec.points_path, spec.point_count, spec.dimension)?;
73 let points = make_points(&vectors);
74 let count = ((spec.point_count as f64 * fraction).round() as usize).clamp(1, spec.point_count);
75 let mut changed = points[..count].to_vec();
76 for point in &mut changed {
77 point.vector[0] += 0.001;
78 }
79 let build: serde_json::Value = serde_json::from_slice(&fs::read(build_report)?)?;
80 let root = build
81 .get("root")
82 .and_then(serde_json::Value::as_str)
83 .ok_or("build report root is missing")?;
84 let engine = SnapshotEngine::open(repository)?;
85
86 let upsert_started = Instant::now();
87 let upserted = engine.apply(
88 root,
89 changed.into_iter().map(SnapshotMutation::upsert).collect(),
90 )?;
91 let upsert_us = micros(upsert_started);
92 let delete_started = Instant::now();
93 let deleted = engine.apply(
94 root,
95 vec![SnapshotMutation::delete_ids(
96 (0..count).map(|id| PointId::from(id as u64)),
97 )],
98 )?;
99 let delete_us = micros(delete_started);
100 fs::write(
101 output,
102 serde_json::to_vec_pretty(&json!({
103 "schema_version": 1,
104 "root": root,
105 "fraction": fraction,
106 "points": count,
107 "upsert_us": upsert_us,
108 "delete_us": delete_us,
109 "upsert_root": upserted.root(),
110 "delete_root": deleted.root(),
111 "on_disk_bytes_after": directory_bytes(repository)?,
112 }))?,
113 )?;
114 Ok(())
115}
116
117fn validate(
118 repository: &Path,
119 build_report: &Path,
120 output: &Path,
121) -> Result<(), Box<dyn std::error::Error>> {
122 let build: serde_json::Value = serde_json::from_slice(&fs::read(build_report)?)?;
123 let root = build
124 .get("root")
125 .and_then(serde_json::Value::as_str)
126 .ok_or("build report root is missing")?;
127 let engine = SnapshotEngine::open(repository)?;
128 let started = Instant::now();
129 let report = engine.validate(root, true)?;
130 fs::write(
131 output,
132 serde_json::to_vec_pretty(&json!({
133 "schema_version": 1,
134 "root": root,
135 "validation_us": micros(started),
136 "report": report,
137 }))?,
138 )?;
139 Ok(())
140}
141
142fn build(input: &Path, repository: &Path, output: &Path) -> Result<(), Box<dyn std::error::Error>> {
143 if repository.exists() {
144 return Err(format!("repository already exists: {}", repository.display()).into());
145 }
146 let spec = read_spec(input)?;
147 let vectors = read_vectors(&spec.points_path, spec.point_count, spec.dimension)?;
148 let points = make_points(&vectors);
149 let config = CollectionConfig {
150 dimension: spec.dimension,
151 ..CollectionConfig::default()
152 };
153 let engine = SnapshotEngine::init(repository)?;
154 let started = Instant::now();
155 let snapshot = engine.build(config, points)?;
156 let build_us = micros(started);
157 fs::write(
158 output,
159 serde_json::to_vec_pretty(&json!({
160 "schema_version": 1,
161 "root": snapshot.root(),
162 "build_us": build_us,
163 "on_disk_bytes": directory_bytes(repository)?,
164 }))?,
165 )?;
166 Ok(())
167}
168
169fn query(
170 input: &Path,
171 repository: &Path,
172 build_report: &Path,
173 mode: &str,
174 output: &Path,
175) -> Result<(), Box<dyn std::error::Error>> {
176 let (exact, warm_exact) = match mode {
177 "exact" => (true, false),
178 "approximate" => (false, false),
179 "approximate-after-exact" => (false, true),
180 _ => return Err(format!("unsupported query mode: {mode}").into()),
181 };
182 let spec = read_spec(input)?;
183 let queries = read_vectors(&spec.queries_path, spec.query_count, spec.dimension)?;
184 let maximum_k = *spec.k.iter().max().ok_or("k must not be empty")?;
185 let build: serde_json::Value = serde_json::from_slice(&fs::read(build_report)?)?;
186 let root = build
187 .get("root")
188 .and_then(serde_json::Value::as_str)
189 .ok_or("build report root is missing")?;
190 let engine = SnapshotEngine::open(repository)?;
191 let snapshot = engine.open_snapshot(root)?;
192
193 let cache_build_us = if warm_exact {
194 let started = Instant::now();
195 snapshot.query(make_query(&queries[0], maximum_k, true))?;
196 Some(micros(started))
197 } else {
198 None
199 };
200 // Fill the immutable snapshot cache, construct an approximate lookup over an
201 // already-warm exact view, or warm the unchanged approximate ODB path without
202 // including that one-time work in the samples.
203 let warmup_started = Instant::now();
204 snapshot.query(make_query(&queries[0], maximum_k, exact))?;
205 let warmup_us = micros(warmup_started);
206 wait_for_profiler()?;
207
208 let mut query_us = Vec::with_capacity(queries.len());
209 let mut results = Vec::with_capacity(queries.len());
210 let mut vectors_scored = Vec::with_capacity(queries.len());
211 let batch_started = Instant::now();
212 for vector in &queries {
213 let started = Instant::now();
214 let result = snapshot.query(make_query(vector, maximum_k, exact))?;
215 query_us.push(micros(started));
216 vectors_scored.push(result.stats.vectors_scored);
217 results.push(result.points);
218 }
219 let batch_us = micros(batch_started);
220 fs::write(
221 output,
222 serde_json::to_vec_pretty(&json!({
223 "schema_version": 1,
224 "root": root,
225 "mode": mode,
226 "cache_build_us": cache_build_us,
227 "warmup_us": warmup_us,
228 "query_us": query_us,
229 "batch_us": batch_us,
230 "vectors_scored": vectors_scored,
231 "results": results,
232 }))?,
233 )?;
234 Ok(())
235}Sourcepub fn init(path: impl AsRef<Path>) -> Result<Self>
pub fn init(path: impl AsRef<Path>) -> Result<Self>
Initializes a bare Git object database for immutable snapshots.
Examples found in repository?
142fn build(input: &Path, repository: &Path, output: &Path) -> Result<(), Box<dyn std::error::Error>> {
143 if repository.exists() {
144 return Err(format!("repository already exists: {}", repository.display()).into());
145 }
146 let spec = read_spec(input)?;
147 let vectors = read_vectors(&spec.points_path, spec.point_count, spec.dimension)?;
148 let points = make_points(&vectors);
149 let config = CollectionConfig {
150 dimension: spec.dimension,
151 ..CollectionConfig::default()
152 };
153 let engine = SnapshotEngine::init(repository)?;
154 let started = Instant::now();
155 let snapshot = engine.build(config, points)?;
156 let build_us = micros(started);
157 fs::write(
158 output,
159 serde_json::to_vec_pretty(&json!({
160 "schema_version": 1,
161 "root": snapshot.root(),
162 "build_us": build_us,
163 "on_disk_bytes": directory_bytes(repository)?,
164 }))?,
165 )?;
166 Ok(())
167}More examples
28fn main() -> Result<(), Box<dyn std::error::Error>> {
29 let mut args = env::args_os().skip(1);
30 let input = args.next().ok_or("usage: runner INPUT.json OUTPUT.json")?;
31 let output = args.next().ok_or("usage: runner INPUT.json OUTPUT.json")?;
32 if args.next().is_some() {
33 return Err("usage: runner INPUT.json OUTPUT.json".into());
34 }
35 let spec: RunSpec = serde_json::from_slice(&fs::read(input)?)?;
36 if spec.schema_version != 1 {
37 return Err(format!("unsupported harness schema version {}", spec.schema_version).into());
38 }
39 let vectors = read_vectors(&spec.points_path, spec.point_count, spec.dimension)?;
40 let queries = read_vectors(&spec.queries_path, spec.query_count, spec.dimension)?;
41 let points = make_points(&vectors);
42 let maximum_k = *spec.k.iter().max().ok_or("k must not be empty")?;
43 let config = CollectionConfig {
44 dimension: spec.dimension,
45 ..CollectionConfig::default()
46 };
47
48 let setup_started = Instant::now();
49 let core_dir = tempfile::TempDir::new()?;
50 let engine = SnapshotEngine::init(core_dir.path())?;
51 let setup_us = micros(setup_started);
52 let build_started = Instant::now();
53 let snapshot = engine.build(config.clone(), points.clone())?;
54 let build_us = micros(build_started);
55 let baseline_on_disk_bytes = directory_bytes(core_dir.path())?;
56
57 let (exact_query_us, exact_results, exact_vectors_scored) =
58 query_all(&snapshot, &queries, maximum_k, true, None)?;
59 let (approximate_query_us, approximate_results, approximate_vectors_scored) =
60 query_all(&snapshot, &queries, maximum_k, false, None)?;
61 let snapshot_throughput = json!({
62 "exact": query_snapshot_throughput(
63 &snapshot,
64 &queries,
65 maximum_k,
66 true,
67 &spec.concurrency,
68 )?,
69 "approximate": query_snapshot_throughput(
70 &snapshot,
71 &queries,
72 maximum_k,
73 false,
74 &spec.concurrency,
75 )?,
76 });
77
78 let mut filtered = Map::new();
79 for selectivity in &spec.filter_selectivities {
80 let filter = selectivity_filter(*selectivity);
81 let (exact_us, exact, exact_scored) =
82 query_all(&snapshot, &queries, maximum_k, true, Some(filter.clone()))?;
83 let (approximate_us, approximate, approximate_scored) =
84 query_all(&snapshot, &queries, maximum_k, false, Some(filter))?;
85 filtered.insert(
86 selectivity.to_string(),
87 json!({
88 "exact_query_us": exact_us,
89 "approximate_query_us": approximate_us,
90 "exact_results": exact,
91 "approximate_results": approximate,
92 "exact_vectors_scored": exact_scored,
93 "approximate_vectors_scored": approximate_scored,
94 }),
95 );
96 }
97
98 let mut mutations = Map::new();
99 for fraction in &spec.mutation_fractions {
100 let count = fraction_count(spec.point_count, *fraction);
101 let changed = changed_points(&points[..count]);
102 let upsert_started = Instant::now();
103 let upserted = engine.apply(
104 snapshot.root(),
105 changed.into_iter().map(SnapshotMutation::upsert).collect(),
106 )?;
107 let upsert_us = micros(upsert_started);
108 let delete_started = Instant::now();
109 let deleted = engine.apply(
110 snapshot.root(),
111 vec![SnapshotMutation::delete_ids(
112 (0..count).map(|id| PointId::from(id as u64)),
113 )],
114 )?;
115 let delete_us = micros(delete_started);
116 mutations.insert(
117 fraction.to_string(),
118 json!({
119 "points": count,
120 "upsert_us": upsert_us,
121 "delete_us": delete_us,
122 "upsert_root": upserted.root(),
123 "delete_root": deleted.root(),
124 }),
125 );
126 }
127
128 let adapter_setup_started = Instant::now();
129 let adapter_dir = tempfile::TempDir::new()?;
130 let database = Database::init_bare(adapter_dir.path())?;
131 let collection = database.create_collection("benchmark", config)?;
132 let adapter_setup_us = micros(adapter_setup_started);
133 let adapter_build_started = Instant::now();
134 let adapter_root = collection.upsert(points)?.root;
135 let adapter_build_us = micros(adapter_build_started);
136 if adapter_root != snapshot.root() {
137 return Err("snapshot-core and named-adapter roots differ".into());
138 }
139 query_collection_all(&collection, &queries[..1], maximum_k, true, None)?;
140 query_collection_all(&collection, &queries[..1], maximum_k, false, None)?;
141 let (adapter_exact_query_us, adapter_exact_results, adapter_exact_vectors_scored) =
142 query_collection_all(&collection, &queries, maximum_k, true, None)?;
143 let (
144 adapter_approximate_query_us,
145 adapter_approximate_results,
146 adapter_approximate_vectors_scored,
147 ) = query_collection_all(&collection, &queries, maximum_k, false, None)?;
148 let adapter_throughput = json!({
149 "exact": query_collection_throughput(
150 &collection,
151 &queries,
152 maximum_k,
153 true,
154 &spec.concurrency,
155 )?,
156 "approximate": query_collection_throughput(
157 &collection,
158 &queries,
159 maximum_k,
160 false,
161 &spec.concurrency,
162 )?,
163 });
164 let historical_started = Instant::now();
165 let historical = collection.at(&adapter_root)?;
166 let historical_count = historical.count(None)?.count;
167 let historical_read_us = micros(historical_started);
168
169 let report = json!({
170 "schema_version": 1,
171 "engine": "git-vdb",
172 "case_name": spec.case_name,
173 "point_count": spec.point_count,
174 "dimension": spec.dimension,
175 "query_count": spec.query_count,
176 "k": spec.k,
177 "root": snapshot.root(),
178 "setup_us": setup_us,
179 "snapshot_core": {
180 "build_us": build_us,
181 "exact_query_us": exact_query_us,
182 "approximate_query_us": approximate_query_us,
183 "exact_results": exact_results,
184 "approximate_results": approximate_results,
185 "exact_vectors_scored": exact_vectors_scored,
186 "approximate_vectors_scored": approximate_vectors_scored,
187 "throughput": snapshot_throughput,
188 "filtered": filtered,
189 "mutations": mutations,
190 "on_disk_bytes": baseline_on_disk_bytes,
191 },
192 "named_adapter": {
193 "setup_us": adapter_setup_us,
194 "build_us": adapter_build_us,
195 "exact_query_us": adapter_exact_query_us,
196 "approximate_query_us": adapter_approximate_query_us,
197 "exact_results": adapter_exact_results,
198 "approximate_results": adapter_approximate_results,
199 "exact_vectors_scored": adapter_exact_vectors_scored,
200 "approximate_vectors_scored": adapter_approximate_vectors_scored,
201 "throughput": adapter_throughput,
202 "historical_read_us": historical_read_us,
203 "historical_count": historical_count,
204 "on_disk_bytes": directory_bytes(adapter_dir.path())?,
205 }
206 });
207 fs::write(output, serde_json::to_vec_pretty(&report)?)?;
208 Ok(())
209}Sourcepub fn ephemeral() -> Result<Self>
pub fn ephemeral() -> Result<Self>
Creates an isolated temporary object database whose lifetime is retained by the engine and snapshots returned from it.
Sourcepub fn build(
&self,
config: CollectionConfig,
points: Vec<Point>,
) -> Result<Snapshot>
pub fn build( &self, config: CollectionConfig, points: Vec<Point>, ) -> Result<Snapshot>
Builds a canonical root from a complete point set.
Examples found in repository?
142fn build(input: &Path, repository: &Path, output: &Path) -> Result<(), Box<dyn std::error::Error>> {
143 if repository.exists() {
144 return Err(format!("repository already exists: {}", repository.display()).into());
145 }
146 let spec = read_spec(input)?;
147 let vectors = read_vectors(&spec.points_path, spec.point_count, spec.dimension)?;
148 let points = make_points(&vectors);
149 let config = CollectionConfig {
150 dimension: spec.dimension,
151 ..CollectionConfig::default()
152 };
153 let engine = SnapshotEngine::init(repository)?;
154 let started = Instant::now();
155 let snapshot = engine.build(config, points)?;
156 let build_us = micros(started);
157 fs::write(
158 output,
159 serde_json::to_vec_pretty(&json!({
160 "schema_version": 1,
161 "root": snapshot.root(),
162 "build_us": build_us,
163 "on_disk_bytes": directory_bytes(repository)?,
164 }))?,
165 )?;
166 Ok(())
167}More examples
28fn main() -> Result<(), Box<dyn std::error::Error>> {
29 let mut args = env::args_os().skip(1);
30 let input = args.next().ok_or("usage: runner INPUT.json OUTPUT.json")?;
31 let output = args.next().ok_or("usage: runner INPUT.json OUTPUT.json")?;
32 if args.next().is_some() {
33 return Err("usage: runner INPUT.json OUTPUT.json".into());
34 }
35 let spec: RunSpec = serde_json::from_slice(&fs::read(input)?)?;
36 if spec.schema_version != 1 {
37 return Err(format!("unsupported harness schema version {}", spec.schema_version).into());
38 }
39 let vectors = read_vectors(&spec.points_path, spec.point_count, spec.dimension)?;
40 let queries = read_vectors(&spec.queries_path, spec.query_count, spec.dimension)?;
41 let points = make_points(&vectors);
42 let maximum_k = *spec.k.iter().max().ok_or("k must not be empty")?;
43 let config = CollectionConfig {
44 dimension: spec.dimension,
45 ..CollectionConfig::default()
46 };
47
48 let setup_started = Instant::now();
49 let core_dir = tempfile::TempDir::new()?;
50 let engine = SnapshotEngine::init(core_dir.path())?;
51 let setup_us = micros(setup_started);
52 let build_started = Instant::now();
53 let snapshot = engine.build(config.clone(), points.clone())?;
54 let build_us = micros(build_started);
55 let baseline_on_disk_bytes = directory_bytes(core_dir.path())?;
56
57 let (exact_query_us, exact_results, exact_vectors_scored) =
58 query_all(&snapshot, &queries, maximum_k, true, None)?;
59 let (approximate_query_us, approximate_results, approximate_vectors_scored) =
60 query_all(&snapshot, &queries, maximum_k, false, None)?;
61 let snapshot_throughput = json!({
62 "exact": query_snapshot_throughput(
63 &snapshot,
64 &queries,
65 maximum_k,
66 true,
67 &spec.concurrency,
68 )?,
69 "approximate": query_snapshot_throughput(
70 &snapshot,
71 &queries,
72 maximum_k,
73 false,
74 &spec.concurrency,
75 )?,
76 });
77
78 let mut filtered = Map::new();
79 for selectivity in &spec.filter_selectivities {
80 let filter = selectivity_filter(*selectivity);
81 let (exact_us, exact, exact_scored) =
82 query_all(&snapshot, &queries, maximum_k, true, Some(filter.clone()))?;
83 let (approximate_us, approximate, approximate_scored) =
84 query_all(&snapshot, &queries, maximum_k, false, Some(filter))?;
85 filtered.insert(
86 selectivity.to_string(),
87 json!({
88 "exact_query_us": exact_us,
89 "approximate_query_us": approximate_us,
90 "exact_results": exact,
91 "approximate_results": approximate,
92 "exact_vectors_scored": exact_scored,
93 "approximate_vectors_scored": approximate_scored,
94 }),
95 );
96 }
97
98 let mut mutations = Map::new();
99 for fraction in &spec.mutation_fractions {
100 let count = fraction_count(spec.point_count, *fraction);
101 let changed = changed_points(&points[..count]);
102 let upsert_started = Instant::now();
103 let upserted = engine.apply(
104 snapshot.root(),
105 changed.into_iter().map(SnapshotMutation::upsert).collect(),
106 )?;
107 let upsert_us = micros(upsert_started);
108 let delete_started = Instant::now();
109 let deleted = engine.apply(
110 snapshot.root(),
111 vec![SnapshotMutation::delete_ids(
112 (0..count).map(|id| PointId::from(id as u64)),
113 )],
114 )?;
115 let delete_us = micros(delete_started);
116 mutations.insert(
117 fraction.to_string(),
118 json!({
119 "points": count,
120 "upsert_us": upsert_us,
121 "delete_us": delete_us,
122 "upsert_root": upserted.root(),
123 "delete_root": deleted.root(),
124 }),
125 );
126 }
127
128 let adapter_setup_started = Instant::now();
129 let adapter_dir = tempfile::TempDir::new()?;
130 let database = Database::init_bare(adapter_dir.path())?;
131 let collection = database.create_collection("benchmark", config)?;
132 let adapter_setup_us = micros(adapter_setup_started);
133 let adapter_build_started = Instant::now();
134 let adapter_root = collection.upsert(points)?.root;
135 let adapter_build_us = micros(adapter_build_started);
136 if adapter_root != snapshot.root() {
137 return Err("snapshot-core and named-adapter roots differ".into());
138 }
139 query_collection_all(&collection, &queries[..1], maximum_k, true, None)?;
140 query_collection_all(&collection, &queries[..1], maximum_k, false, None)?;
141 let (adapter_exact_query_us, adapter_exact_results, adapter_exact_vectors_scored) =
142 query_collection_all(&collection, &queries, maximum_k, true, None)?;
143 let (
144 adapter_approximate_query_us,
145 adapter_approximate_results,
146 adapter_approximate_vectors_scored,
147 ) = query_collection_all(&collection, &queries, maximum_k, false, None)?;
148 let adapter_throughput = json!({
149 "exact": query_collection_throughput(
150 &collection,
151 &queries,
152 maximum_k,
153 true,
154 &spec.concurrency,
155 )?,
156 "approximate": query_collection_throughput(
157 &collection,
158 &queries,
159 maximum_k,
160 false,
161 &spec.concurrency,
162 )?,
163 });
164 let historical_started = Instant::now();
165 let historical = collection.at(&adapter_root)?;
166 let historical_count = historical.count(None)?.count;
167 let historical_read_us = micros(historical_started);
168
169 let report = json!({
170 "schema_version": 1,
171 "engine": "git-vdb",
172 "case_name": spec.case_name,
173 "point_count": spec.point_count,
174 "dimension": spec.dimension,
175 "query_count": spec.query_count,
176 "k": spec.k,
177 "root": snapshot.root(),
178 "setup_us": setup_us,
179 "snapshot_core": {
180 "build_us": build_us,
181 "exact_query_us": exact_query_us,
182 "approximate_query_us": approximate_query_us,
183 "exact_results": exact_results,
184 "approximate_results": approximate_results,
185 "exact_vectors_scored": exact_vectors_scored,
186 "approximate_vectors_scored": approximate_vectors_scored,
187 "throughput": snapshot_throughput,
188 "filtered": filtered,
189 "mutations": mutations,
190 "on_disk_bytes": baseline_on_disk_bytes,
191 },
192 "named_adapter": {
193 "setup_us": adapter_setup_us,
194 "build_us": adapter_build_us,
195 "exact_query_us": adapter_exact_query_us,
196 "approximate_query_us": adapter_approximate_query_us,
197 "exact_results": adapter_exact_results,
198 "approximate_results": adapter_approximate_results,
199 "exact_vectors_scored": adapter_exact_vectors_scored,
200 "approximate_vectors_scored": adapter_approximate_vectors_scored,
201 "throughput": adapter_throughput,
202 "historical_read_us": historical_read_us,
203 "historical_count": historical_count,
204 "on_disk_bytes": directory_bytes(adapter_dir.path())?,
205 }
206 });
207 fs::write(output, serde_json::to_vec_pretty(&report)?)?;
208 Ok(())
209}Sourcepub fn apply(
&self,
previous_root: impl AsRef<str>,
mutations: Vec<SnapshotMutation>,
) -> Result<Snapshot>
pub fn apply( &self, previous_root: impl AsRef<str>, mutations: Vec<SnapshotMutation>, ) -> Result<Snapshot>
Applies an ordered mutation batch to a root and returns the new root.
Mutation order is significant. Duplicate upserts for the same typed ID within one call are rejected. No ref or commit is created.
Examples found in repository?
61fn mutate(
62 input: &Path,
63 repository: &Path,
64 build_report: &Path,
65 fraction: f64,
66 output: &Path,
67) -> Result<(), Box<dyn std::error::Error>> {
68 let spec = read_spec(input)?;
69 if !(0.0..=1.0).contains(&fraction) || fraction == 0.0 {
70 return Err("mutation fraction must be greater than zero and at most one".into());
71 }
72 let vectors = read_vectors(&spec.points_path, spec.point_count, spec.dimension)?;
73 let points = make_points(&vectors);
74 let count = ((spec.point_count as f64 * fraction).round() as usize).clamp(1, spec.point_count);
75 let mut changed = points[..count].to_vec();
76 for point in &mut changed {
77 point.vector[0] += 0.001;
78 }
79 let build: serde_json::Value = serde_json::from_slice(&fs::read(build_report)?)?;
80 let root = build
81 .get("root")
82 .and_then(serde_json::Value::as_str)
83 .ok_or("build report root is missing")?;
84 let engine = SnapshotEngine::open(repository)?;
85
86 let upsert_started = Instant::now();
87 let upserted = engine.apply(
88 root,
89 changed.into_iter().map(SnapshotMutation::upsert).collect(),
90 )?;
91 let upsert_us = micros(upsert_started);
92 let delete_started = Instant::now();
93 let deleted = engine.apply(
94 root,
95 vec![SnapshotMutation::delete_ids(
96 (0..count).map(|id| PointId::from(id as u64)),
97 )],
98 )?;
99 let delete_us = micros(delete_started);
100 fs::write(
101 output,
102 serde_json::to_vec_pretty(&json!({
103 "schema_version": 1,
104 "root": root,
105 "fraction": fraction,
106 "points": count,
107 "upsert_us": upsert_us,
108 "delete_us": delete_us,
109 "upsert_root": upserted.root(),
110 "delete_root": deleted.root(),
111 "on_disk_bytes_after": directory_bytes(repository)?,
112 }))?,
113 )?;
114 Ok(())
115}More examples
28fn main() -> Result<(), Box<dyn std::error::Error>> {
29 let mut args = env::args_os().skip(1);
30 let input = args.next().ok_or("usage: runner INPUT.json OUTPUT.json")?;
31 let output = args.next().ok_or("usage: runner INPUT.json OUTPUT.json")?;
32 if args.next().is_some() {
33 return Err("usage: runner INPUT.json OUTPUT.json".into());
34 }
35 let spec: RunSpec = serde_json::from_slice(&fs::read(input)?)?;
36 if spec.schema_version != 1 {
37 return Err(format!("unsupported harness schema version {}", spec.schema_version).into());
38 }
39 let vectors = read_vectors(&spec.points_path, spec.point_count, spec.dimension)?;
40 let queries = read_vectors(&spec.queries_path, spec.query_count, spec.dimension)?;
41 let points = make_points(&vectors);
42 let maximum_k = *spec.k.iter().max().ok_or("k must not be empty")?;
43 let config = CollectionConfig {
44 dimension: spec.dimension,
45 ..CollectionConfig::default()
46 };
47
48 let setup_started = Instant::now();
49 let core_dir = tempfile::TempDir::new()?;
50 let engine = SnapshotEngine::init(core_dir.path())?;
51 let setup_us = micros(setup_started);
52 let build_started = Instant::now();
53 let snapshot = engine.build(config.clone(), points.clone())?;
54 let build_us = micros(build_started);
55 let baseline_on_disk_bytes = directory_bytes(core_dir.path())?;
56
57 let (exact_query_us, exact_results, exact_vectors_scored) =
58 query_all(&snapshot, &queries, maximum_k, true, None)?;
59 let (approximate_query_us, approximate_results, approximate_vectors_scored) =
60 query_all(&snapshot, &queries, maximum_k, false, None)?;
61 let snapshot_throughput = json!({
62 "exact": query_snapshot_throughput(
63 &snapshot,
64 &queries,
65 maximum_k,
66 true,
67 &spec.concurrency,
68 )?,
69 "approximate": query_snapshot_throughput(
70 &snapshot,
71 &queries,
72 maximum_k,
73 false,
74 &spec.concurrency,
75 )?,
76 });
77
78 let mut filtered = Map::new();
79 for selectivity in &spec.filter_selectivities {
80 let filter = selectivity_filter(*selectivity);
81 let (exact_us, exact, exact_scored) =
82 query_all(&snapshot, &queries, maximum_k, true, Some(filter.clone()))?;
83 let (approximate_us, approximate, approximate_scored) =
84 query_all(&snapshot, &queries, maximum_k, false, Some(filter))?;
85 filtered.insert(
86 selectivity.to_string(),
87 json!({
88 "exact_query_us": exact_us,
89 "approximate_query_us": approximate_us,
90 "exact_results": exact,
91 "approximate_results": approximate,
92 "exact_vectors_scored": exact_scored,
93 "approximate_vectors_scored": approximate_scored,
94 }),
95 );
96 }
97
98 let mut mutations = Map::new();
99 for fraction in &spec.mutation_fractions {
100 let count = fraction_count(spec.point_count, *fraction);
101 let changed = changed_points(&points[..count]);
102 let upsert_started = Instant::now();
103 let upserted = engine.apply(
104 snapshot.root(),
105 changed.into_iter().map(SnapshotMutation::upsert).collect(),
106 )?;
107 let upsert_us = micros(upsert_started);
108 let delete_started = Instant::now();
109 let deleted = engine.apply(
110 snapshot.root(),
111 vec![SnapshotMutation::delete_ids(
112 (0..count).map(|id| PointId::from(id as u64)),
113 )],
114 )?;
115 let delete_us = micros(delete_started);
116 mutations.insert(
117 fraction.to_string(),
118 json!({
119 "points": count,
120 "upsert_us": upsert_us,
121 "delete_us": delete_us,
122 "upsert_root": upserted.root(),
123 "delete_root": deleted.root(),
124 }),
125 );
126 }
127
128 let adapter_setup_started = Instant::now();
129 let adapter_dir = tempfile::TempDir::new()?;
130 let database = Database::init_bare(adapter_dir.path())?;
131 let collection = database.create_collection("benchmark", config)?;
132 let adapter_setup_us = micros(adapter_setup_started);
133 let adapter_build_started = Instant::now();
134 let adapter_root = collection.upsert(points)?.root;
135 let adapter_build_us = micros(adapter_build_started);
136 if adapter_root != snapshot.root() {
137 return Err("snapshot-core and named-adapter roots differ".into());
138 }
139 query_collection_all(&collection, &queries[..1], maximum_k, true, None)?;
140 query_collection_all(&collection, &queries[..1], maximum_k, false, None)?;
141 let (adapter_exact_query_us, adapter_exact_results, adapter_exact_vectors_scored) =
142 query_collection_all(&collection, &queries, maximum_k, true, None)?;
143 let (
144 adapter_approximate_query_us,
145 adapter_approximate_results,
146 adapter_approximate_vectors_scored,
147 ) = query_collection_all(&collection, &queries, maximum_k, false, None)?;
148 let adapter_throughput = json!({
149 "exact": query_collection_throughput(
150 &collection,
151 &queries,
152 maximum_k,
153 true,
154 &spec.concurrency,
155 )?,
156 "approximate": query_collection_throughput(
157 &collection,
158 &queries,
159 maximum_k,
160 false,
161 &spec.concurrency,
162 )?,
163 });
164 let historical_started = Instant::now();
165 let historical = collection.at(&adapter_root)?;
166 let historical_count = historical.count(None)?.count;
167 let historical_read_us = micros(historical_started);
168
169 let report = json!({
170 "schema_version": 1,
171 "engine": "git-vdb",
172 "case_name": spec.case_name,
173 "point_count": spec.point_count,
174 "dimension": spec.dimension,
175 "query_count": spec.query_count,
176 "k": spec.k,
177 "root": snapshot.root(),
178 "setup_us": setup_us,
179 "snapshot_core": {
180 "build_us": build_us,
181 "exact_query_us": exact_query_us,
182 "approximate_query_us": approximate_query_us,
183 "exact_results": exact_results,
184 "approximate_results": approximate_results,
185 "exact_vectors_scored": exact_vectors_scored,
186 "approximate_vectors_scored": approximate_vectors_scored,
187 "throughput": snapshot_throughput,
188 "filtered": filtered,
189 "mutations": mutations,
190 "on_disk_bytes": baseline_on_disk_bytes,
191 },
192 "named_adapter": {
193 "setup_us": adapter_setup_us,
194 "build_us": adapter_build_us,
195 "exact_query_us": adapter_exact_query_us,
196 "approximate_query_us": adapter_approximate_query_us,
197 "exact_results": adapter_exact_results,
198 "approximate_results": adapter_approximate_results,
199 "exact_vectors_scored": adapter_exact_vectors_scored,
200 "approximate_vectors_scored": adapter_approximate_vectors_scored,
201 "throughput": adapter_throughput,
202 "historical_read_us": historical_read_us,
203 "historical_count": historical_count,
204 "on_disk_bytes": directory_bytes(adapter_dir.path())?,
205 }
206 });
207 fs::write(output, serde_json::to_vec_pretty(&report)?)?;
208 Ok(())
209}Sourcepub fn open_snapshot(&self, root: impl AsRef<str>) -> Result<Snapshot>
pub fn open_snapshot(&self, root: impl AsRef<str>) -> Result<Snapshot>
Opens an exact tree object ID without resolving refs or commits.
Examples found in repository?
169fn query(
170 input: &Path,
171 repository: &Path,
172 build_report: &Path,
173 mode: &str,
174 output: &Path,
175) -> Result<(), Box<dyn std::error::Error>> {
176 let (exact, warm_exact) = match mode {
177 "exact" => (true, false),
178 "approximate" => (false, false),
179 "approximate-after-exact" => (false, true),
180 _ => return Err(format!("unsupported query mode: {mode}").into()),
181 };
182 let spec = read_spec(input)?;
183 let queries = read_vectors(&spec.queries_path, spec.query_count, spec.dimension)?;
184 let maximum_k = *spec.k.iter().max().ok_or("k must not be empty")?;
185 let build: serde_json::Value = serde_json::from_slice(&fs::read(build_report)?)?;
186 let root = build
187 .get("root")
188 .and_then(serde_json::Value::as_str)
189 .ok_or("build report root is missing")?;
190 let engine = SnapshotEngine::open(repository)?;
191 let snapshot = engine.open_snapshot(root)?;
192
193 let cache_build_us = if warm_exact {
194 let started = Instant::now();
195 snapshot.query(make_query(&queries[0], maximum_k, true))?;
196 Some(micros(started))
197 } else {
198 None
199 };
200 // Fill the immutable snapshot cache, construct an approximate lookup over an
201 // already-warm exact view, or warm the unchanged approximate ODB path without
202 // including that one-time work in the samples.
203 let warmup_started = Instant::now();
204 snapshot.query(make_query(&queries[0], maximum_k, exact))?;
205 let warmup_us = micros(warmup_started);
206 wait_for_profiler()?;
207
208 let mut query_us = Vec::with_capacity(queries.len());
209 let mut results = Vec::with_capacity(queries.len());
210 let mut vectors_scored = Vec::with_capacity(queries.len());
211 let batch_started = Instant::now();
212 for vector in &queries {
213 let started = Instant::now();
214 let result = snapshot.query(make_query(vector, maximum_k, exact))?;
215 query_us.push(micros(started));
216 vectors_scored.push(result.stats.vectors_scored);
217 results.push(result.points);
218 }
219 let batch_us = micros(batch_started);
220 fs::write(
221 output,
222 serde_json::to_vec_pretty(&json!({
223 "schema_version": 1,
224 "root": root,
225 "mode": mode,
226 "cache_build_us": cache_build_us,
227 "warmup_us": warmup_us,
228 "query_us": query_us,
229 "batch_us": batch_us,
230 "vectors_scored": vectors_scored,
231 "results": results,
232 }))?,
233 )?;
234 Ok(())
235}Sourcepub fn import_directory(&self, path: impl AsRef<Path>) -> Result<Snapshot>
pub fn import_directory(&self, path: impl AsRef<Path>) -> Result<Snapshot>
Imports a materialized canonical tree into this engine’s object database.
Sourcepub fn query(&self, root: impl AsRef<str>, query: Query) -> Result<QueryResult>
pub fn query(&self, root: impl AsRef<str>, query: Query) -> Result<QueryResult>
Queries an exact root ID without first constructing a named collection.
Sourcepub fn get(
&self,
root: impl AsRef<str>,
request: GetRequest,
) -> Result<GetResult>
pub fn get( &self, root: impl AsRef<str>, request: GetRequest, ) -> Result<GetResult>
Retrieves records from an exact root ID.
Sourcepub fn count(
&self,
root: impl AsRef<str>,
filter: Option<Filter>,
) -> Result<CountResult>
pub fn count( &self, root: impl AsRef<str>, filter: Option<Filter>, ) -> Result<CountResult>
Counts records in an exact root ID.
Sourcepub fn validate(
&self,
root: impl AsRef<str>,
full: bool,
) -> Result<ValidationReport>
pub fn validate( &self, root: impl AsRef<str>, full: bool, ) -> Result<ValidationReport>
Validates an exact root ID.
Examples found in repository?
117fn validate(
118 repository: &Path,
119 build_report: &Path,
120 output: &Path,
121) -> Result<(), Box<dyn std::error::Error>> {
122 let build: serde_json::Value = serde_json::from_slice(&fs::read(build_report)?)?;
123 let root = build
124 .get("root")
125 .and_then(serde_json::Value::as_str)
126 .ok_or("build report root is missing")?;
127 let engine = SnapshotEngine::open(repository)?;
128 let started = Instant::now();
129 let report = engine.validate(root, true)?;
130 fs::write(
131 output,
132 serde_json::to_vec_pretty(&json!({
133 "schema_version": 1,
134 "root": root,
135 "validation_us": micros(started),
136 "report": report,
137 }))?,
138 )?;
139 Ok(())
140}Sourcepub fn build_directory(
path: impl AsRef<Path>,
config: CollectionConfig,
points: Vec<Point>,
) -> Result<Snapshot>
pub fn build_directory( path: impl AsRef<Path>, config: CollectionConfig, points: Vec<Point>, ) -> Result<Snapshot>
Builds a snapshot without a caller-provided Git repository and writes its canonical files into a new materialized directory.
Trait Implementations§
Source§impl Clone for SnapshotEngine
impl Clone for SnapshotEngine
Source§fn clone(&self) -> SnapshotEngine
fn clone(&self) -> SnapshotEngine
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read more