Skip to main content

SnapshotEngine

Struct SnapshotEngine 

Source
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

Source

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?
examples/lancedb_git_vdb_profile.rs (line 84)
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}
Source

pub fn init(path: impl AsRef<Path>) -> Result<Self>

Initializes a bare Git object database for immutable snapshots.

Examples found in repository?
examples/lancedb_git_vdb_profile.rs (line 153)
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
Hide additional examples
examples/lancedb_git_vdb_runner.rs (line 50)
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}
Source

pub fn ephemeral() -> Result<Self>

Creates an isolated temporary object database whose lifetime is retained by the engine and snapshots returned from it.

Source

pub fn build( &self, config: CollectionConfig, points: Vec<Point>, ) -> Result<Snapshot>

Builds a canonical root from a complete point set.

Examples found in repository?
examples/lancedb_git_vdb_profile.rs (line 155)
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
Hide additional examples
examples/lancedb_git_vdb_runner.rs (line 53)
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}
Source

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?
examples/lancedb_git_vdb_profile.rs (lines 87-90)
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
Hide additional examples
examples/lancedb_git_vdb_runner.rs (lines 103-106)
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}
Source

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?
examples/lancedb_git_vdb_profile.rs (line 191)
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}
Source

pub fn import_directory(&self, path: impl AsRef<Path>) -> Result<Snapshot>

Imports a materialized canonical tree into this engine’s object database.

Source

pub fn query(&self, root: impl AsRef<str>, query: Query) -> Result<QueryResult>

Queries an exact root ID without first constructing a named collection.

Source

pub fn get( &self, root: impl AsRef<str>, request: GetRequest, ) -> Result<GetResult>

Retrieves records from an exact root ID.

Source

pub fn count( &self, root: impl AsRef<str>, filter: Option<Filter>, ) -> Result<CountResult>

Counts records in an exact root ID.

Source

pub fn validate( &self, root: impl AsRef<str>, full: bool, ) -> Result<ValidationReport>

Validates an exact root ID.

Examples found in repository?
examples/lancedb_git_vdb_profile.rs (line 129)
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}
Source

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

Source§

fn clone(&self) -> SnapshotEngine

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SnapshotEngine

Source§

fn fmt(&self, formatter: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.