tensorfs 0.3.32__tar.gz → 0.3.34__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.32 → tensorfs-0.3.34}/Cargo.lock +2 -2
- {tensorfs-0.3.32 → tensorfs-0.3.34}/Cargo.toml +1 -1
- {tensorfs-0.3.32 → tensorfs-0.3.34}/PKG-INFO +1 -1
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ingest/source.rs +79 -1
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ingest/transaction.rs +15 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/repo_cache.rs +54 -10
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/ledger.rs +25 -2
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-py/src/store.rs +70 -6
- {tensorfs-0.3.32 → tensorfs-0.3.34}/pyproject.toml +1 -1
- {tensorfs-0.3.32 → tensorfs-0.3.34}/python/tensorfs/_ext.pyi +6 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/Cargo.toml +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/build.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/examples/h3-swiglu-layout.md +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/examples/optional-cache-backfill.md +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/examples/resident-conversion.md +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/examples/resident_conversion_profile.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/b64.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/bin/tfs/ckpt.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/bin/tfs/conform.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/bin/tfs/fetch.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/bin/tfs/ingest.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/bin/tfs/main.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/bin/tfs/rd.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/bin/tfs/repo.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/bin/tfs/snap.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/bin/tfs/watch.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/cache_roots.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/canon.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/capability.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/catalog.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/cbor.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/checkpoint.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/corpus.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/derived/adoption.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/derived/graft_tests.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/derived/progress.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/derived/resume_tests.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/derived/retention.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/derived/roots.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/derived.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/dtype.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/durability.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/err.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/fetch.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/fill.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/fit.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/gc.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/header.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/home.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ids.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ingest/carrier.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ingest/convert.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ingest/fingerprint.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ingest/gguf.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ingest/journal.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ingest/mod.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ingest/preflight.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/ingest/stamp.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/jcs.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/lib.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/limits.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/machine.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/manifest.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/meta.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/project.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/providers.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/read.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/receipt.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/reclaim.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/registry.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/relation.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/repository.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/sha256.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/source_artifact.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/spec.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/staging.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/storage.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/store.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/api.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/http.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/hub.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/mod.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/policy.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/prefix.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/pull.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/push.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/ranged.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/transport/tests.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/src/vectors.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fetch_url_progress.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fit.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/h3_swiglu.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/home.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/import_tree.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/instrument_profiles.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/liveness.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/read_plan.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/reclaim.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/reclaim_run.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/repo_cache.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/repo_store.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/resident_admission.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/sha256_backends.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/sharded_cas.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/source_artifacts.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/source_plan.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/source_prefixes.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/source_preflight.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/staging.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/store_boundaries.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/tensor_schema.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/version.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/write_through.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-py/Cargo.toml +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-py/src/docs.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-py/src/errors.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-py/src/lib.rs +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/python/tensorfs/__init__.py +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/python/tensorfs/errors.py +0 -0
- {tensorfs-0.3.32 → tensorfs-0.3.34}/python/tensorfs/py.typed +0 -0
|
@@ -392,7 +392,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
392
392
|
|
|
393
393
|
[[package]]
|
|
394
394
|
name = "tensorfs-core"
|
|
395
|
-
version = "0.3.
|
|
395
|
+
version = "0.3.34"
|
|
396
396
|
dependencies = [
|
|
397
397
|
"fs2",
|
|
398
398
|
"minicbor",
|
|
@@ -404,7 +404,7 @@ dependencies = [
|
|
|
404
404
|
|
|
405
405
|
[[package]]
|
|
406
406
|
name = "tensorfs-py"
|
|
407
|
-
version = "0.3.
|
|
407
|
+
version = "0.3.34"
|
|
408
408
|
dependencies = [
|
|
409
409
|
"pyo3",
|
|
410
410
|
"tensorfs-core",
|
|
@@ -809,6 +809,8 @@ pub struct PreparedModelSources {
|
|
|
809
809
|
pub deferred_ops: usize,
|
|
810
810
|
pub converted_bytes: u64,
|
|
811
811
|
pub largest_op_bytes: u64,
|
|
812
|
+
/// Minimum body admission for the next atomic op, without writing that op.
|
|
813
|
+
pub required_write_bytes: u64,
|
|
812
814
|
}
|
|
813
815
|
|
|
814
816
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
@@ -1112,6 +1114,24 @@ pub fn prepare_model_source_with_registry(
|
|
|
1112
1114
|
request: &PrepareModelSource,
|
|
1113
1115
|
registry_locator: &str,
|
|
1114
1116
|
registry_bytes: &[u8],
|
|
1117
|
+
) -> Result<PreparedModelSources> {
|
|
1118
|
+
prepare_model_source_with_registry_budget(
|
|
1119
|
+
store,
|
|
1120
|
+
request,
|
|
1121
|
+
registry_locator,
|
|
1122
|
+
registry_bytes,
|
|
1123
|
+
None,
|
|
1124
|
+
)
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/// An explicit body budget never admits an oversized op. Zero advances only metadata
|
|
1128
|
+
/// and replay, reporting the next atomic body's native byte count for host admission.
|
|
1129
|
+
pub fn prepare_model_source_with_registry_budget(
|
|
1130
|
+
store: &Store,
|
|
1131
|
+
request: &PrepareModelSource,
|
|
1132
|
+
registry_locator: &str,
|
|
1133
|
+
registry_bytes: &[u8],
|
|
1134
|
+
write_budget_bytes: Option<u64>,
|
|
1115
1135
|
) -> Result<PreparedModelSources> {
|
|
1116
1136
|
// The guard bridges local snapshot publication and recipient root adoption. A
|
|
1117
1137
|
// predecessor can be released between them, but GC cannot remove the saved bytes.
|
|
@@ -1171,7 +1191,10 @@ pub fn prepare_model_source_with_registry(
|
|
|
1171
1191
|
request,
|
|
1172
1192
|
registry_locator,
|
|
1173
1193
|
registry_bytes,
|
|
1174
|
-
&mut
|
|
1194
|
+
&mut match write_budget_bytes {
|
|
1195
|
+
Some(limit) => transaction::Budget::bounded(limit),
|
|
1196
|
+
None => transaction::Budget::new(durability::INTERVAL_BYTES),
|
|
1197
|
+
},
|
|
1175
1198
|
)
|
|
1176
1199
|
}
|
|
1177
1200
|
|
|
@@ -1365,6 +1388,7 @@ fn prepare_model_source_with_budget(
|
|
|
1365
1388
|
deferred_ops: 0,
|
|
1366
1389
|
converted_bytes: 0,
|
|
1367
1390
|
largest_op_bytes: 0,
|
|
1391
|
+
required_write_bytes: 0,
|
|
1368
1392
|
});
|
|
1369
1393
|
}
|
|
1370
1394
|
crate::catalog::SourcePreparation::Open(writer) => writer,
|
|
@@ -1381,6 +1405,7 @@ fn prepare_model_source_with_budget(
|
|
|
1381
1405
|
deferred_ops: 0,
|
|
1382
1406
|
converted_bytes: 0,
|
|
1383
1407
|
largest_op_bytes: 0,
|
|
1408
|
+
required_write_bytes: 0,
|
|
1384
1409
|
};
|
|
1385
1410
|
let mut needed = std::collections::BTreeSet::new();
|
|
1386
1411
|
for (requested, (plan_hex, prepared)) in request.profiles.iter().zip(plans) {
|
|
@@ -1470,6 +1495,7 @@ fn prepare_model_source_with_budget(
|
|
|
1470
1495
|
result.converted_roles += progress.converted_roles;
|
|
1471
1496
|
result.converted_bytes += progress.written;
|
|
1472
1497
|
result.largest_op_bytes = budget.largest_op;
|
|
1498
|
+
result.required_write_bytes = budget.required_write_bytes;
|
|
1473
1499
|
result.resumed_roles += progress.resumed_roles;
|
|
1474
1500
|
result.deferred_ops += progress.deferred_ops;
|
|
1475
1501
|
if let Some(outcome) = outcome {
|
|
@@ -2526,6 +2552,58 @@ mod tests {
|
|
|
2526
2552
|
fs::remove_dir_all(root).unwrap();
|
|
2527
2553
|
}
|
|
2528
2554
|
|
|
2555
|
+
#[test]
|
|
2556
|
+
fn strict_source_pass_reports_body_admission_then_resumes_without_reconversion() {
|
|
2557
|
+
let root = temporary("strict-source-admission");
|
|
2558
|
+
let (store, registry, request) = fixture_sized(&root, false, Some(1024));
|
|
2559
|
+
let advance = |limit| {
|
|
2560
|
+
prepare_model_source_with_registry_budget(
|
|
2561
|
+
&store,
|
|
2562
|
+
&request,
|
|
2563
|
+
"fixture:registry",
|
|
2564
|
+
®istry,
|
|
2565
|
+
Some(limit),
|
|
2566
|
+
)
|
|
2567
|
+
.unwrap()
|
|
2568
|
+
};
|
|
2569
|
+
for limit in [0, 4095] {
|
|
2570
|
+
let held = advance(limit);
|
|
2571
|
+
assert!(!held.complete);
|
|
2572
|
+
assert_eq!(held.converted_roles, 0);
|
|
2573
|
+
assert_eq!(held.converted_bytes, 0);
|
|
2574
|
+
assert_eq!(held.largest_op_bytes, 0);
|
|
2575
|
+
assert_eq!(held.required_write_bytes, 4096);
|
|
2576
|
+
assert_eq!(held.deferred_ops, 2);
|
|
2577
|
+
}
|
|
2578
|
+
let first = advance(4096);
|
|
2579
|
+
assert!(!first.complete);
|
|
2580
|
+
assert_eq!(first.converted_bytes, 4096);
|
|
2581
|
+
assert_eq!(first.converted_roles, 1);
|
|
2582
|
+
assert_eq!(first.required_write_bytes, 0);
|
|
2583
|
+
let held = advance(0);
|
|
2584
|
+
assert_eq!(held.converted_bytes, 0);
|
|
2585
|
+
assert_eq!(held.resumed_roles, 1);
|
|
2586
|
+
assert_eq!(held.required_write_bytes, 4096);
|
|
2587
|
+
assert_eq!(held.checkpoints, first.checkpoints);
|
|
2588
|
+
let completed = advance(4096);
|
|
2589
|
+
assert!(completed.complete);
|
|
2590
|
+
assert_eq!(completed.converted_bytes, 4096);
|
|
2591
|
+
assert_eq!(completed.resumed_roles, 1);
|
|
2592
|
+
let replay = advance(0);
|
|
2593
|
+
assert!(replay.replayed && replay.complete);
|
|
2594
|
+
assert_eq!(replay.sources, completed.sources);
|
|
2595
|
+
assert_eq!(replay.converted_bytes, 0);
|
|
2596
|
+
assert_eq!(replay.required_write_bytes, 0);
|
|
2597
|
+
let clean = Store::init(&root.join("clean")).unwrap();
|
|
2598
|
+
let expected =
|
|
2599
|
+
prepare_model_source_with_registry(&clean, &request, "fixture:registry", ®istry)
|
|
2600
|
+
.unwrap();
|
|
2601
|
+
assert_eq!(completed.sources, expected.sources);
|
|
2602
|
+
release_model_source(&store, &request.operation_id).unwrap();
|
|
2603
|
+
release_model_source(&clean, &request.operation_id).unwrap();
|
|
2604
|
+
fs::remove_dir_all(root).unwrap();
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2529
2607
|
#[test]
|
|
2530
2608
|
fn one_pass_budget_is_shared_by_profiles_and_allows_one_oversized_op() {
|
|
2531
2609
|
for limit in [1024, 4096] {
|
|
@@ -444,6 +444,8 @@ pub struct Budget {
|
|
|
444
444
|
used: u64,
|
|
445
445
|
stopped: bool,
|
|
446
446
|
pub largest_op: u64,
|
|
447
|
+
strict: bool,
|
|
448
|
+
pub required_write_bytes: u64,
|
|
447
449
|
}
|
|
448
450
|
|
|
449
451
|
impl Budget {
|
|
@@ -453,12 +455,25 @@ impl Budget {
|
|
|
453
455
|
used: 0,
|
|
454
456
|
stopped: false,
|
|
455
457
|
largest_op: 0,
|
|
458
|
+
strict: false,
|
|
459
|
+
required_write_bytes: 0,
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
pub fn bounded(limit: u64) -> Self {
|
|
463
|
+
Self {
|
|
464
|
+
strict: true,
|
|
465
|
+
..Self::new(limit)
|
|
456
466
|
}
|
|
457
467
|
}
|
|
458
468
|
fn take(&mut self, bytes: u64) -> bool {
|
|
459
469
|
if self.stopped {
|
|
460
470
|
return false;
|
|
461
471
|
}
|
|
472
|
+
if self.strict && self.used == 0 && bytes > self.limit {
|
|
473
|
+
self.required_write_bytes = bytes;
|
|
474
|
+
self.stopped = true;
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
462
477
|
if self.used > 0 && bytes > self.limit.saturating_sub(self.used) {
|
|
463
478
|
self.stopped = true;
|
|
464
479
|
return false;
|
|
@@ -1085,6 +1085,8 @@ mod backfill_tests {
|
|
|
1085
1085
|
#[cfg(test)]
|
|
1086
1086
|
mod exit_wait_tests {
|
|
1087
1087
|
use super::*;
|
|
1088
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
1089
|
+
use std::sync::mpsc;
|
|
1088
1090
|
use std::sync::Arc;
|
|
1089
1091
|
use std::time::{Duration, Instant};
|
|
1090
1092
|
|
|
@@ -1093,11 +1095,24 @@ mod exit_wait_tests {
|
|
|
1093
1095
|
struct Paced<'a>(&'a [u8]);
|
|
1094
1096
|
impl Read for Paced<'_> {
|
|
1095
1097
|
fn read(&mut self, out: &mut [u8]) -> std::io::Result<usize> {
|
|
1096
|
-
std::thread::sleep(Duration::from_millis(5));
|
|
1097
1098
|
let take = out.len().min(4096);
|
|
1098
1099
|
self.0.read(&mut out[..take])
|
|
1099
1100
|
}
|
|
1100
1101
|
}
|
|
1102
|
+
struct Progress {
|
|
1103
|
+
activity: Arc<BackfillActivity>,
|
|
1104
|
+
elapsed_ms: Arc<AtomicU64>,
|
|
1105
|
+
}
|
|
1106
|
+
impl store::Progress for Progress {
|
|
1107
|
+
fn admitted(&self) {}
|
|
1108
|
+
fn moved(&self, bytes: u64) {
|
|
1109
|
+
// Advance time only after real bytes were copied and hashed. Thread
|
|
1110
|
+
// startup, filesystem scheduling and final fsync are not a stalled
|
|
1111
|
+
// transfer injected by this test.
|
|
1112
|
+
self.elapsed_ms.fetch_add(5, Ordering::SeqCst);
|
|
1113
|
+
self.activity.moved(bytes);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1101
1116
|
let root = std::env::temp_dir().join(format!(
|
|
1102
1117
|
"tensorfs-exit-moving-{}-{}",
|
|
1103
1118
|
std::process::id(),
|
|
@@ -1110,22 +1125,51 @@ mod exit_wait_tests {
|
|
|
1110
1125
|
Duration::from_millis(10),
|
|
1111
1126
|
)));
|
|
1112
1127
|
activity.offered();
|
|
1113
|
-
let
|
|
1128
|
+
let elapsed_ms = Arc::new(AtomicU64::new(0));
|
|
1129
|
+
let clock = elapsed_ms.clone();
|
|
1130
|
+
let started = Instant::now();
|
|
1131
|
+
let (sampled, observation) = mpsc::channel();
|
|
1132
|
+
activity.pending.lock().unwrap().1 =
|
|
1133
|
+
Ledger::with_clock(Duration::from_millis(10), move || {
|
|
1134
|
+
let elapsed = clock.load(Ordering::SeqCst);
|
|
1135
|
+
if elapsed >= 320 && std::thread::current().name() == Some("copy-wait") {
|
|
1136
|
+
let _ = sampled.send(());
|
|
1137
|
+
}
|
|
1138
|
+
started + Duration::from_millis(elapsed)
|
|
1139
|
+
});
|
|
1140
|
+
let observed = Progress {
|
|
1141
|
+
activity: activity.clone(),
|
|
1142
|
+
elapsed_ms: elapsed_ms.clone(),
|
|
1143
|
+
};
|
|
1114
1144
|
let target = destination.clone();
|
|
1115
1145
|
let wanted = object.clone();
|
|
1116
|
-
let
|
|
1146
|
+
let (finish, finishing) = mpsc::channel();
|
|
1117
1147
|
let copy = std::thread::spawn(move || {
|
|
1118
|
-
let result =
|
|
1119
|
-
|
|
1120
|
-
observed.finished();
|
|
1148
|
+
let result = publish_stream(&mut Paced(&data), &target, &wanted, Some(&observed));
|
|
1149
|
+
finishing.recv().unwrap();
|
|
1150
|
+
observed.activity.finished();
|
|
1121
1151
|
result
|
|
1122
1152
|
});
|
|
1123
|
-
|
|
1124
|
-
|
|
1153
|
+
let waiting = activity.clone();
|
|
1154
|
+
let waiter = std::thread::Builder::new()
|
|
1155
|
+
.name("copy-wait".into())
|
|
1156
|
+
.spawn(move || waiting.wait())
|
|
1157
|
+
.unwrap();
|
|
1158
|
+
// The wait must actually sample a pending copy after more than the initial
|
|
1159
|
+
// 60 ms floor, before finished() can make it return trivially. This timeout
|
|
1160
|
+
// is only a deadlock guard; it is not the liveness clock being qualified.
|
|
1161
|
+
let sampled_pending = observation.recv_timeout(Duration::from_secs(5));
|
|
1162
|
+
finish.send(()).unwrap();
|
|
1125
1163
|
assert!(
|
|
1126
|
-
|
|
1127
|
-
"
|
|
1164
|
+
sampled_pending.is_ok(),
|
|
1165
|
+
"wait never observed the moving copy"
|
|
1128
1166
|
);
|
|
1167
|
+
assert!(waiter.join().unwrap(), "a moving copy was abandoned");
|
|
1168
|
+
assert_eq!(copy.join().unwrap(), Ok(true));
|
|
1169
|
+
let state = activity.pending.lock().unwrap();
|
|
1170
|
+
assert!(Duration::from_millis(elapsed_ms.load(Ordering::SeqCst)) > state.1.floor());
|
|
1171
|
+
assert_eq!(state.1.still().moved, object.length);
|
|
1172
|
+
drop(state);
|
|
1129
1173
|
assert_eq!(
|
|
1130
1174
|
verify_path(&destination, CacheKind::Blob, &object, None),
|
|
1131
1175
|
Ok(())
|
|
@@ -137,6 +137,8 @@ pub struct Ledger {
|
|
|
137
137
|
sample: Duration,
|
|
138
138
|
worst_gap: Mutex<Duration>,
|
|
139
139
|
progress: Mutex<Progress>,
|
|
140
|
+
#[cfg(test)]
|
|
141
|
+
clock: Option<Box<dyn Fn() -> Instant + Send + Sync>>,
|
|
140
142
|
}
|
|
141
143
|
|
|
142
144
|
impl Ledger {
|
|
@@ -155,9 +157,30 @@ impl Ledger {
|
|
|
155
157
|
advanced: Instant::now(),
|
|
156
158
|
worst_still: Duration::ZERO,
|
|
157
159
|
}),
|
|
160
|
+
#[cfg(test)]
|
|
161
|
+
clock: None,
|
|
158
162
|
}
|
|
159
163
|
}
|
|
160
164
|
|
|
165
|
+
#[cfg(test)]
|
|
166
|
+
pub(crate) fn with_clock(
|
|
167
|
+
sample: Duration,
|
|
168
|
+
clock: impl Fn() -> Instant + Send + Sync + 'static,
|
|
169
|
+
) -> Self {
|
|
170
|
+
let mut ledger = Self::with_resolution(sample);
|
|
171
|
+
ledger.progress.lock().unwrap().advanced = clock();
|
|
172
|
+
ledger.clock = Some(Box::new(clock));
|
|
173
|
+
ledger
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
fn now(&self) -> Instant {
|
|
177
|
+
#[cfg(test)]
|
|
178
|
+
if let Some(clock) = &self.clock {
|
|
179
|
+
return clock();
|
|
180
|
+
}
|
|
181
|
+
Instant::now()
|
|
182
|
+
}
|
|
183
|
+
|
|
161
184
|
pub fn sample(&self) -> Duration {
|
|
162
185
|
self.sample
|
|
163
186
|
}
|
|
@@ -215,7 +238,7 @@ impl Ledger {
|
|
|
215
238
|
/// stream cannot raise the bar the next stream is judged by.
|
|
216
239
|
fn advance(&self, bytes: u64) {
|
|
217
240
|
let mut progress = self.progress.lock().unwrap();
|
|
218
|
-
let now =
|
|
241
|
+
let now = self.now();
|
|
219
242
|
let gap = now.saturating_duration_since(progress.advanced);
|
|
220
243
|
if gap > progress.worst_still {
|
|
221
244
|
progress.worst_still = gap;
|
|
@@ -238,7 +261,7 @@ impl Ledger {
|
|
|
238
261
|
let progress = self.progress.lock().unwrap();
|
|
239
262
|
Still {
|
|
240
263
|
moved: progress.moved,
|
|
241
|
-
silent: progress.advanced
|
|
264
|
+
silent: self.now().saturating_duration_since(progress.advanced),
|
|
242
265
|
}
|
|
243
266
|
}
|
|
244
267
|
}
|
|
@@ -1073,7 +1073,7 @@ impl PyStore {
|
|
|
1073
1073
|
|
|
1074
1074
|
/// Advance the exact source plan over verified physical carriers. Checkpoint heads are
|
|
1075
1075
|
/// local progress until the RecordOwner acknowledges their complete upload.
|
|
1076
|
-
#[pyo3(signature = (operation_id, source_selection_digest, profiles, roster, landed, *, checkpoints=Vec::new(), adopt_from_operation_id=None, registry=None))]
|
|
1076
|
+
#[pyo3(signature = (operation_id, source_selection_digest, profiles, roster, landed, *, checkpoints=Vec::new(), adopt_from_operation_id=None, registry=None, write_budget_bytes=None))]
|
|
1077
1077
|
#[allow(clippy::too_many_arguments, clippy::type_complexity)]
|
|
1078
1078
|
fn prepare_model_source<'p>(
|
|
1079
1079
|
&self,
|
|
@@ -1086,6 +1086,7 @@ impl PyStore {
|
|
|
1086
1086
|
checkpoints: Vec<(String, String, u64)>,
|
|
1087
1087
|
adopt_from_operation_id: Option<String>,
|
|
1088
1088
|
registry: Option<Vec<u8>>,
|
|
1089
|
+
write_budget_bytes: Option<u64>,
|
|
1089
1090
|
) -> PyResult<Bound<'p, PyDict>> {
|
|
1090
1091
|
let checkpoints = checkpoints
|
|
1091
1092
|
.into_iter()
|
|
@@ -1126,14 +1127,21 @@ impl PyStore {
|
|
|
1126
1127
|
adopt_from_operation_id,
|
|
1127
1128
|
};
|
|
1128
1129
|
let prepared = py
|
|
1129
|
-
.detach(||
|
|
1130
|
-
|
|
1130
|
+
.detach(|| {
|
|
1131
|
+
let (locator, bytes) = match ®istry {
|
|
1132
|
+
Some(bytes) => ("supplied:native-registry", bytes.as_slice()),
|
|
1133
|
+
None => (
|
|
1134
|
+
model_source::BUILTIN_REGISTRY,
|
|
1135
|
+
model_source::BUILTIN_REGISTRY_BYTES,
|
|
1136
|
+
),
|
|
1137
|
+
};
|
|
1138
|
+
model_source::prepare_model_source_with_registry_budget(
|
|
1131
1139
|
&self.store,
|
|
1132
1140
|
&request,
|
|
1133
|
-
|
|
1141
|
+
locator,
|
|
1134
1142
|
bytes,
|
|
1135
|
-
|
|
1136
|
-
|
|
1143
|
+
write_budget_bytes,
|
|
1144
|
+
)
|
|
1137
1145
|
})
|
|
1138
1146
|
.or_refuse(py)?;
|
|
1139
1147
|
let out = PyDict::new(py);
|
|
@@ -1145,6 +1153,11 @@ impl PyStore {
|
|
|
1145
1153
|
out.set_item("deferred_ops", prepared.deferred_ops)?;
|
|
1146
1154
|
out.set_item("converted_bytes", prepared.converted_bytes)?;
|
|
1147
1155
|
out.set_item("largest_op_bytes", prepared.largest_op_bytes)?;
|
|
1156
|
+
out.set_item("required_write_bytes", prepared.required_write_bytes)?;
|
|
1157
|
+
out.set_item(
|
|
1158
|
+
"write_interval_bytes",
|
|
1159
|
+
tensorfs_core::durability::INTERVAL_BYTES,
|
|
1160
|
+
)?;
|
|
1148
1161
|
let rows = PyList::empty(py);
|
|
1149
1162
|
for source in prepared.sources {
|
|
1150
1163
|
let row = PyDict::new(py);
|
|
@@ -1208,6 +1221,7 @@ impl PyStore {
|
|
|
1208
1221
|
checkpoints,
|
|
1209
1222
|
adopt_from_operation_id,
|
|
1210
1223
|
registry,
|
|
1224
|
+
None,
|
|
1211
1225
|
)
|
|
1212
1226
|
}
|
|
1213
1227
|
|
|
@@ -2798,6 +2812,56 @@ impl PyLease {
|
|
|
2798
2812
|
Ok(PyBytes::new(py, &bytes))
|
|
2799
2813
|
}
|
|
2800
2814
|
|
|
2815
|
+
/// Read a bounded semantic part from a header already covered by this read lease.
|
|
2816
|
+
/// This is the same native range reader used by derived writers, without a writer.
|
|
2817
|
+
#[allow(clippy::too_many_arguments)]
|
|
2818
|
+
fn read_part_into(
|
|
2819
|
+
&self,
|
|
2820
|
+
py: Python<'_>,
|
|
2821
|
+
header: &[u8],
|
|
2822
|
+
component: &str,
|
|
2823
|
+
key: &str,
|
|
2824
|
+
role: &str,
|
|
2825
|
+
off: u64,
|
|
2826
|
+
into: &Bound<'_, PyAny>,
|
|
2827
|
+
) -> PyResult<()> {
|
|
2828
|
+
let header_ref = ObjectRef::of(header);
|
|
2829
|
+
let header = Header::parse(header).or_refuse(py)?;
|
|
2830
|
+
let name = format!("{component}/{key}/{role}");
|
|
2831
|
+
let part = header
|
|
2832
|
+
.components
|
|
2833
|
+
.iter()
|
|
2834
|
+
.find(|(candidate, _)| candidate == component)
|
|
2835
|
+
.and_then(|(_, tensors)| tensors.iter().find(|(candidate, _)| candidate == key))
|
|
2836
|
+
.and_then(|(_, tensor)| tensor.parts.iter().find(|(candidate, _)| candidate == role))
|
|
2837
|
+
.map(|(_, part)| part)
|
|
2838
|
+
.ok_or_else(|| missing(py, "model tensor part", &name))?;
|
|
2839
|
+
let mut buffer = Writable::new(py, into)?;
|
|
2840
|
+
let destination = buffer.slice();
|
|
2841
|
+
let guard = self.lease.lock_py_attached(py).unwrap();
|
|
2842
|
+
let lease = guard.as_ref().ok_or_else(|| released(py))?;
|
|
2843
|
+
lease
|
|
2844
|
+
.covers(&ObjectRange {
|
|
2845
|
+
obj: header_ref,
|
|
2846
|
+
off: 0,
|
|
2847
|
+
len: 0,
|
|
2848
|
+
})
|
|
2849
|
+
.or_refuse(py)?;
|
|
2850
|
+
py.detach(|| {
|
|
2851
|
+
lease.recheck(&self.meta)?;
|
|
2852
|
+
read::read_part_into(
|
|
2853
|
+
lease,
|
|
2854
|
+
&name,
|
|
2855
|
+
part,
|
|
2856
|
+
off,
|
|
2857
|
+
destination.len() as u64,
|
|
2858
|
+
destination,
|
|
2859
|
+
)?;
|
|
2860
|
+
lease.recheck(&self.meta)
|
|
2861
|
+
})
|
|
2862
|
+
.or_refuse(py)
|
|
2863
|
+
}
|
|
2864
|
+
|
|
2801
2865
|
/// **The streaming shape.** One plan, a ring of CALLER-owned slots, and a reader pool
|
|
2802
2866
|
/// that never stops at a batch boundary.
|
|
2803
2867
|
///
|
|
@@ -108,6 +108,8 @@ class PreparedModelSource(TypedDict):
|
|
|
108
108
|
deferred_ops: int
|
|
109
109
|
converted_bytes: int
|
|
110
110
|
largest_op_bytes: int
|
|
111
|
+
required_write_bytes: int
|
|
112
|
+
write_interval_bytes: int
|
|
111
113
|
|
|
112
114
|
class ManifestRow(TypedDict):
|
|
113
115
|
manifest_id: str
|
|
@@ -351,6 +353,9 @@ class ReadLease:
|
|
|
351
353
|
self, object: str, object_length: int, off: int, length: int, into: object
|
|
352
354
|
) -> None: ...
|
|
353
355
|
def read_asset(self, header: _Bytes, name: str, max_bytes: int = 67108864) -> _Bytes: ...
|
|
356
|
+
def read_part_into(
|
|
357
|
+
self, header: _Bytes, component: str, key: str, role: str, off: int, into: object
|
|
358
|
+
) -> None: ...
|
|
354
359
|
def stream(
|
|
355
360
|
self,
|
|
356
361
|
plan: ReadPlan,
|
|
@@ -519,6 +524,7 @@ class Store:
|
|
|
519
524
|
checkpoints: Sequence[tuple[str, str, int]] = ...,
|
|
520
525
|
adopt_from_operation_id: str | None = ...,
|
|
521
526
|
registry: bytes | None = None,
|
|
527
|
+
write_budget_bytes: int | None = None,
|
|
522
528
|
) -> PreparedModelSource: ...
|
|
523
529
|
def checkpoint_page(
|
|
524
530
|
self, head_id: str, head_length: int, *, operation_id: str, slot: str,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/examples/optional-cache-backfill.md
RENAMED
|
File without changes
|
|
File without changes
|
{tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/examples/resident_conversion_profile.rs
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor
RENAMED
|
File without changes
|
{tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json
RENAMED
|
File without changes
|
{tensorfs-0.3.32 → tensorfs-0.3.34}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|