tensorfs 0.3.64__tar.gz → 0.3.66__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tensorfs-0.3.64 → tensorfs-0.3.66}/Cargo.lock +2 -2
- {tensorfs-0.3.64 → tensorfs-0.3.66}/Cargo.toml +1 -1
- {tensorfs-0.3.64 → tensorfs-0.3.66}/PKG-INFO +1 -1
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/bin/tfs/fetch.rs +7 -3
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/catalog.rs +36 -11
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/derived/part_rows_tests.rs +157 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/derived/progress.rs +10 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/derived/resume_tests.rs +13 -3
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/derived.rs +166 -10
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/durability.rs +67 -27
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/preflight.rs +20 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/source.rs +87 -17
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/meta.rs +11 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/store.rs +13 -11
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/tests.rs +122 -89
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/anima_hf_profile.rs +11 -0
- tensorfs-0.3.66/crates/tensorfs-core/tests/civitai_sdxl_profile.rs +84 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fd_budget.rs +32 -19
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-py/src/ingest.rs +33 -9
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-py/src/lib.rs +1 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-py/src/store.rs +7 -1
- {tensorfs-0.3.64 → tensorfs-0.3.66}/pyproject.toml +1 -1
- {tensorfs-0.3.64 → tensorfs-0.3.66}/python/tensorfs/__init__.py +2 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/python/tensorfs/_ext.pyi +6 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/Cargo.toml +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/build.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/examples/h3-swiglu-layout.md +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/examples/optional-cache-backfill.md +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/examples/resident-conversion.md +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/examples/resident_conversion_profile.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/asset_update.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/b64.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/bin/tfs/ckpt.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/bin/tfs/conform.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/bin/tfs/ingest.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/bin/tfs/main.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/bin/tfs/rd.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/bin/tfs/repo.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/bin/tfs/snap.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/bin/tfs/watch.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/cache_roots.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/canon.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/capability.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/cbor.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/checkpoint.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/checkpoint_root.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/corpus.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/derived/adoption.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/derived/companion_tests.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/derived/estimate.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/derived/graft_tests.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/derived/retention.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/derived/roots.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/descriptors.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/dtype.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/err.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/fetch.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/fill.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/fit.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/gc.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/header.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/home.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ids.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/carrier.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/convert.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/custody.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/fingerprint.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/gguf.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/h3_lora.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/journal.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/mod.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/stamp.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/ingest/transaction.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/jcs.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/lib.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/limits.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/machine.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/manifest.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/project.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/providers.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/read.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/receipt.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/reclaim.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/registry.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/relation.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/repo_cache/identity.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/repo_cache.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/repository.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/sha256.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/source_artifact.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/spec.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/staging.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/stats.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/storage.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/api.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/http.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/http_reuse_tests.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/hub.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/ledger.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/memory.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/mod.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/pace.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/policy.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/prefix.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/pull.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/push.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/ranged.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/transport/speculate.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/src/vectors.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/additive_fields.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/checkpoint_roots.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fd_headroom.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fetch_url_progress.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fit.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/foreign_json.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/h3_lora.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/h3_swiglu.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/home.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/import_tree.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/instrument_profiles.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/liveness.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/qwen_image21_profile.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/read_plan.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/reclaim.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/reclaim_run.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/repo_cache.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/repo_store.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/resident_admission.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/sha256_backends.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/sharded_cas.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/source_artifacts.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/source_download.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/source_plan.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/source_prefixes.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/source_preflight.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/staging.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/store_boundaries.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/tensor_schema.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/transfer_memory.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/trust_index.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/version.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-core/tests/write_through.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-py/Cargo.toml +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-py/src/docs.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/crates/tensorfs-py/src/errors.rs +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/python/tensorfs/_derived_channel.py +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/python/tensorfs/_source_channel.py +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/python/tensorfs/_tensors.py +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/python/tensorfs/derived.py +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/python/tensorfs/errors.py +0 -0
- {tensorfs-0.3.64 → tensorfs-0.3.66}/python/tensorfs/py.typed +0 -0
|
@@ -439,7 +439,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
439
439
|
|
|
440
440
|
[[package]]
|
|
441
441
|
name = "tensorfs-core"
|
|
442
|
-
version = "0.3.
|
|
442
|
+
version = "0.3.66"
|
|
443
443
|
dependencies = [
|
|
444
444
|
"fs2",
|
|
445
445
|
"libc",
|
|
@@ -454,7 +454,7 @@ dependencies = [
|
|
|
454
454
|
|
|
455
455
|
[[package]]
|
|
456
456
|
name = "tensorfs-py"
|
|
457
|
-
version = "0.3.
|
|
457
|
+
version = "0.3.66"
|
|
458
458
|
dependencies = [
|
|
459
459
|
"pyo3",
|
|
460
460
|
"tensorfs-core",
|
|
@@ -1358,6 +1358,7 @@ struct PullProgress {
|
|
|
1358
1358
|
struct PullProgressState {
|
|
1359
1359
|
total_bytes: u64,
|
|
1360
1360
|
held_bytes: u64,
|
|
1361
|
+
wanted_objects: u64,
|
|
1361
1362
|
origin_bytes: u64,
|
|
1362
1363
|
cache_bytes: u64,
|
|
1363
1364
|
origin_objects: u64,
|
|
@@ -1371,6 +1372,7 @@ impl PullProgress {
|
|
|
1371
1372
|
state: Mutex::new(PullProgressState {
|
|
1372
1373
|
total_bytes: 0,
|
|
1373
1374
|
held_bytes: 0,
|
|
1375
|
+
wanted_objects: 0,
|
|
1374
1376
|
origin_bytes: 0,
|
|
1375
1377
|
cache_bytes: 0,
|
|
1376
1378
|
origin_objects: 0,
|
|
@@ -1385,6 +1387,7 @@ impl PullProgress {
|
|
|
1385
1387
|
let mut state = self.state.lock().unwrap();
|
|
1386
1388
|
state.total_bytes = plan.declared_bytes();
|
|
1387
1389
|
state.held_bytes = plan.held_bytes();
|
|
1390
|
+
state.wanted_objects = plan.wanted.len() as u64;
|
|
1388
1391
|
state.last = Some(Instant::now());
|
|
1389
1392
|
print_progress(&state);
|
|
1390
1393
|
}
|
|
@@ -1447,11 +1450,12 @@ impl PullProgress {
|
|
|
1447
1450
|
///
|
|
1448
1451
|
/// Every counter is always present, including a zero. A consumer that reads an absent key
|
|
1449
1452
|
/// as zero cannot tell a cache that answered nothing from a build that never reported the
|
|
1450
|
-
/// door at all, and those are the two states worth telling apart.
|
|
1453
|
+
/// door at all, and those are the two states worth telling apart. `wanted_objects` is the
|
|
1454
|
+
/// plan's count of objects to land; a pod host sizes this pull's storage reservation from it.
|
|
1451
1455
|
fn print_progress(state: &PullProgressState) {
|
|
1452
1456
|
eprintln!(
|
|
1453
|
-
"{{\"event\":\"pull.progress\",\"total_bytes\":{},\"held_bytes\":{},\"origin_bytes\":{},\"cache_bytes\":{},\"origin_objects\":{},\"cache_objects\":{}}}",
|
|
1454
|
-
state.total_bytes, state.held_bytes, state.origin_bytes, state.cache_bytes,
|
|
1457
|
+
"{{\"event\":\"pull.progress\",\"total_bytes\":{},\"held_bytes\":{},\"wanted_objects\":{},\"origin_bytes\":{},\"cache_bytes\":{},\"origin_objects\":{},\"cache_objects\":{}}}",
|
|
1458
|
+
state.total_bytes, state.held_bytes, state.wanted_objects, state.origin_bytes, state.cache_bytes,
|
|
1455
1459
|
state.origin_objects, state.cache_objects
|
|
1456
1460
|
);
|
|
1457
1461
|
}
|
|
@@ -62,6 +62,11 @@ fn sql(what: impl AsRef<str>, error: rusqlite::Error) -> Refusal {
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/// Whether a catalog refusal is SQLite's busy timeout expiring behind another writer.
|
|
66
|
+
fn busy(refusal: &Refusal) -> bool {
|
|
67
|
+
refusal.code == Code::IO_FAILED && refusal.detail.ends_with("database is locked")
|
|
68
|
+
}
|
|
69
|
+
|
|
65
70
|
fn io(what: impl AsRef<str>, error: std::io::Error) -> Refusal {
|
|
66
71
|
crate::store::classify_io(what, error)
|
|
67
72
|
}
|
|
@@ -115,7 +120,20 @@ fn connect(path: &Path) -> Result<Connection> {
|
|
|
115
120
|
.entry(path.to_path_buf())
|
|
116
121
|
.or_default() += 1;
|
|
117
122
|
crate::stats::catalog_opened();
|
|
118
|
-
let connection = Connection::open(path).map_err(|error|
|
|
123
|
+
let connection = Connection::open(path).map_err(|error| {
|
|
124
|
+
// SQLite reports a full descriptor table only as "unable to open"; the table
|
|
125
|
+
// itself answers whether that is what happened.
|
|
126
|
+
if crate::descriptors::available().is_some_and(|free| free == 0) {
|
|
127
|
+
crate::descriptors::exhausted(&std::io::Error::from_raw_os_error(
|
|
128
|
+
rustix::io::Errno::MFILE.raw_os_error(),
|
|
129
|
+
));
|
|
130
|
+
return Refusal {
|
|
131
|
+
code: Code::FD_HEADROOM,
|
|
132
|
+
detail: format!("open catalog: no file descriptor ({error})"),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
sql("open catalog", error)
|
|
136
|
+
})?;
|
|
119
137
|
connection
|
|
120
138
|
.busy_timeout(BUSY_TIMEOUT)
|
|
121
139
|
.map_err(|error| sql("set catalog busy timeout", error))?;
|
|
@@ -372,6 +390,22 @@ impl Catalog {
|
|
|
372
390
|
Ok(catalog)
|
|
373
391
|
}
|
|
374
392
|
|
|
393
|
+
/// `open` for an object admission. A catalog busy past SQLite's timeout while this
|
|
394
|
+
/// process's own admission batches keep committing is a queue, not a failure: the
|
|
395
|
+
/// verified bytes wait for their turn instead of being thrown away and fetched again.
|
|
396
|
+
/// Busy with no batch committed in the meantime is another writer's lock, and refuses.
|
|
397
|
+
pub(crate) fn open_admitting(root: &Path) -> Result<Self> {
|
|
398
|
+
loop {
|
|
399
|
+
let committed = crate::stats::snapshot().admission_batches;
|
|
400
|
+
match Self::open(root) {
|
|
401
|
+
Err(refusal)
|
|
402
|
+
if busy(&refusal) && crate::stats::snapshot().admission_batches > committed => {
|
|
403
|
+
}
|
|
404
|
+
opened => return opened,
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
375
409
|
pub fn open(root: &Path) -> Result<Self> {
|
|
376
410
|
let path = Self::path(root);
|
|
377
411
|
validated(&path)?;
|
|
@@ -1473,16 +1507,7 @@ impl WriterGuard {
|
|
|
1473
1507
|
.read(true)
|
|
1474
1508
|
.write(true)
|
|
1475
1509
|
.open(&path)
|
|
1476
|
-
.map_err(|error|
|
|
1477
|
-
if crate::descriptors::exhausted(&error) {
|
|
1478
|
-
Refusal {
|
|
1479
|
-
code: Code::FD_HEADROOM,
|
|
1480
|
-
detail: format!("create writer marker: {error}"),
|
|
1481
|
-
}
|
|
1482
|
-
} else {
|
|
1483
|
-
io("create writer marker", error)
|
|
1484
|
-
}
|
|
1485
|
-
})?;
|
|
1510
|
+
.map_err(|error| io("create writer marker", error))?;
|
|
1486
1511
|
file.try_lock_exclusive()
|
|
1487
1512
|
.map_err(|error| io("lock writer marker", error))?;
|
|
1488
1513
|
Ok(Self {
|
|
@@ -44,6 +44,7 @@ fn add(store: &Store, meta: &Meta, id: &str, writer: &Begin, key: &str, byte: u8
|
|
|
44
44
|
id,
|
|
45
45
|
1,
|
|
46
46
|
&writer.additions,
|
|
47
|
+
None,
|
|
47
48
|
("model", key, "value"),
|
|
48
49
|
&mut [byte; 512].as_slice(),
|
|
49
50
|
)
|
|
@@ -112,6 +113,7 @@ fn parts_are_rows_that_resume_refuse_conflicts_and_stay_retained() {
|
|
|
112
113
|
&id,
|
|
113
114
|
2,
|
|
114
115
|
&resumed.additions,
|
|
116
|
+
None,
|
|
115
117
|
("model", "weight_2", "value"),
|
|
116
118
|
&mut [3u8; 512].as_slice(),
|
|
117
119
|
)
|
|
@@ -167,3 +169,158 @@ fn an_older_builds_inline_row_and_a_torn_journal_line_are_read() {
|
|
|
167
169
|
assert!(!journal.exists());
|
|
168
170
|
std::fs::remove_dir_all(store.root()).unwrap();
|
|
169
171
|
}
|
|
172
|
+
|
|
173
|
+
#[test]
|
|
174
|
+
fn indexed_checkpoints_write_the_chain_full_checkpoints_write() {
|
|
175
|
+
const COUNT: usize = 40;
|
|
176
|
+
let mut stores = Vec::new();
|
|
177
|
+
for label in ["full", "indexed"] {
|
|
178
|
+
let (store, meta, id, mut declaration) = fixture(label);
|
|
179
|
+
let prototype = declaration.components[0].add[0].clone();
|
|
180
|
+
declaration.components[0].add = (0..COUNT)
|
|
181
|
+
.map(|index| {
|
|
182
|
+
let mut tensor = prototype.clone();
|
|
183
|
+
tensor.key = format!("weight_{index:03}");
|
|
184
|
+
tensor
|
|
185
|
+
})
|
|
186
|
+
.collect();
|
|
187
|
+
declaration.order = (0..COUNT)
|
|
188
|
+
.map(|index| ("model".into(), format!("weight_{index:03}")))
|
|
189
|
+
.collect();
|
|
190
|
+
declaration.configs = vec![ConfigDeclaration::Add {
|
|
191
|
+
target: "model.json".into(),
|
|
192
|
+
}];
|
|
193
|
+
declaration.max_new_bytes = (COUNT * 512 + 64) as u64;
|
|
194
|
+
let writer = begin(&store, &meta, &id, 1, declaration.clone(), None).unwrap();
|
|
195
|
+
stores.push((store, meta, id, declaration, writer));
|
|
196
|
+
}
|
|
197
|
+
let mut cursor = None;
|
|
198
|
+
let (mut full_prior, mut indexed_prior) = (None, None);
|
|
199
|
+
let mut heads: Vec<ObjectRef> = Vec::new();
|
|
200
|
+
for index in 0..COUNT {
|
|
201
|
+
let key = format!("weight_{index:03}");
|
|
202
|
+
let payload = [index as u8; 512];
|
|
203
|
+
let (store, meta, id, _, writer) = &stores[0];
|
|
204
|
+
add_indexed_part(
|
|
205
|
+
store,
|
|
206
|
+
meta,
|
|
207
|
+
id,
|
|
208
|
+
1,
|
|
209
|
+
&writer.additions,
|
|
210
|
+
None,
|
|
211
|
+
("model", &key, "value"),
|
|
212
|
+
&mut payload.as_slice(),
|
|
213
|
+
)
|
|
214
|
+
.unwrap();
|
|
215
|
+
let full = checkpoint_progress(store, meta, id, 1, "request", "model", full_prior.as_ref())
|
|
216
|
+
.unwrap();
|
|
217
|
+
let (store, meta, id, _, writer) = &stores[1];
|
|
218
|
+
add_indexed_part(
|
|
219
|
+
store,
|
|
220
|
+
meta,
|
|
221
|
+
id,
|
|
222
|
+
1,
|
|
223
|
+
&writer.additions,
|
|
224
|
+
cursor.as_mut(),
|
|
225
|
+
("model", &key, "value"),
|
|
226
|
+
&mut payload.as_slice(),
|
|
227
|
+
)
|
|
228
|
+
.unwrap();
|
|
229
|
+
// An older acknowledged head sends the writer down the full path, which re-seeds it.
|
|
230
|
+
let previous = if index == 25 {
|
|
231
|
+
heads.get(heads.len() - 3).cloned()
|
|
232
|
+
} else {
|
|
233
|
+
indexed_prior.clone()
|
|
234
|
+
};
|
|
235
|
+
let indexed = checkpoint_indexed(
|
|
236
|
+
store,
|
|
237
|
+
meta,
|
|
238
|
+
id,
|
|
239
|
+
1,
|
|
240
|
+
"request",
|
|
241
|
+
"model",
|
|
242
|
+
previous.as_ref(),
|
|
243
|
+
&mut cursor,
|
|
244
|
+
)
|
|
245
|
+
.unwrap();
|
|
246
|
+
assert_eq!(full, indexed, "checkpoint {index} diverged");
|
|
247
|
+
assert_eq!(cursor.as_ref().unwrap().tip.head, indexed.1);
|
|
248
|
+
full_prior = full.1.head;
|
|
249
|
+
indexed_prior = indexed.1.head;
|
|
250
|
+
heads.push(indexed_prior.clone().unwrap());
|
|
251
|
+
if index == 20 {
|
|
252
|
+
for (store, meta, id, _, _) in &stores {
|
|
253
|
+
add_config(meta, id, 1, "model.json", &mut b"{}".as_slice()).unwrap();
|
|
254
|
+
let _ = store;
|
|
255
|
+
}
|
|
256
|
+
cursor = None;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
let (store, meta, id, declaration, writer) = stores.pop().unwrap();
|
|
260
|
+
let head = indexed_prior.unwrap();
|
|
261
|
+
let canonical = String::from_utf8(declaration.clone().canonical_work_bytes().unwrap()).unwrap();
|
|
262
|
+
let restored = read_progress(&store, &head, &id, &canonical, &[]).unwrap();
|
|
263
|
+
assert_eq!(restored.added_parts.len(), COUNT);
|
|
264
|
+
assert_eq!(restored.added_configs.len(), 1);
|
|
265
|
+
fence(&meta, &id, 1).unwrap();
|
|
266
|
+
release_guards(&meta, Some(writer.writer_hold), writer.source_leases);
|
|
267
|
+
let resumed = begin(&store, &meta, &id, 2, declaration, Some(&head)).unwrap();
|
|
268
|
+
assert_eq!(completed(&meta, &id, 2).unwrap().0.len(), COUNT);
|
|
269
|
+
fence(&meta, &id, 2).unwrap();
|
|
270
|
+
release_guards(&meta, Some(resumed.writer_hold), resumed.source_leases);
|
|
271
|
+
std::fs::remove_dir_all(store.root()).unwrap();
|
|
272
|
+
for (store, meta, id, _, writer) in stores {
|
|
273
|
+
fence(&meta, &id, 1).unwrap();
|
|
274
|
+
release_guards(&meta, Some(writer.writer_hold), writer.source_leases);
|
|
275
|
+
std::fs::remove_dir_all(store.root()).unwrap();
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
#[test]
|
|
280
|
+
fn an_indexed_checkpoint_does_not_reread_its_chain() {
|
|
281
|
+
let (store, meta, id, declaration) = fixture("no-reread");
|
|
282
|
+
let writer = begin(&store, &meta, &id, 1, declaration, None).unwrap();
|
|
283
|
+
let mut cursor = None;
|
|
284
|
+
let mut prior: Option<ObjectRef> = None;
|
|
285
|
+
let mut first = None;
|
|
286
|
+
for (index, key) in ["weight_0", "weight_1", "weight_2"].into_iter().enumerate() {
|
|
287
|
+
add_indexed_part(
|
|
288
|
+
&store,
|
|
289
|
+
&meta,
|
|
290
|
+
&id,
|
|
291
|
+
1,
|
|
292
|
+
&writer.additions,
|
|
293
|
+
cursor.as_mut(),
|
|
294
|
+
("model", key, "value"),
|
|
295
|
+
&mut [index as u8 + 1; 512].as_slice(),
|
|
296
|
+
)
|
|
297
|
+
.unwrap();
|
|
298
|
+
let (_, head) = checkpoint_indexed(
|
|
299
|
+
&store,
|
|
300
|
+
&meta,
|
|
301
|
+
&id,
|
|
302
|
+
1,
|
|
303
|
+
"request",
|
|
304
|
+
"model",
|
|
305
|
+
prior.as_ref(),
|
|
306
|
+
&mut cursor,
|
|
307
|
+
)
|
|
308
|
+
.unwrap();
|
|
309
|
+
prior = head.head;
|
|
310
|
+
first.get_or_insert(prior.clone().unwrap());
|
|
311
|
+
if index == 0 {
|
|
312
|
+
// The chain's root link is gone: a checkpoint that walked the chain now fails.
|
|
313
|
+
let root = first.clone().unwrap();
|
|
314
|
+
std::fs::remove_file(store.object_path(root.sha256.trim_start_matches("sha256:")))
|
|
315
|
+
.unwrap();
|
|
316
|
+
assert!(
|
|
317
|
+
checkpoint_progress(&store, &meta, &id, 1, "request", "model", prior.as_ref())
|
|
318
|
+
.is_err()
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
assert_eq!(cursor.as_ref().unwrap().parts, 3);
|
|
323
|
+
fence(&meta, &id, 1).unwrap();
|
|
324
|
+
release_guards(&meta, Some(writer.writer_hold), writer.source_leases);
|
|
325
|
+
std::fs::remove_dir_all(store.root()).unwrap();
|
|
326
|
+
}
|
|
@@ -21,6 +21,16 @@ fn part_key(part: &AddedPart) -> (&str, &str, &str) {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
impl Delta {
|
|
24
|
+
/// The parts a writer accepted since its last checkpoint, sorted as a delta stores them.
|
|
25
|
+
pub(super) fn accepted(work: ObjectRef, mut parts: Vec<AddedPart>) -> Self {
|
|
26
|
+
parts.sort_by(|left, right| part_key(left).cmp(&part_key(right)));
|
|
27
|
+
Self {
|
|
28
|
+
work,
|
|
29
|
+
parts,
|
|
30
|
+
configs: Vec::new(),
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
pub(super) fn between(
|
|
25
35
|
row: &TransactionRow,
|
|
26
36
|
prior: Option<&TransactionRow>,
|
|
@@ -981,6 +981,7 @@ fn profile_derived_transaction_scaling() {
|
|
|
981
981
|
let mut part_ms = 0.0;
|
|
982
982
|
let mut checkpoint_ms = 0.0;
|
|
983
983
|
let mut prior = None;
|
|
984
|
+
let mut cursor = None;
|
|
984
985
|
for index in 0..writes {
|
|
985
986
|
let key = format!("weight_{index:04}");
|
|
986
987
|
let started = Instant::now();
|
|
@@ -990,15 +991,24 @@ fn profile_derived_transaction_scaling() {
|
|
|
990
991
|
&id,
|
|
991
992
|
1,
|
|
992
993
|
&writer.additions,
|
|
994
|
+
cursor.as_mut(),
|
|
993
995
|
("model", &key, "value"),
|
|
994
996
|
&mut [index as u8; 16].as_slice(),
|
|
995
997
|
)
|
|
996
998
|
.unwrap();
|
|
997
999
|
part_ms += started.elapsed().as_secs_f64() * 1000.0;
|
|
998
1000
|
let started = Instant::now();
|
|
999
|
-
let (_, head) =
|
|
1000
|
-
|
|
1001
|
-
|
|
1001
|
+
let (_, head) = checkpoint_indexed(
|
|
1002
|
+
&store,
|
|
1003
|
+
&meta,
|
|
1004
|
+
&id,
|
|
1005
|
+
1,
|
|
1006
|
+
"profile",
|
|
1007
|
+
"model",
|
|
1008
|
+
prior.as_ref(),
|
|
1009
|
+
&mut cursor,
|
|
1010
|
+
)
|
|
1011
|
+
.unwrap();
|
|
1002
1012
|
prior = head.head;
|
|
1003
1013
|
checkpoint_ms += started.elapsed().as_secs_f64() * 1000.0;
|
|
1004
1014
|
}
|
|
@@ -2711,6 +2711,158 @@ pub fn completed(
|
|
|
2711
2711
|
))
|
|
2712
2712
|
}
|
|
2713
2713
|
|
|
2714
|
+
/// A writer's position in its checkpoint chain: the chain's tip and held blobs, its fixed
|
|
2715
|
+
/// plan and work context, and the parts it accepted since. With it a checkpoint writes and
|
|
2716
|
+
/// verifies only those parts instead of re-reading the chain it extends.
|
|
2717
|
+
#[derive(Debug, Clone)]
|
|
2718
|
+
pub struct CheckpointCursor {
|
|
2719
|
+
chain: String,
|
|
2720
|
+
plan: String,
|
|
2721
|
+
work: ObjectRef,
|
|
2722
|
+
tip: crate::durability::Tip,
|
|
2723
|
+
parts: usize,
|
|
2724
|
+
pending: Vec<AddedPart>,
|
|
2725
|
+
}
|
|
2726
|
+
|
|
2727
|
+
impl CheckpointCursor {
|
|
2728
|
+
/// Seed from a checkpoint the full path just wrote or confirmed. `None` when there is
|
|
2729
|
+
/// no head yet to extend.
|
|
2730
|
+
fn at(
|
|
2731
|
+
store: &Store,
|
|
2732
|
+
meta: &Meta,
|
|
2733
|
+
transaction: &str,
|
|
2734
|
+
writer_session: u64,
|
|
2735
|
+
chain: String,
|
|
2736
|
+
plan: &str,
|
|
2737
|
+
head: &crate::durability::Head,
|
|
2738
|
+
) -> Result<Option<Self>> {
|
|
2739
|
+
let Some(tip) = head.head.as_ref() else {
|
|
2740
|
+
return Ok(None);
|
|
2741
|
+
};
|
|
2742
|
+
let mut rows: Vec<_> = meta.derived_row(transaction)?.into_iter().collect();
|
|
2743
|
+
let row = open_row(&mut rows, transaction, writer_session)?;
|
|
2744
|
+
let context = progress::context(row);
|
|
2745
|
+
let held = crate::durability::local_chain(store, tip, plan)?
|
|
2746
|
+
.blobs
|
|
2747
|
+
.into_iter()
|
|
2748
|
+
.map(|object| object.sha256)
|
|
2749
|
+
.collect();
|
|
2750
|
+
Ok(Some(Self {
|
|
2751
|
+
chain,
|
|
2752
|
+
plan: plan.to_string(),
|
|
2753
|
+
work: ObjectRef::of(&crate::canon::write(&context.to_value())),
|
|
2754
|
+
tip: crate::durability::Tip {
|
|
2755
|
+
head: head.clone(),
|
|
2756
|
+
held,
|
|
2757
|
+
},
|
|
2758
|
+
parts: row.added_parts.len(),
|
|
2759
|
+
pending: Vec::new(),
|
|
2760
|
+
}))
|
|
2761
|
+
}
|
|
2762
|
+
|
|
2763
|
+
/// Extend the chain by the pending parts, or `None` when anything but this writer's own
|
|
2764
|
+
/// accepted parts moved since the cursor was taken.
|
|
2765
|
+
fn advance(
|
|
2766
|
+
&mut self,
|
|
2767
|
+
store: &Store,
|
|
2768
|
+
meta: &Meta,
|
|
2769
|
+
transaction: &str,
|
|
2770
|
+
writer_session: u64,
|
|
2771
|
+
chain: &str,
|
|
2772
|
+
previous: Option<&ObjectRef>,
|
|
2773
|
+
) -> Result<Option<(String, crate::durability::Head)>> {
|
|
2774
|
+
let tip = self.tip.head.head.clone();
|
|
2775
|
+
let root = roots::read(store, transaction)?;
|
|
2776
|
+
if self.chain != chain
|
|
2777
|
+
|| previous.is_some_and(|previous| Some(previous) != tip.as_ref())
|
|
2778
|
+
|| root.as_ref().and_then(|root| root.checkpoint.as_ref()) != tip.as_ref()
|
|
2779
|
+
{
|
|
2780
|
+
return Ok(None);
|
|
2781
|
+
}
|
|
2782
|
+
meta.check_session(transaction, writer_session)?;
|
|
2783
|
+
if meta.part_count(transaction)? != self.parts + self.pending.len() {
|
|
2784
|
+
return Ok(None);
|
|
2785
|
+
}
|
|
2786
|
+
if self.pending.is_empty() {
|
|
2787
|
+
return Ok(Some((self.plan.clone(), self.tip.head.clone())));
|
|
2788
|
+
}
|
|
2789
|
+
let parts = std::mem::take(&mut self.pending);
|
|
2790
|
+
let objects = parts
|
|
2791
|
+
.iter()
|
|
2792
|
+
.flat_map(|added| added.part.segments().iter().cloned())
|
|
2793
|
+
.collect();
|
|
2794
|
+
let count = parts.len();
|
|
2795
|
+
let delta = progress::Delta::accepted(self.work.clone(), parts);
|
|
2796
|
+
let policy = crate::durability::Policy {
|
|
2797
|
+
chain,
|
|
2798
|
+
operation: transaction,
|
|
2799
|
+
plan: &self.plan,
|
|
2800
|
+
interval: crate::durability::INTERVAL_BYTES,
|
|
2801
|
+
};
|
|
2802
|
+
let extended = crate::durability::extend(
|
|
2803
|
+
store,
|
|
2804
|
+
&policy,
|
|
2805
|
+
&self.tip,
|
|
2806
|
+
&crate::canon::write(&delta.to_value()),
|
|
2807
|
+
objects,
|
|
2808
|
+
None,
|
|
2809
|
+
)?;
|
|
2810
|
+
let Some(mut root) = root else {
|
|
2811
|
+
return Ok(None);
|
|
2812
|
+
};
|
|
2813
|
+
root.checkpoint = extended.head.head.clone();
|
|
2814
|
+
roots::write(store, &root)?;
|
|
2815
|
+
self.tip
|
|
2816
|
+
.held
|
|
2817
|
+
.extend(extended.named.into_iter().map(|object| object.sha256));
|
|
2818
|
+
self.tip.head = extended.head;
|
|
2819
|
+
self.parts += count;
|
|
2820
|
+
Ok(Some((self.plan.clone(), self.tip.head.clone())))
|
|
2821
|
+
}
|
|
2822
|
+
}
|
|
2823
|
+
|
|
2824
|
+
/// Checkpoint through the writer's cursor, which each full checkpoint re-seeds.
|
|
2825
|
+
#[allow(clippy::too_many_arguments)]
|
|
2826
|
+
pub fn checkpoint_indexed(
|
|
2827
|
+
store: &Store,
|
|
2828
|
+
meta: &Meta,
|
|
2829
|
+
transaction: &str,
|
|
2830
|
+
writer_session: u64,
|
|
2831
|
+
operation: &str,
|
|
2832
|
+
slot: &str,
|
|
2833
|
+
previous: Option<&ObjectRef>,
|
|
2834
|
+
cursor: &mut Option<CheckpointCursor>,
|
|
2835
|
+
) -> Result<(String, crate::durability::Head)> {
|
|
2836
|
+
let chain = format!("{operation}/{slot}");
|
|
2837
|
+
if let Some(current) = cursor.as_mut() {
|
|
2838
|
+
if let Some(advanced) =
|
|
2839
|
+
current.advance(store, meta, transaction, writer_session, &chain, previous)?
|
|
2840
|
+
{
|
|
2841
|
+
return Ok(advanced);
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
*cursor = None;
|
|
2845
|
+
let (plan, head) = checkpoint_progress(
|
|
2846
|
+
store,
|
|
2847
|
+
meta,
|
|
2848
|
+
transaction,
|
|
2849
|
+
writer_session,
|
|
2850
|
+
operation,
|
|
2851
|
+
slot,
|
|
2852
|
+
previous,
|
|
2853
|
+
)?;
|
|
2854
|
+
*cursor = CheckpointCursor::at(
|
|
2855
|
+
store,
|
|
2856
|
+
meta,
|
|
2857
|
+
transaction,
|
|
2858
|
+
writer_session,
|
|
2859
|
+
chain,
|
|
2860
|
+
&plan,
|
|
2861
|
+
&head,
|
|
2862
|
+
)?;
|
|
2863
|
+
Ok((plan, head))
|
|
2864
|
+
}
|
|
2865
|
+
|
|
2714
2866
|
pub fn checkpoint_progress(
|
|
2715
2867
|
store: &Store,
|
|
2716
2868
|
meta: &Meta,
|
|
@@ -3115,6 +3267,7 @@ pub fn add_part<R: Read>(
|
|
|
3115
3267
|
transaction,
|
|
3116
3268
|
writer_session,
|
|
3117
3269
|
&additions,
|
|
3270
|
+
None,
|
|
3118
3271
|
value,
|
|
3119
3272
|
reader,
|
|
3120
3273
|
)
|
|
@@ -3122,12 +3275,14 @@ pub fn add_part<R: Read>(
|
|
|
3122
3275
|
|
|
3123
3276
|
/// Accept one part. The cost is this part's bytes plus one indexed SQL row: the
|
|
3124
3277
|
/// declaration is not re-read and earlier accepted parts are neither read nor rewritten.
|
|
3278
|
+
#[allow(clippy::too_many_arguments)]
|
|
3125
3279
|
pub fn add_indexed_part<R: Read>(
|
|
3126
3280
|
store: &Store,
|
|
3127
3281
|
meta: &Meta,
|
|
3128
3282
|
transaction: &str,
|
|
3129
3283
|
writer_session: u64,
|
|
3130
3284
|
additions: &Additions,
|
|
3285
|
+
cursor: Option<&mut CheckpointCursor>,
|
|
3131
3286
|
value: (&str, &str, &str),
|
|
3132
3287
|
reader: &mut R,
|
|
3133
3288
|
) -> Result<crate::checkpoint::Written> {
|
|
@@ -3168,16 +3323,17 @@ pub fn add_indexed_part<R: Read>(
|
|
|
3168
3323
|
// The private root names the objects before SQL accepts the part, so GC never
|
|
3169
3324
|
// observes an accepted part whose bytes it may reclaim.
|
|
3170
3325
|
roots::append_parts(store, &transaction, written.part.segments())?;
|
|
3171
|
-
|
|
3172
|
-
|
|
3173
|
-
|
|
3174
|
-
|
|
3175
|
-
|
|
3176
|
-
|
|
3177
|
-
|
|
3178
|
-
|
|
3179
|
-
|
|
3180
|
-
|
|
3326
|
+
let added = AddedPart {
|
|
3327
|
+
component: component.to_string(),
|
|
3328
|
+
key: tensor_key.to_string(),
|
|
3329
|
+
role: role.to_string(),
|
|
3330
|
+
part: written.part.clone(),
|
|
3331
|
+
};
|
|
3332
|
+
if meta.accept_part(&transaction, writer_session, &added)? {
|
|
3333
|
+
if let Some(cursor) = cursor {
|
|
3334
|
+
cursor.pending.push(added);
|
|
3335
|
+
}
|
|
3336
|
+
}
|
|
3181
3337
|
Ok(written)
|
|
3182
3338
|
}
|
|
3183
3339
|
|