tensorfs 0.3.65__tar.gz → 0.3.67__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.65 → tensorfs-0.3.67}/Cargo.lock +2 -2
- {tensorfs-0.3.65 → tensorfs-0.3.67}/Cargo.toml +1 -1
- {tensorfs-0.3.65 → tensorfs-0.3.67}/PKG-INFO +1 -1
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/bin/tfs/fetch.rs +7 -3
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/bin/tfs/rd.rs +9 -5
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/catalog.rs +36 -11
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/preflight.rs +20 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/source.rs +87 -17
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/read.rs +58 -57
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/store.rs +13 -11
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/tests.rs +122 -89
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/anima_hf_profile.rs +103 -0
- tensorfs-0.3.67/crates/tensorfs-core/tests/civitai_sdxl_profile.rs +84 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fd_budget.rs +32 -19
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/read_plan.rs +1 -1
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-py/src/ingest.rs +33 -9
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-py/src/lib.rs +1 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/pyproject.toml +1 -1
- {tensorfs-0.3.65 → tensorfs-0.3.67}/python/tensorfs/__init__.py +2 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/python/tensorfs/_ext.pyi +6 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/Cargo.toml +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/build.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/examples/h3-swiglu-layout.md +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/examples/optional-cache-backfill.md +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/examples/resident-conversion.md +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/examples/resident_conversion_profile.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/asset_update.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/b64.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/bin/tfs/ckpt.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/bin/tfs/conform.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/bin/tfs/ingest.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/bin/tfs/main.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/bin/tfs/repo.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/bin/tfs/snap.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/bin/tfs/watch.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/cache_roots.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/canon.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/capability.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/cbor.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/checkpoint.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/checkpoint_root.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/corpus.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/derived/adoption.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/derived/companion_tests.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/derived/estimate.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/derived/graft_tests.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/derived/part_rows_tests.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/derived/progress.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/derived/resume_tests.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/derived/retention.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/derived/roots.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/derived.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/descriptors.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/dtype.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/durability.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/err.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/fetch.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/fill.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/fit.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/gc.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/header.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/home.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ids.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/carrier.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/convert.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/custody.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/fingerprint.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/gguf.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/h3_lora.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/journal.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/mod.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/stamp.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/ingest/transaction.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/jcs.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/lib.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/limits.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/machine.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/manifest.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/meta.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/project.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/providers.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/receipt.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/reclaim.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/registry.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/relation.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/repo_cache/identity.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/repo_cache.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/repository.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/sha256.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/source_artifact.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/spec.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/staging.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/stats.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/storage.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/api.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/http.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/http_reuse_tests.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/hub.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/ledger.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/memory.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/mod.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/pace.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/policy.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/prefix.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/pull.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/push.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/ranged.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/transport/speculate.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/src/vectors.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/additive_fields.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/checkpoint_roots.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fd_headroom.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fetch_url_progress.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fit.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/foreign_json.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/h3_lora.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/h3_swiglu.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/home.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/import_tree.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/instrument_profiles.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/liveness.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/qwen_image21_profile.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/reclaim.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/reclaim_run.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/repo_cache.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/repo_store.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/resident_admission.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/sha256_backends.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/sharded_cas.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/source_artifacts.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/source_download.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/source_plan.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/source_prefixes.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/source_preflight.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/staging.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/store_boundaries.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/tensor_schema.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/transfer_memory.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/trust_index.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/version.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-core/tests/write_through.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-py/Cargo.toml +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-py/src/docs.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-py/src/errors.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/crates/tensorfs-py/src/store.rs +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/python/tensorfs/_derived_channel.py +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/python/tensorfs/_source_channel.py +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/python/tensorfs/_tensors.py +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/python/tensorfs/derived.py +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/python/tensorfs/errors.py +0 -0
- {tensorfs-0.3.65 → tensorfs-0.3.67}/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.67"
|
|
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.67"
|
|
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
|
}
|
|
@@ -133,9 +133,13 @@ pub fn cmd_plan(root: &Path, hex: &str, f: &Flags) -> ExitCode {
|
|
|
133
133
|
let mut rev = construction_order(&h);
|
|
134
134
|
rev.reverse();
|
|
135
135
|
println!(
|
|
136
|
-
"contract: reversed caller traversal
|
|
136
|
+
"contract: reversed caller traversal plans in its own order: {}",
|
|
137
137
|
match read::plan_for_traversal(&h, &rev, &all_components(&h), window) {
|
|
138
|
-
|
|
138
|
+
Ok(p)
|
|
139
|
+
if p.items.first().is_some_and(|it| it
|
|
140
|
+
.what
|
|
141
|
+
.starts_with(&format!("{}/{}#", rev[0].0, rev[0].1))) =>
|
|
142
|
+
"YES",
|
|
139
143
|
_ => "NO — DEFECT",
|
|
140
144
|
}
|
|
141
145
|
);
|
|
@@ -991,7 +995,7 @@ pub fn cmd_arms(root: &Path, hex: &str) -> ExitCode {
|
|
|
991
995
|
short.pop();
|
|
992
996
|
a.arm(
|
|
993
997
|
"traversal missing a tensor",
|
|
994
|
-
Code::
|
|
998
|
+
Code::TRAVERSAL_INCOMPLETE,
|
|
995
999
|
read::plan_for_traversal(&h, &short, &all_components(&h), 1 << 20),
|
|
996
1000
|
);
|
|
997
1001
|
|
|
@@ -1026,7 +1030,7 @@ pub fn cmd_arms(root: &Path, hex: &str) -> ExitCode {
|
|
|
1026
1030
|
holed.pop();
|
|
1027
1031
|
a.arm(
|
|
1028
1032
|
"a tensor missing WITHIN a requested component",
|
|
1029
|
-
Code::
|
|
1033
|
+
Code::TRAVERSAL_INCOMPLETE,
|
|
1030
1034
|
read::plan_for_traversal(&h, &holed, only_one, 1 << 20),
|
|
1031
1035
|
);
|
|
1032
1036
|
}
|
|
@@ -1042,7 +1046,7 @@ pub fn cmd_arms(root: &Path, hex: &str) -> ExitCode {
|
|
|
1042
1046
|
}
|
|
1043
1047
|
a.arm(
|
|
1044
1048
|
"a tensor from a component the plan did not request",
|
|
1045
|
-
Code::
|
|
1049
|
+
Code::TRAVERSAL_INCOMPLETE,
|
|
1046
1050
|
read::plan_for_traversal(&h, &smuggled, only_one, 1 << 20),
|
|
1047
1051
|
);
|
|
1048
1052
|
|
|
@@ -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 {
|
|
@@ -239,6 +239,26 @@ fn profile_plan(plan: &SourcePlan) -> ProfilePlan {
|
|
|
239
239
|
}
|
|
240
240
|
}
|
|
241
241
|
|
|
242
|
+
/// The one reviewed profile these member heads plan under, chosen from their headers alone:
|
|
243
|
+
/// several matching profiles that plan identically are one answer; different plans refuse
|
|
244
|
+
/// `AMBIGUOUS_CLASSIFICATION` naming them, and none refuses `UNREGISTERED_FINGERPRINT`.
|
|
245
|
+
pub fn select_profile(
|
|
246
|
+
registry_locator: &str,
|
|
247
|
+
registry_bytes: &[u8],
|
|
248
|
+
heads: &[MemberHead],
|
|
249
|
+
scratch: &Path,
|
|
250
|
+
) -> Result<String> {
|
|
251
|
+
let staged = stage(scratch, heads)?;
|
|
252
|
+
source::plan_source(
|
|
253
|
+
registry_locator,
|
|
254
|
+
registry_bytes,
|
|
255
|
+
staged.carriers(),
|
|
256
|
+
None,
|
|
257
|
+
None,
|
|
258
|
+
)
|
|
259
|
+
.map(|plan| plan.profile)
|
|
260
|
+
}
|
|
261
|
+
|
|
242
262
|
/// Decide the conversion plan for every requested profile, from headers alone.
|
|
243
263
|
///
|
|
244
264
|
/// One [`source::plan_source`] call per profile, over one carrier set — which is precisely
|
|
@@ -690,12 +690,6 @@ fn plan_source_prepared(
|
|
|
690
690
|
}
|
|
691
691
|
for assignments in profile_assignments(profile, ®istry, carriers, &headers) {
|
|
692
692
|
matches.push((profile, assignments));
|
|
693
|
-
if matches.len() >= 2 {
|
|
694
|
-
break;
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
if matches.len() >= 2 {
|
|
698
|
-
break;
|
|
699
693
|
}
|
|
700
694
|
}
|
|
701
695
|
if matches.is_empty() {
|
|
@@ -704,20 +698,61 @@ fn plan_source_prepared(
|
|
|
704
698
|
"carrier set matches no reviewed source profile",
|
|
705
699
|
);
|
|
706
700
|
}
|
|
707
|
-
|
|
708
|
-
|
|
701
|
+
// Every match is planned. Matches whose plans are identical (the same sources, order,
|
|
702
|
+
// target and session under different profile names) are one answer, named by the most
|
|
703
|
+
// specific profile; only genuinely different plans are ambiguous.
|
|
704
|
+
let mut planned = Vec::with_capacity(matches.len());
|
|
705
|
+
let mut first_error = None;
|
|
706
|
+
for (profile, assignments) in matches {
|
|
707
|
+
match plan_match(
|
|
708
|
+
profile,
|
|
709
|
+
assignments,
|
|
710
|
+
carriers,
|
|
711
|
+
&set,
|
|
712
|
+
®istry,
|
|
713
|
+
registry_locator,
|
|
714
|
+
registry_bytes,
|
|
715
|
+
spec_variant,
|
|
716
|
+
) {
|
|
717
|
+
Ok(result) => planned.push((profile, result)),
|
|
718
|
+
Err(error) => {
|
|
719
|
+
first_error.get_or_insert(error);
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
if planned.is_empty() {
|
|
724
|
+
return Err(first_error.expect("every match was planned or refused"));
|
|
725
|
+
}
|
|
726
|
+
let same = |left: &SourcePlan, right: &SourcePlan| {
|
|
727
|
+
(
|
|
728
|
+
&left.session,
|
|
729
|
+
&left.construction_order,
|
|
730
|
+
&left.sources,
|
|
731
|
+
&left.target,
|
|
732
|
+
) == (
|
|
733
|
+
&right.session,
|
|
734
|
+
&right.construction_order,
|
|
735
|
+
&right.sources,
|
|
736
|
+
&right.target,
|
|
737
|
+
)
|
|
738
|
+
};
|
|
739
|
+
if planned
|
|
740
|
+
.iter()
|
|
741
|
+
.any(|(_, (plan, _))| !same(plan, &planned[0].1 .0))
|
|
742
|
+
{
|
|
743
|
+
let mut profiles: Vec<&str> = planned
|
|
709
744
|
.iter()
|
|
710
745
|
.map(|(profile, _)| profile.name.as_str())
|
|
711
746
|
.collect();
|
|
712
|
-
|
|
747
|
+
profiles.dedup();
|
|
748
|
+
let mut members: Vec<String> = planned
|
|
713
749
|
.iter()
|
|
714
|
-
.flat_map(|(_,
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
.member
|
|
750
|
+
.flat_map(|(_, (plan, _))| {
|
|
751
|
+
plan.sources.iter().map(|source| {
|
|
752
|
+
source
|
|
753
|
+
.source_member
|
|
719
754
|
.clone()
|
|
720
|
-
.unwrap_or_else(||
|
|
755
|
+
.unwrap_or_else(|| source.path.display().to_string())
|
|
721
756
|
})
|
|
722
757
|
})
|
|
723
758
|
.collect();
|
|
@@ -731,7 +766,42 @@ fn plan_source_prepared(
|
|
|
731
766
|
),
|
|
732
767
|
);
|
|
733
768
|
}
|
|
734
|
-
|
|
769
|
+
planned.sort_by_key(|(profile, _)| {
|
|
770
|
+
(
|
|
771
|
+
std::cmp::Reverse(specificity(profile)),
|
|
772
|
+
profile.name.clone(),
|
|
773
|
+
)
|
|
774
|
+
});
|
|
775
|
+
let (_, (plan, prepared)) = planned.swap_remove(0);
|
|
776
|
+
Ok((plan, prepared))
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/// How exactly a profile binds its components: provider members, then member prefixes.
|
|
780
|
+
fn specificity(profile: &SourceProfile) -> (usize, usize) {
|
|
781
|
+
let exact = profile
|
|
782
|
+
.components
|
|
783
|
+
.iter()
|
|
784
|
+
.filter(|component| component.source_member.is_some())
|
|
785
|
+
.count();
|
|
786
|
+
let prefixed = profile
|
|
787
|
+
.components
|
|
788
|
+
.iter()
|
|
789
|
+
.filter(|component| component.source_member_prefix.is_some())
|
|
790
|
+
.count();
|
|
791
|
+
(exact, prefixed)
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
#[allow(clippy::too_many_arguments)]
|
|
795
|
+
fn plan_match(
|
|
796
|
+
profile: &SourceProfile,
|
|
797
|
+
mut assignments: Vec<SourceAssignment>,
|
|
798
|
+
carriers: &[CarrierInput],
|
|
799
|
+
set: &CarrierSet,
|
|
800
|
+
registry: &FingerprintRegistry,
|
|
801
|
+
registry_locator: &str,
|
|
802
|
+
registry_bytes: &[u8],
|
|
803
|
+
spec_variant: Option<usize>,
|
|
804
|
+
) -> Result<(SourcePlan, Prepared)> {
|
|
735
805
|
assignments.sort_by(|left, right| left.component.cmp(&right.component));
|
|
736
806
|
let sources: Vec<(String, CarrierInput)> = assignments
|
|
737
807
|
.iter()
|
|
@@ -762,7 +832,7 @@ fn plan_source_prepared(
|
|
|
762
832
|
.map(|key| (component.component.clone(), key.clone()))
|
|
763
833
|
})
|
|
764
834
|
.collect();
|
|
765
|
-
let mut prepared = prepare_with_registry(&target, &sources,
|
|
835
|
+
let mut prepared = prepare_with_registry(&target, &sources, set, registry, spec_variant)?;
|
|
766
836
|
prepared.plan.apply_order(&order)?;
|
|
767
837
|
prepared.plan.check_converts_something()?;
|
|
768
838
|
let plan = SourcePlan {
|
|
@@ -24,7 +24,7 @@ use std::time::Instant;
|
|
|
24
24
|
|
|
25
25
|
use crate::checkpoint;
|
|
26
26
|
use crate::err::{refuse, Code, Refusal, Result};
|
|
27
|
-
use crate::header::{Asset, Body, Header, Located, Part};
|
|
27
|
+
use crate::header::{Asset, Body, Header, Located, Part, Tensor};
|
|
28
28
|
use crate::ids::ObjectRef;
|
|
29
29
|
use crate::manifest::Manifest;
|
|
30
30
|
use crate::meta::{Hold, Meta};
|
|
@@ -594,48 +594,43 @@ impl ReadPlan {
|
|
|
594
594
|
}
|
|
595
595
|
}
|
|
596
596
|
|
|
597
|
-
fn build(
|
|
597
|
+
fn build(ordered: &[(&str, &str, &Tensor)], window: u64) -> Result<ReadPlan> {
|
|
598
598
|
let mut items = Vec::new();
|
|
599
599
|
let mut dest: u64 = 0;
|
|
600
|
-
for (comp,
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
600
|
+
for (comp, key, tensor) in ordered {
|
|
601
|
+
for (role, part) in &tensor.parts {
|
|
602
|
+
let what = format!("{comp}/{key}#{role}");
|
|
603
|
+
if let Body::Inline(b) = &part.body {
|
|
604
|
+
let n = part.nbytes(&what)?;
|
|
605
|
+
items.push(PlanItem {
|
|
606
|
+
what: what.clone(),
|
|
607
|
+
dest_off: dest,
|
|
608
|
+
len: n,
|
|
609
|
+
source: Source::Inline(b.clone()),
|
|
610
|
+
});
|
|
611
|
+
dest += n;
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
for segment in part.segments() {
|
|
615
|
+
let mut off = 0u64;
|
|
616
|
+
while off < segment.length {
|
|
617
|
+
let n = if window == 0 {
|
|
618
|
+
segment.length - off
|
|
619
|
+
} else {
|
|
620
|
+
window.min(segment.length - off)
|
|
621
|
+
};
|
|
609
622
|
items.push(PlanItem {
|
|
610
623
|
what: what.clone(),
|
|
611
624
|
dest_off: dest,
|
|
612
625
|
len: n,
|
|
613
|
-
source: Source::
|
|
626
|
+
source: Source::Object(ObjectRange {
|
|
627
|
+
obj: segment.clone(),
|
|
628
|
+
off,
|
|
629
|
+
len: n,
|
|
630
|
+
}),
|
|
614
631
|
});
|
|
615
632
|
dest += n;
|
|
616
|
-
|
|
617
|
-
}
|
|
618
|
-
for segment in part.segments() {
|
|
619
|
-
let mut off = 0u64;
|
|
620
|
-
while off < segment.length {
|
|
621
|
-
let n = if window == 0 {
|
|
622
|
-
segment.length - off
|
|
623
|
-
} else {
|
|
624
|
-
window.min(segment.length - off)
|
|
625
|
-
};
|
|
626
|
-
items.push(PlanItem {
|
|
627
|
-
what: what.clone(),
|
|
628
|
-
dest_off: dest,
|
|
629
|
-
len: n,
|
|
630
|
-
source: Source::Object(ObjectRange {
|
|
631
|
-
obj: segment.clone(),
|
|
632
|
-
off,
|
|
633
|
-
len: n,
|
|
634
|
-
}),
|
|
635
|
-
});
|
|
636
|
-
dest += n;
|
|
637
|
-
off += n;
|
|
638
|
-
}
|
|
633
|
+
off += n;
|
|
639
634
|
}
|
|
640
635
|
}
|
|
641
636
|
}
|
|
@@ -658,8 +653,10 @@ fn tally(items: &[PlanItem]) -> (u64, u64, usize) {
|
|
|
658
653
|
(bytes, io_bytes, inline)
|
|
659
654
|
}
|
|
660
655
|
|
|
661
|
-
/// The read planner.
|
|
662
|
-
///
|
|
656
|
+
/// The read planner. The destination follows the CALLER's traversal, which is the order it
|
|
657
|
+
/// constructs in; the header's tensor order is a storage choice of whoever wrote it. The two
|
|
658
|
+
/// are written by independently versioned peers, so a permutation is planned, never refused:
|
|
659
|
+
/// only a traversal that names a different set of tensors is.
|
|
663
660
|
///
|
|
664
661
|
/// COMPLETENESS SCOPES TO THE REQUESTED COMPONENT SET (#570b). `requested` is the caller's
|
|
665
662
|
/// explicit declaration of which components this plan is for; the traversal must name every
|
|
@@ -716,34 +713,38 @@ pub fn plan_for_traversal(
|
|
|
716
713
|
}
|
|
717
714
|
}
|
|
718
715
|
|
|
719
|
-
let
|
|
720
|
-
.iter()
|
|
721
|
-
.map(|(c, k)| (c.as_str(), k.as_str()))
|
|
722
|
-
.collect();
|
|
723
|
-
let have: Vec<(&str, &str)> = header
|
|
716
|
+
let mut carried: HashMap<(&str, &str), &Tensor> = header
|
|
724
717
|
.tensors()
|
|
725
718
|
.filter(|(c, _, _)| scope.contains(&c.as_str()))
|
|
726
|
-
.map(|(c, k,
|
|
719
|
+
.map(|(c, k, t)| ((c.as_str(), k.as_str()), t))
|
|
727
720
|
.collect();
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
721
|
+
let mut ordered = Vec::with_capacity(traversal.len());
|
|
722
|
+
for (c, k) in traversal {
|
|
723
|
+
let Some(tensor) = carried.remove(&(c.as_str(), k.as_str())) else {
|
|
724
|
+
return refuse(
|
|
725
|
+
Code::TRAVERSAL_INCOMPLETE,
|
|
726
|
+
format!(
|
|
727
|
+
"the traversal names {c}/{k}, which requested components {scope:?} do \
|
|
728
|
+
not carry, or names it twice"
|
|
729
|
+
),
|
|
730
|
+
);
|
|
731
|
+
};
|
|
732
|
+
ordered.push((c.as_str(), k.as_str(), tensor));
|
|
733
|
+
}
|
|
734
|
+
if !carried.is_empty() {
|
|
735
|
+
let mut missing: Vec<String> = carried.keys().map(|(c, k)| format!("{c}/{k}")).collect();
|
|
736
|
+
missing.sort();
|
|
737
|
+
missing.truncate(4);
|
|
736
738
|
return refuse(
|
|
737
|
-
Code::
|
|
739
|
+
Code::TRAVERSAL_INCOMPLETE,
|
|
738
740
|
format!(
|
|
739
|
-
"
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
have.len()
|
|
741
|
+
"the traversal omits {} tensor(s) requested components {scope:?} carry: \
|
|
742
|
+
{missing:?}",
|
|
743
|
+
carried.len()
|
|
743
744
|
),
|
|
744
745
|
);
|
|
745
746
|
}
|
|
746
|
-
build(
|
|
747
|
+
build(&ordered, window)
|
|
747
748
|
}
|
|
748
749
|
|
|
749
750
|
// ---------------------------------------------------------------- the reader pool
|
|
@@ -158,6 +158,14 @@ const EDQUOT: i32 = 69;
|
|
|
158
158
|
/// the owner re-issued the fetch, which re-planned, re-asked, and failed at the same byte.
|
|
159
159
|
/// On rented hardware, forever. Naming the condition is what lets a caller stop.
|
|
160
160
|
pub(crate) fn classify_io(what: impl AsRef<str>, e: std::io::Error) -> Refusal {
|
|
161
|
+
// A full descriptor table is the process's shortage, not the Store's: every open in
|
|
162
|
+
// the Store says so the same way, and a transfer treats it as backpressure.
|
|
163
|
+
if crate::descriptors::exhausted(&e) {
|
|
164
|
+
return Refusal {
|
|
165
|
+
code: Code::FD_HEADROOM,
|
|
166
|
+
detail: format!("{}: no file descriptor ({e})", what.as_ref()),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
161
169
|
if matches!(e.raw_os_error(), Some(ENOSPC) | Some(EDQUOT)) {
|
|
162
170
|
return Refusal {
|
|
163
171
|
code: Code::CAPACITY_EXHAUSTED,
|
|
@@ -1362,16 +1370,7 @@ impl Store {
|
|
|
1362
1370
|
.create_new(true)
|
|
1363
1371
|
.mode(0o600)
|
|
1364
1372
|
.open(&tmp)
|
|
1365
|
-
.map_err(|e| {
|
|
1366
|
-
if crate::descriptors::exhausted(&e) {
|
|
1367
|
-
Refusal {
|
|
1368
|
-
code: Code::FD_HEADROOM,
|
|
1369
|
-
detail: format!("create temp {}: {e}", tmp.display()),
|
|
1370
|
-
}
|
|
1371
|
-
} else {
|
|
1372
|
-
io(format!("create temp {}", tmp.display()), e)
|
|
1373
|
-
}
|
|
1374
|
-
})?;
|
|
1373
|
+
.map_err(|e| io(format!("create temp {}", tmp.display()), e))?;
|
|
1375
1374
|
match f.try_lock_exclusive() {
|
|
1376
1375
|
Ok(()) => break (tmp, f),
|
|
1377
1376
|
// Only a reaper can hold the lock on a name this process just minted: it
|
|
@@ -1473,7 +1472,10 @@ impl Store {
|
|
|
1473
1472
|
fault: &Fault,
|
|
1474
1473
|
operation: Option<&str>,
|
|
1475
1474
|
) -> Result<Put> {
|
|
1476
|
-
let catalog = crate::catalog::Catalog::
|
|
1475
|
+
let catalog = match crate::catalog::Catalog::open_admitting(&self.root) {
|
|
1476
|
+
Ok(catalog) => catalog,
|
|
1477
|
+
Err(refusal) => return cleanup(&verified.tmp, refusal),
|
|
1478
|
+
};
|
|
1477
1479
|
let _writer = crate::catalog::WriterGuard::acquire(&self.root)?;
|
|
1478
1480
|
let object = ObjectRef {
|
|
1479
1481
|
sha256: verified.sha256.clone(),
|