tensorfs 0.3.36__tar.gz → 0.3.38__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.
Potentially problematic release.
This version of tensorfs might be problematic. Click here for more details.
- {tensorfs-0.3.36 → tensorfs-0.3.38}/Cargo.lock +32 -2
- {tensorfs-0.3.36 → tensorfs-0.3.38}/Cargo.toml +1 -1
- {tensorfs-0.3.36 → tensorfs-0.3.38}/PKG-INFO +1 -1
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/Cargo.toml +1 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/derived/adoption.rs +140 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/reclaim.rs +34 -106
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/repo_cache.rs +340 -104
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/tests.rs +64 -194
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/reclaim_run.rs +65 -28
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/repo_cache.rs +155 -4
- {tensorfs-0.3.36 → tensorfs-0.3.38}/pyproject.toml +1 -1
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/build.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/examples/h3-swiglu-layout.md +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/examples/optional-cache-backfill.md +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/examples/resident-conversion.md +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/examples/resident_conversion_profile.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/b64.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/bin/tfs/ckpt.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/bin/tfs/conform.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/bin/tfs/fetch.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/bin/tfs/ingest.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/bin/tfs/main.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/bin/tfs/rd.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/bin/tfs/repo.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/bin/tfs/snap.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/bin/tfs/watch.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/cache_roots.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/canon.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/capability.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/catalog.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/cbor.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/checkpoint.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/corpus.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/derived/graft_tests.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/derived/progress.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/derived/resume_tests.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/derived/retention.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/derived/roots.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/derived.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/dtype.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/durability.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/err.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/fetch.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/fill.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/fit.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/gc.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/header.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/home.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ids.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ingest/carrier.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ingest/convert.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ingest/fingerprint.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ingest/gguf.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ingest/journal.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ingest/mod.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ingest/preflight.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ingest/source.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ingest/stamp.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/ingest/transaction.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/jcs.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/lib.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/limits.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/machine.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/manifest.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/meta.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/project.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/providers.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/read.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/receipt.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/registry.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/relation.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/repository.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/sha256.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/source_artifact.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/spec.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/staging.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/storage.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/store.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/api.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/http.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/hub.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/ledger.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/mod.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/policy.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/prefix.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/pull.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/push.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/transport/ranged.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/src/vectors.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/fetch_url_progress.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/fit.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/h3_swiglu.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/home.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/import_tree.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/instrument_profiles.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/liveness.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/read_plan.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/reclaim.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/repo_store.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/resident_admission.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/sha256_backends.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/sharded_cas.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/source_artifacts.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/source_plan.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/source_prefixes.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/source_preflight.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/staging.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/store_boundaries.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/tensor_schema.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/version.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-core/tests/write_through.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-py/Cargo.toml +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-py/src/docs.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-py/src/errors.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-py/src/lib.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/crates/tensorfs-py/src/store.rs +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/python/tensorfs/__init__.py +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/python/tensorfs/_ext.pyi +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/python/tensorfs/errors.py +0 -0
- {tensorfs-0.3.36 → tensorfs-0.3.38}/python/tensorfs/py.typed +0 -0
|
@@ -48,6 +48,16 @@ version = "0.5.8"
|
|
|
48
48
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
49
49
|
checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c"
|
|
50
50
|
|
|
51
|
+
[[package]]
|
|
52
|
+
name = "errno"
|
|
53
|
+
version = "0.3.14"
|
|
54
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
55
|
+
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
|
|
56
|
+
dependencies = [
|
|
57
|
+
"libc",
|
|
58
|
+
"windows-sys",
|
|
59
|
+
]
|
|
60
|
+
|
|
51
61
|
[[package]]
|
|
52
62
|
name = "fallible-iterator"
|
|
53
63
|
version = "0.3.0"
|
|
@@ -128,6 +138,12 @@ dependencies = [
|
|
|
128
138
|
"vcpkg",
|
|
129
139
|
]
|
|
130
140
|
|
|
141
|
+
[[package]]
|
|
142
|
+
name = "linux-raw-sys"
|
|
143
|
+
version = "0.12.1"
|
|
144
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
145
|
+
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
|
146
|
+
|
|
131
147
|
[[package]]
|
|
132
148
|
name = "minicbor"
|
|
133
149
|
version = "2.3.0"
|
|
@@ -290,6 +306,19 @@ dependencies = [
|
|
|
290
306
|
"smallvec",
|
|
291
307
|
]
|
|
292
308
|
|
|
309
|
+
[[package]]
|
|
310
|
+
name = "rustix"
|
|
311
|
+
version = "1.1.4"
|
|
312
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
313
|
+
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
|
314
|
+
dependencies = [
|
|
315
|
+
"bitflags",
|
|
316
|
+
"errno",
|
|
317
|
+
"libc",
|
|
318
|
+
"linux-raw-sys",
|
|
319
|
+
"windows-sys",
|
|
320
|
+
]
|
|
321
|
+
|
|
293
322
|
[[package]]
|
|
294
323
|
name = "rustls"
|
|
295
324
|
version = "0.23.43"
|
|
@@ -392,19 +421,20 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
392
421
|
|
|
393
422
|
[[package]]
|
|
394
423
|
name = "tensorfs-core"
|
|
395
|
-
version = "0.3.
|
|
424
|
+
version = "0.3.38"
|
|
396
425
|
dependencies = [
|
|
397
426
|
"fs2",
|
|
398
427
|
"minicbor",
|
|
399
428
|
"rcgen",
|
|
400
429
|
"rusqlite",
|
|
430
|
+
"rustix",
|
|
401
431
|
"rustls",
|
|
402
432
|
"webpki-roots 0.26.11",
|
|
403
433
|
]
|
|
404
434
|
|
|
405
435
|
[[package]]
|
|
406
436
|
name = "tensorfs-py"
|
|
407
|
-
version = "0.3.
|
|
437
|
+
version = "0.3.38"
|
|
408
438
|
dependencies = [
|
|
409
439
|
"pyo3",
|
|
410
440
|
"tensorfs-core",
|
|
@@ -11,6 +11,7 @@ path = "src/bin/tfs/main.rs"
|
|
|
11
11
|
fs2 = "0.4"
|
|
12
12
|
minicbor = { version = "2.3.0", default-features = false, features = ["alloc"] }
|
|
13
13
|
rusqlite = { version = "0.32", features = ["bundled"] }
|
|
14
|
+
rustix = { version = "1.1.4", default-features = false, features = ["fs", "std"] }
|
|
14
15
|
# tfs-049: the ONE transport (src/transport/). rustls is the one TLS stack, used only
|
|
15
16
|
# there; scripts/fence.py refuses it anywhere else so a second mover cannot grow.
|
|
16
17
|
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
|
|
@@ -250,6 +250,146 @@ fn export(
|
|
|
250
250
|
#[cfg(test)]
|
|
251
251
|
mod tests {
|
|
252
252
|
use super::*;
|
|
253
|
+
use std::collections::BTreeSet;
|
|
254
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
255
|
+
use std::sync::Arc;
|
|
256
|
+
|
|
257
|
+
#[derive(Default)]
|
|
258
|
+
struct MeasuredIO {
|
|
259
|
+
moved: AtomicU64,
|
|
260
|
+
admitted: AtomicU64,
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
impl crate::store::Progress for MeasuredIO {
|
|
264
|
+
fn moved(&self, bytes: u64) {
|
|
265
|
+
self.moved.fetch_add(bytes, Ordering::Relaxed);
|
|
266
|
+
}
|
|
267
|
+
fn admitted(&self) {
|
|
268
|
+
self.admitted.fetch_add(1, Ordering::Relaxed);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
impl MeasuredIO {
|
|
273
|
+
fn take(&self) -> (u64, u64) {
|
|
274
|
+
(
|
|
275
|
+
self.moved.swap(0, Ordering::Relaxed),
|
|
276
|
+
self.admitted.swap(0, Ordering::Relaxed),
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
#[test]
|
|
282
|
+
fn multiple_checkpoint_groups_separate_rehash_from_adoption_metadata_io() {
|
|
283
|
+
let (store, meta, mut declaration, source, target) = fixture();
|
|
284
|
+
let mut third = declaration.components[0].add[0].clone();
|
|
285
|
+
third.key = "third".into();
|
|
286
|
+
declaration.components[0].add.push(third);
|
|
287
|
+
declaration.order.push(("model".into(), "third".into()));
|
|
288
|
+
declaration.max_new_bytes += 2048;
|
|
289
|
+
let raw = declaration.canonical_work_bytes().unwrap();
|
|
290
|
+
let writer = begin(&store, &meta, &source, 1, declaration.clone(), None).unwrap();
|
|
291
|
+
let first = write(&store, &meta, &source, "weight", 3);
|
|
292
|
+
checkpoint_progress(&store, &meta, &source, 1, "source", "model", None).unwrap();
|
|
293
|
+
let second = write(&store, &meta, &source, "next", 4);
|
|
294
|
+
let (_, checkpoint) =
|
|
295
|
+
checkpoint_progress(&store, &meta, &source, 1, "source", "model", None).unwrap();
|
|
296
|
+
fence(&meta, &source, 1).unwrap();
|
|
297
|
+
release_guards(&meta, Some(writer.writer_hold), writer.source_leases);
|
|
298
|
+
|
|
299
|
+
let progress = Arc::new(MeasuredIO::default());
|
|
300
|
+
let store = store.observed(progress.clone());
|
|
301
|
+
eprintln!("CR108_IO_ROOT {}", store.root().display());
|
|
302
|
+
eprintln!("CR108_IO_PAYLOAD {} {}", first.sha256, second.sha256);
|
|
303
|
+
// Lose only the verification receipts in this fixture, not the actual
|
|
304
|
+
// bytes. The real verifier must rehash them before the warm adoption arm.
|
|
305
|
+
let catalog = crate::catalog::Catalog::open(store.root()).unwrap();
|
|
306
|
+
catalog.drop_verification(&first.sha256);
|
|
307
|
+
catalog.drop_verification(&second.sha256);
|
|
308
|
+
drop(catalog);
|
|
309
|
+
eprintln!("CR108_IO_PHASE verification");
|
|
310
|
+
assert!(matches!(
|
|
311
|
+
store.verify(&first.sha256).unwrap(),
|
|
312
|
+
crate::store::Verdict::Invalidated { .. }
|
|
313
|
+
));
|
|
314
|
+
assert!(matches!(
|
|
315
|
+
store.verify(&second.sha256).unwrap(),
|
|
316
|
+
crate::store::Verdict::Invalidated { .. }
|
|
317
|
+
));
|
|
318
|
+
let verification = progress.take();
|
|
319
|
+
assert_eq!(verification, (4096, 0));
|
|
320
|
+
|
|
321
|
+
eprintln!("CR108_IO_PHASE inspect_before");
|
|
322
|
+
let before: BTreeSet<_> = store.objects().unwrap().into_iter().collect();
|
|
323
|
+
let before_payloads: Vec<_> = [&first, &second]
|
|
324
|
+
.iter()
|
|
325
|
+
.map(|object| {
|
|
326
|
+
let path = store.blob_path(&object.sha256);
|
|
327
|
+
(
|
|
328
|
+
std::fs::read(&path).unwrap(),
|
|
329
|
+
std::fs::metadata(path).unwrap().modified().unwrap(),
|
|
330
|
+
)
|
|
331
|
+
})
|
|
332
|
+
.collect();
|
|
333
|
+
eprintln!("CR108_IO_PHASE adoption");
|
|
334
|
+
let (_, adopted) = adopt_checkpoint(
|
|
335
|
+
&store,
|
|
336
|
+
&meta,
|
|
337
|
+
&target,
|
|
338
|
+
&source,
|
|
339
|
+
&raw,
|
|
340
|
+
checkpoint.head.as_ref().unwrap(),
|
|
341
|
+
"target",
|
|
342
|
+
"model",
|
|
343
|
+
)
|
|
344
|
+
.unwrap();
|
|
345
|
+
let adoption = progress.take();
|
|
346
|
+
eprintln!("CR108_IO_PHASE inspect_after");
|
|
347
|
+
let fresh: Vec<_> = store
|
|
348
|
+
.objects()
|
|
349
|
+
.unwrap()
|
|
350
|
+
.into_iter()
|
|
351
|
+
.filter(|id| !before.contains(id))
|
|
352
|
+
.collect();
|
|
353
|
+
let metadata_bytes: u64 = fresh
|
|
354
|
+
.iter()
|
|
355
|
+
.map(|id| std::fs::metadata(store.blob_path(id)).unwrap().len())
|
|
356
|
+
.sum();
|
|
357
|
+
assert!(metadata_bytes > 0);
|
|
358
|
+
assert_eq!(adoption, (metadata_bytes, fresh.len() as u64));
|
|
359
|
+
for (object, prior) in [&first, &second].iter().zip(before_payloads) {
|
|
360
|
+
let path = store.blob_path(&object.sha256);
|
|
361
|
+
assert_eq!(std::fs::read(&path).unwrap(), prior.0);
|
|
362
|
+
assert_eq!(
|
|
363
|
+
std::fs::metadata(path).unwrap().modified().unwrap(),
|
|
364
|
+
prior.1
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
eprintln!("CR108_IO_PHASE restore");
|
|
368
|
+
let next = begin(
|
|
369
|
+
&store,
|
|
370
|
+
&meta,
|
|
371
|
+
&target,
|
|
372
|
+
1,
|
|
373
|
+
declaration,
|
|
374
|
+
adopted.head.as_ref(),
|
|
375
|
+
)
|
|
376
|
+
.unwrap();
|
|
377
|
+
assert_eq!(
|
|
378
|
+
completed(&meta, &target, 1).unwrap().0,
|
|
379
|
+
vec![
|
|
380
|
+
("model".into(), "next".into(), "value".into()),
|
|
381
|
+
("model".into(), "weight".into(), "value".into())
|
|
382
|
+
]
|
|
383
|
+
);
|
|
384
|
+
let restore = progress.take();
|
|
385
|
+
assert_eq!(restore, (0, 0));
|
|
386
|
+
eprintln!("CR108_IO_PHASE finish");
|
|
387
|
+
write(&store, &meta, &target, "third", 5);
|
|
388
|
+
commit(&store, &meta, &target, 1).unwrap();
|
|
389
|
+
release_guards(&meta, Some(next.writer_hold), next.source_leases);
|
|
390
|
+
eprintln!("CR108_IO_PHASE done");
|
|
391
|
+
eprintln!("CR108_IO_COUNTS verification={verification:?} adoption={adoption:?} restore={restore:?} adoption_metadata_bytes={metadata_bytes}");
|
|
392
|
+
}
|
|
253
393
|
|
|
254
394
|
fn fixture() -> (Store, Meta, Declaration, String, String) {
|
|
255
395
|
let path = std::env::temp_dir().join(format!(
|
|
@@ -498,22 +498,11 @@ pub(crate) fn plan_managed(
|
|
|
498
498
|
|
|
499
499
|
// ------------------------------------------------------------------ the act (tfs-066)
|
|
500
500
|
|
|
501
|
-
/// How a plan's bytes are proven to be gettable again before they are let go.
|
|
502
|
-
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
503
|
-
pub enum Recovery {
|
|
504
|
-
/// The object is already on the bound repo cache at its exact length.
|
|
505
|
-
Cache,
|
|
506
|
-
/// This run put it there.
|
|
507
|
-
Demoted,
|
|
508
|
-
/// The caller declared the roots recoverable from the hub. Their word, like free space.
|
|
509
|
-
Declared,
|
|
510
|
-
}
|
|
511
|
-
|
|
512
501
|
/// What `run` was asked to do beyond the plan itself.
|
|
513
502
|
#[derive(Debug, Clone, Copy, Default)]
|
|
514
503
|
pub struct RunOptions {
|
|
515
|
-
///
|
|
516
|
-
///
|
|
504
|
+
/// Best-effort cache warming before reclaim. This never authorizes deletion;
|
|
505
|
+
/// assume_upstream is still required because the cache is disposable.
|
|
517
506
|
pub demote: bool,
|
|
518
507
|
/// The caller declares these roots recoverable from the hub. Same class of caller-input
|
|
519
508
|
/// as free space (decisions.md 241): TensorFS cannot see the hub, the caller can.
|
|
@@ -573,87 +562,23 @@ impl RunReport {
|
|
|
573
562
|
}
|
|
574
563
|
}
|
|
575
564
|
|
|
576
|
-
///
|
|
577
|
-
///
|
|
578
|
-
///
|
|
579
|
-
///
|
|
580
|
-
/// pressure-evicted"* — with the falsifier *"a pressure sweep evicting a local-only
|
|
581
|
-
/// artifact"*. tfs-020 then deleted the `hub_published` boolean along with the pressure
|
|
582
|
-
/// plane it gated, so the invariant has had no mechanism since.
|
|
583
|
-
///
|
|
584
|
-
/// It comes back CONSTRUCTIVELY rather than as a flag someone must remember to set: the
|
|
585
|
-
/// cache is checked, or this run puts the bytes there, or the caller says so. Demotion is
|
|
586
|
-
/// the interesting one, because it does not FIND the invariant true — it MAKES it true, and
|
|
587
|
-
/// it makes it true against the `/workspace` volume, which measurably survives the
|
|
588
|
-
/// container-disk reset that destroys the Store (th-152, 2026-09-04).
|
|
589
|
-
fn recovery(
|
|
590
|
-
store: &Store,
|
|
591
|
-
row: &crate::storage::GcDelete,
|
|
592
|
-
options: RunOptions,
|
|
593
|
-
report: &mut RunReport,
|
|
594
|
-
) -> Option<Recovery> {
|
|
595
|
-
if options.assume_upstream {
|
|
596
|
-
return Some(Recovery::Declared);
|
|
597
|
-
}
|
|
598
|
-
let cache = store.repo_cache()?;
|
|
599
|
-
let kind = match row.kind {
|
|
600
|
-
"manifest" => CacheKind::Manifest,
|
|
601
|
-
_ => CacheKind::Blob,
|
|
602
|
-
};
|
|
603
|
-
let want = ObjectRef {
|
|
604
|
-
sha256: row.sha256.clone(),
|
|
605
|
-
length: row.length,
|
|
606
|
-
};
|
|
607
|
-
// Present at the exact length is the cheap check and the honest one: the cache's own
|
|
608
|
-
// admission path re-verifies the digest on the way back in, so a byte-level check here
|
|
609
|
-
// would pay for a promise something else already keeps.
|
|
610
|
-
if let Ok(path) = cache.path(kind, &row.sha256) {
|
|
611
|
-
if std::fs::metadata(&path).is_ok_and(|meta| meta.is_file() && meta.len() == row.length) {
|
|
612
|
-
report.already_cached_objects += 1;
|
|
613
|
-
return Some(Recovery::Cache);
|
|
614
|
-
}
|
|
615
|
-
}
|
|
616
|
-
if !options.demote {
|
|
617
|
-
return None;
|
|
618
|
-
}
|
|
619
|
-
match cache.backfill_store(store, kind, &want) {
|
|
620
|
-
Ok(CacheWrite::Stored) => {
|
|
621
|
-
report.demoted_objects += 1;
|
|
622
|
-
report.demoted_bytes = report.demoted_bytes.saturating_add(row.length);
|
|
623
|
-
Some(Recovery::Demoted)
|
|
624
|
-
}
|
|
625
|
-
Ok(CacheWrite::Present) => {
|
|
626
|
-
report.already_cached_objects += 1;
|
|
627
|
-
Some(Recovery::Cache)
|
|
628
|
-
}
|
|
629
|
-
// A cache that cannot take the bytes is weather everywhere else in this crate and
|
|
630
|
-
// stays weather here — but for an EVICTION the consequence is different. On a fetch
|
|
631
|
-
// the authority is still R2; on an eviction this pod may hold the last copy. So a
|
|
632
|
-
// failed demote does not become a silent delete, it becomes a refusal.
|
|
633
|
-
Ok(CacheWrite::Unavailable) | Err(_) => None,
|
|
634
|
-
}
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
/// Demote, tombstone, collect — in that order, and the order is the argument.
|
|
638
|
-
///
|
|
639
|
-
/// 1. **Demote** while the objects are still live and still reachable. Nothing is deleted.
|
|
640
|
-
/// 2. **Tombstone** — `DeleteRepository` per victim, through `apply_repository`'s
|
|
641
|
-
/// compare-and-set. This is the *explicit act* decisions.md 1135 requires: bytes become
|
|
642
|
-
/// collectable because somebody removed the thing that named them, never because a sweep
|
|
643
|
-
/// inferred they were garbage.
|
|
644
|
-
/// 3. **Collect** — the existing `gc::collect`, unchanged. It recomputes reachability from
|
|
645
|
-
/// `repos/`, `manifests/` and `blobs/` alone.
|
|
646
|
-
///
|
|
647
|
-
/// **Step 3 is why a wrong plan cannot corrupt the Store.** The plan chooses which ROOTS
|
|
648
|
-
/// stop being named; the census decides which OBJECTS that actually frees. A caller that
|
|
649
|
-
/// names every root still cannot delete an object a hold protects or a retained manifest
|
|
650
|
-
/// needs.
|
|
565
|
+
/// Reclaim named roots only after the caller confirms durable upstream custody.
|
|
566
|
+
/// Optional cache warming precedes repository tombstones and ordinary GC; cache
|
|
567
|
+
/// presence never authorizes either step. Retained roots and active holds still
|
|
568
|
+
/// protect their objects through the existing reachability census.
|
|
651
569
|
pub fn run(store: &Store, drop: &[RepositoryName], options: RunOptions) -> Result<RunReport> {
|
|
652
570
|
permitted(store)?;
|
|
653
571
|
let mut report = RunReport::default();
|
|
654
572
|
if drop.is_empty() {
|
|
655
573
|
return Ok(report);
|
|
656
574
|
}
|
|
575
|
+
if !options.assume_upstream {
|
|
576
|
+
return refuse(
|
|
577
|
+
Code::DURABILITY_UNPROVEN,
|
|
578
|
+
"named local roots require --assume-upstream after confirming durable upstream \
|
|
579
|
+
custody; an optional cache or --demote cannot preserve the only copy",
|
|
580
|
+
);
|
|
581
|
+
}
|
|
657
582
|
|
|
658
583
|
// EXCLUSIVITY IS PROVED BEFORE ANYTHING IS DONE, not discovered at the end.
|
|
659
584
|
//
|
|
@@ -698,26 +623,29 @@ pub fn run(store: &Store, drop: &[RepositoryName], options: RunOptions) -> Resul
|
|
|
698
623
|
.filter(|row| !already.contains(&row.key))
|
|
699
624
|
.collect();
|
|
700
625
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
626
|
+
if options.demote {
|
|
627
|
+
if let Some(cache) = store.repo_cache() {
|
|
628
|
+
for row in &condemned {
|
|
629
|
+
let kind = if row.kind == "manifest" {
|
|
630
|
+
CacheKind::Manifest
|
|
631
|
+
} else {
|
|
632
|
+
CacheKind::Blob
|
|
633
|
+
};
|
|
634
|
+
let object = ObjectRef {
|
|
635
|
+
sha256: row.sha256.clone(),
|
|
636
|
+
length: row.length,
|
|
637
|
+
};
|
|
638
|
+
match cache.backfill_store(store, kind, &object) {
|
|
639
|
+
Ok(CacheWrite::Stored) => {
|
|
640
|
+
report.demoted_objects += 1;
|
|
641
|
+
report.demoted_bytes = report.demoted_bytes.saturating_add(row.length);
|
|
642
|
+
}
|
|
643
|
+
Ok(CacheWrite::Present) => report.already_cached_objects += 1,
|
|
644
|
+
Ok(CacheWrite::Unavailable) | Err(_) => {}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
705
647
|
}
|
|
706
648
|
}
|
|
707
|
-
if !unrecoverable.is_empty() {
|
|
708
|
-
let shown: Vec<&str> = unrecoverable.iter().take(3).map(String::as_str).collect();
|
|
709
|
-
return refuse(
|
|
710
|
-
Code::DURABILITY_UNPROVEN,
|
|
711
|
-
format!(
|
|
712
|
-
"{} of {} objects cannot be got back if they go — {} — so nothing was \
|
|
713
|
-
dropped; bind a repo cache and pass --demote, or declare the roots \
|
|
714
|
-
recoverable with --assume-upstream",
|
|
715
|
-
unrecoverable.len(),
|
|
716
|
-
condemned.len(),
|
|
717
|
-
shown.join(", "),
|
|
718
|
-
),
|
|
719
|
-
);
|
|
720
|
-
}
|
|
721
649
|
|
|
722
650
|
let fault = crate::store::Fault::default();
|
|
723
651
|
for repo in drop {
|