tensorfs 0.3.30__tar.gz → 0.3.31__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.30 → tensorfs-0.3.31}/Cargo.lock +2 -2
- {tensorfs-0.3.30 → tensorfs-0.3.31}/Cargo.toml +1 -1
- {tensorfs-0.3.30 → tensorfs-0.3.31}/PKG-INFO +1 -1
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/bin/tfs/fetch.rs +3 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/repo_cache.rs +244 -16
- {tensorfs-0.3.30 → tensorfs-0.3.31}/pyproject.toml +1 -1
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/Cargo.toml +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/build.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/examples/h3-swiglu-layout.md +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/examples/optional-cache-backfill.md +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/examples/resident-conversion.md +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/examples/resident_conversion_profile.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/b64.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/bin/tfs/ckpt.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/bin/tfs/conform.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/bin/tfs/ingest.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/bin/tfs/main.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/bin/tfs/rd.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/bin/tfs/repo.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/bin/tfs/snap.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/bin/tfs/watch.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/canon.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/capability.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/catalog.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/cbor.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/checkpoint.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/corpus.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/derived/adoption.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/derived/graft_tests.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/derived/progress.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/derived/resume_tests.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/derived/retention.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/derived/roots.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/derived.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/dtype.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/durability.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/err.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/fetch.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/fill.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/fit.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/gc.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/header.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/home.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ids.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ingest/carrier.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ingest/convert.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ingest/fingerprint.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ingest/gguf.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ingest/journal.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ingest/mod.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ingest/preflight.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ingest/source.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ingest/stamp.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/ingest/transaction.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/jcs.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/lib.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/limits.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/machine.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/manifest.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/meta.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/project.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/providers.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/read.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/receipt.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/reclaim.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/registry.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/relation.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/repository.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/sha256.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/source_artifact.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/spec.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/staging.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/storage.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/store.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/api.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/http.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/hub.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/ledger.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/mod.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/policy.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/prefix.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/pull.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/push.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/ranged.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/transport/tests.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/src/vectors.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fetch_url_progress.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fit.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/h3_swiglu.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/home.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/import_tree.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/instrument_profiles.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/liveness.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/read_plan.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/reclaim.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/reclaim_run.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/repo_cache.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/repo_store.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/resident_admission.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/sha256_backends.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/sharded_cas.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/source_artifacts.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/source_plan.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/source_prefixes.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/source_preflight.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/staging.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/store_boundaries.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/tensor_schema.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/version.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/write_through.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-py/Cargo.toml +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-py/src/docs.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-py/src/errors.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-py/src/lib.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-py/src/store.rs +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/python/tensorfs/__init__.py +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/python/tensorfs/_ext.pyi +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/python/tensorfs/errors.py +0 -0
- {tensorfs-0.3.30 → tensorfs-0.3.31}/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.31"
|
|
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.31"
|
|
408
408
|
dependencies = [
|
|
409
409
|
"pyo3",
|
|
410
410
|
"tensorfs-core",
|
|
@@ -302,6 +302,9 @@ fn pull_with_budget(
|
|
|
302
302
|
request.on_object = Some(&observe);
|
|
303
303
|
let report = ok!(transport::pull(&request));
|
|
304
304
|
progress.settle();
|
|
305
|
+
if !tensorfs_core::repo_cache::finish_optional_backfills() {
|
|
306
|
+
eprintln!("repo cache backfill stopped advancing; local fetch remains complete");
|
|
307
|
+
}
|
|
305
308
|
println!(
|
|
306
309
|
"model {}@{} lane {}",
|
|
307
310
|
report.model, report.release, report.lane
|
|
@@ -16,6 +16,7 @@ use crate::limits;
|
|
|
16
16
|
use crate::manifest::Manifest;
|
|
17
17
|
use crate::sha256::{self, Sha256};
|
|
18
18
|
use crate::store::{self, Fault, Store};
|
|
19
|
+
use crate::transport::Ledger;
|
|
19
20
|
|
|
20
21
|
/// The two immutable repository object namespaces a cache may contain.
|
|
21
22
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
@@ -101,16 +102,21 @@ impl RepoObjectCache {
|
|
|
101
102
|
kind: CacheKind,
|
|
102
103
|
object: &ObjectRef,
|
|
103
104
|
) -> bool {
|
|
104
|
-
static BACKFILLS: std::sync::OnceLock<Backfills> = std::sync::OnceLock::new();
|
|
105
105
|
let pool = BACKFILLS.get_or_init(Backfills::start);
|
|
106
|
-
pool.
|
|
106
|
+
pool.activity.offered();
|
|
107
|
+
let sent = pool
|
|
108
|
+
.sender
|
|
107
109
|
.try_send(Backfill {
|
|
108
110
|
cache: self.clone(),
|
|
109
111
|
source: source.root().to_path_buf(),
|
|
110
112
|
kind,
|
|
111
113
|
object: object.clone(),
|
|
112
114
|
})
|
|
113
|
-
.is_ok()
|
|
115
|
+
.is_ok();
|
|
116
|
+
if !sent {
|
|
117
|
+
pool.activity.finished();
|
|
118
|
+
}
|
|
119
|
+
sent
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
/// The one cross-language layout contract. `digest` is bare lowercase SHA-256.
|
|
@@ -218,10 +224,20 @@ impl RepoObjectCache {
|
|
|
218
224
|
source: &Store,
|
|
219
225
|
kind: CacheKind,
|
|
220
226
|
want: &ObjectRef,
|
|
227
|
+
) -> Result<CacheWrite> {
|
|
228
|
+
self.backfill_observed(source, kind, want, None)
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
fn backfill_observed(
|
|
232
|
+
&self,
|
|
233
|
+
source: &Store,
|
|
234
|
+
kind: CacheKind,
|
|
235
|
+
want: &ObjectRef,
|
|
236
|
+
progress: Option<&dyn store::Progress>,
|
|
221
237
|
) -> Result<CacheWrite> {
|
|
222
238
|
let destination = self.path(kind, &want.sha256)?;
|
|
223
239
|
if destination.exists() {
|
|
224
|
-
return Ok(match verify_path(&destination, kind, want) {
|
|
240
|
+
return Ok(match verify_path(&destination, kind, want, progress) {
|
|
225
241
|
Ok(()) => CacheWrite::Present,
|
|
226
242
|
Err(_) => CacheWrite::Unavailable,
|
|
227
243
|
});
|
|
@@ -229,18 +245,18 @@ impl RepoObjectCache {
|
|
|
229
245
|
let result = match kind {
|
|
230
246
|
CacheKind::Blob => match source.open_verified(&want.sha256) {
|
|
231
247
|
Ok(mut file) if file.len() == want.length => {
|
|
232
|
-
publish_stream(&mut file, &destination, want)
|
|
248
|
+
publish_stream(&mut file, &destination, want, progress)
|
|
233
249
|
}
|
|
234
250
|
Ok(_) | Err(_) => Err(SoftFailure::Unavailable),
|
|
235
251
|
},
|
|
236
252
|
CacheKind::Manifest => match source.read_manifest(want) {
|
|
237
|
-
Ok(manifest) => publish_bytes(&manifest.canonical_bytes(), &destination),
|
|
253
|
+
Ok(manifest) => publish_bytes(&manifest.canonical_bytes(), &destination, progress),
|
|
238
254
|
Err(_) => Err(SoftFailure::Unavailable),
|
|
239
255
|
},
|
|
240
256
|
};
|
|
241
257
|
Ok(match result {
|
|
242
258
|
Ok(true) => CacheWrite::Stored,
|
|
243
|
-
Ok(false) => match verify_path(&destination, kind, want) {
|
|
259
|
+
Ok(false) => match verify_path(&destination, kind, want, progress) {
|
|
244
260
|
Ok(()) => CacheWrite::Present,
|
|
245
261
|
Err(_) => CacheWrite::Unavailable,
|
|
246
262
|
},
|
|
@@ -260,19 +276,99 @@ struct Backfill {
|
|
|
260
276
|
object: ObjectRef,
|
|
261
277
|
}
|
|
262
278
|
|
|
279
|
+
static BACKFILLS: std::sync::OnceLock<Backfills> = std::sync::OnceLock::new();
|
|
280
|
+
|
|
263
281
|
struct Backfills {
|
|
264
282
|
sender: std::sync::mpsc::SyncSender<Backfill>,
|
|
283
|
+
activity: std::sync::Arc<BackfillActivity>,
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
struct BackfillActivity {
|
|
287
|
+
pending: std::sync::Mutex<(usize, Ledger)>,
|
|
288
|
+
changed: std::sync::Condvar,
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
impl BackfillActivity {
|
|
292
|
+
fn new(ledger: Ledger) -> Self {
|
|
293
|
+
Self {
|
|
294
|
+
pending: std::sync::Mutex::new((0, ledger)),
|
|
295
|
+
changed: std::sync::Condvar::new(),
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
fn offered(&self) {
|
|
300
|
+
let mut state = self
|
|
301
|
+
.pending
|
|
302
|
+
.lock()
|
|
303
|
+
.unwrap_or_else(|error| error.into_inner());
|
|
304
|
+
if state.0 == 0 {
|
|
305
|
+
state.1 = Ledger::with_resolution(state.1.sample());
|
|
306
|
+
}
|
|
307
|
+
state.0 += 1;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
fn finished(&self) {
|
|
311
|
+
let mut state = self
|
|
312
|
+
.pending
|
|
313
|
+
.lock()
|
|
314
|
+
.unwrap_or_else(|error| error.into_inner());
|
|
315
|
+
state.0 -= 1;
|
|
316
|
+
state.1.answered();
|
|
317
|
+
self.changed.notify_all();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
fn wait(&self) -> bool {
|
|
321
|
+
let mut state = self
|
|
322
|
+
.pending
|
|
323
|
+
.lock()
|
|
324
|
+
.unwrap_or_else(|error| error.into_inner());
|
|
325
|
+
while state.0 != 0 {
|
|
326
|
+
let patience = state.1.stillness().saturating_sub(state.1.still().silent);
|
|
327
|
+
if patience.is_zero() {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
state = self
|
|
331
|
+
.changed
|
|
332
|
+
.wait_timeout(state, patience)
|
|
333
|
+
.unwrap_or_else(|error| error.into_inner())
|
|
334
|
+
.0;
|
|
335
|
+
}
|
|
336
|
+
true
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
impl store::Progress for BackfillActivity {
|
|
341
|
+
fn admitted(&self) {}
|
|
342
|
+
|
|
343
|
+
fn moved(&self, bytes: u64) {
|
|
344
|
+
self.pending
|
|
345
|
+
.lock()
|
|
346
|
+
.unwrap_or_else(|error| error.into_inner())
|
|
347
|
+
.1
|
|
348
|
+
.moved(bytes);
|
|
349
|
+
self.changed.notify_all();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/// Before a short-lived CLI exits, give its existing optional write pool time to
|
|
354
|
+
/// finish while real copy/verification progress continues. A stalled cache only
|
|
355
|
+
/// ends this wait; it never changes the completed local fetch or joins its worker.
|
|
356
|
+
/// Long-lived Store callers keep the original asynchronous behavior.
|
|
357
|
+
pub fn finish_optional_backfills() -> bool {
|
|
358
|
+
BACKFILLS.get().is_none_or(|pool| pool.activity.wait())
|
|
265
359
|
}
|
|
266
360
|
|
|
267
361
|
impl Backfills {
|
|
268
362
|
fn start() -> Self {
|
|
269
363
|
let (sender, receiver) = std::sync::mpsc::sync_channel::<Backfill>(BACKFILL_QUEUE);
|
|
270
364
|
let receiver = std::sync::Arc::new(std::sync::Mutex::new(receiver));
|
|
365
|
+
let activity = std::sync::Arc::new(BackfillActivity::new(Ledger::new()));
|
|
271
366
|
// One pool for the process, never a new blocked thread set for every pull.
|
|
272
367
|
// Workers are detached: process exit may leave cache temps, never canonical
|
|
273
368
|
// partial objects. Dropping a pull must not join a stalled FUSE syscall.
|
|
274
369
|
for _ in 0..STREAMS {
|
|
275
370
|
let receiver = receiver.clone();
|
|
371
|
+
let activity = activity.clone();
|
|
276
372
|
let _ = std::thread::Builder::new()
|
|
277
373
|
.name("tfs-cache-write".into())
|
|
278
374
|
.spawn(move || loop {
|
|
@@ -287,11 +383,18 @@ impl Backfills {
|
|
|
287
383
|
// hold: a source reclaimed before it opens is simply a missed cache fill.
|
|
288
384
|
// Once opened, the verified descriptor pins that inode through the copy.
|
|
289
385
|
if let Ok(store) = Store::open(&work.source) {
|
|
290
|
-
let
|
|
386
|
+
let store = store.observed(activity.clone());
|
|
387
|
+
let _ = work.cache.backfill_observed(
|
|
388
|
+
&store,
|
|
389
|
+
work.kind,
|
|
390
|
+
&work.object,
|
|
391
|
+
Some(activity.as_ref()),
|
|
392
|
+
);
|
|
291
393
|
}
|
|
394
|
+
activity.finished();
|
|
292
395
|
});
|
|
293
396
|
}
|
|
294
|
-
Self { sender }
|
|
397
|
+
Self { sender, activity }
|
|
295
398
|
}
|
|
296
399
|
}
|
|
297
400
|
|
|
@@ -344,27 +447,33 @@ fn verify_path(
|
|
|
344
447
|
path: &Path,
|
|
345
448
|
kind: CacheKind,
|
|
346
449
|
want: &ObjectRef,
|
|
450
|
+
progress: Option<&dyn store::Progress>,
|
|
347
451
|
) -> std::result::Result<(), SoftFailure> {
|
|
348
452
|
match kind {
|
|
349
453
|
CacheKind::Blob => {
|
|
350
454
|
let mut file = open_exact(path, want.length)?;
|
|
351
455
|
let mut sink = std::io::sink();
|
|
352
|
-
stream_exact(&mut file, &mut sink, want)
|
|
456
|
+
stream_exact(&mut file, &mut sink, want, progress)
|
|
353
457
|
}
|
|
354
458
|
CacheKind::Manifest => read_manifest(path, want).map(|_| ()),
|
|
355
459
|
}
|
|
356
460
|
}
|
|
357
461
|
|
|
358
|
-
fn publish_bytes(
|
|
462
|
+
fn publish_bytes(
|
|
463
|
+
bytes: &[u8],
|
|
464
|
+
destination: &Path,
|
|
465
|
+
progress: Option<&dyn store::Progress>,
|
|
466
|
+
) -> std::result::Result<bool, SoftFailure> {
|
|
359
467
|
let mut source = bytes;
|
|
360
468
|
let want = ObjectRef::of(bytes);
|
|
361
|
-
publish_stream(&mut source, destination, &want)
|
|
469
|
+
publish_stream(&mut source, destination, &want, progress)
|
|
362
470
|
}
|
|
363
471
|
|
|
364
472
|
fn publish_stream(
|
|
365
473
|
source: &mut impl Read,
|
|
366
474
|
destination: &Path,
|
|
367
475
|
want: &ObjectRef,
|
|
476
|
+
progress: Option<&dyn store::Progress>,
|
|
368
477
|
) -> std::result::Result<bool, SoftFailure> {
|
|
369
478
|
let Some(directory) = destination.parent() else {
|
|
370
479
|
return Err(SoftFailure::Unavailable);
|
|
@@ -385,7 +494,7 @@ fn publish_stream(
|
|
|
385
494
|
.mode(0o600)
|
|
386
495
|
.open(&temporary)
|
|
387
496
|
.map_err(|_| SoftFailure::Unavailable)?;
|
|
388
|
-
if let Err(error) = stream_exact(source, &mut output, want) {
|
|
497
|
+
if let Err(error) = stream_exact(source, &mut output, want, progress) {
|
|
389
498
|
let _ = fs::remove_file(&temporary);
|
|
390
499
|
return Err(error);
|
|
391
500
|
}
|
|
@@ -414,6 +523,7 @@ fn stream_exact(
|
|
|
414
523
|
source: &mut impl Read,
|
|
415
524
|
destination: &mut impl Write,
|
|
416
525
|
want: &ObjectRef,
|
|
526
|
+
progress: Option<&dyn store::Progress>,
|
|
417
527
|
) -> std::result::Result<(), SoftFailure> {
|
|
418
528
|
let mut digest = Sha256::new();
|
|
419
529
|
let mut length = 0u64;
|
|
@@ -429,6 +539,9 @@ fn stream_exact(
|
|
|
429
539
|
destination
|
|
430
540
|
.write_all(&buffer[..read])
|
|
431
541
|
.map_err(|_| SoftFailure::Unavailable)?;
|
|
542
|
+
if let Some(progress) = progress {
|
|
543
|
+
progress.moved(read as u64);
|
|
544
|
+
}
|
|
432
545
|
length = length
|
|
433
546
|
.checked_add(read as u64)
|
|
434
547
|
.ok_or(SoftFailure::Corrupt)?;
|
|
@@ -918,10 +1031,16 @@ mod backfill_tests {
|
|
|
918
1031
|
first: true,
|
|
919
1032
|
length: object.length,
|
|
920
1033
|
};
|
|
921
|
-
assert_eq!(
|
|
1034
|
+
assert_eq!(
|
|
1035
|
+
publish_stream(&mut input, &destination, &object, None),
|
|
1036
|
+
Ok(true)
|
|
1037
|
+
);
|
|
922
1038
|
assert!(!source.contains(&object.sha256));
|
|
923
1039
|
assert_eq!(fs::read(&destination).unwrap(), bytes);
|
|
924
|
-
assert_eq!(
|
|
1040
|
+
assert_eq!(
|
|
1041
|
+
verify_path(&destination, CacheKind::Blob, &object, None),
|
|
1042
|
+
Ok(())
|
|
1043
|
+
);
|
|
925
1044
|
assert_eq!(
|
|
926
1045
|
fs::read_dir(destination.parent().unwrap()).unwrap().count(),
|
|
927
1046
|
1
|
|
@@ -950,7 +1069,7 @@ mod backfill_tests {
|
|
|
950
1069
|
}
|
|
951
1070
|
}
|
|
952
1071
|
assert_eq!(
|
|
953
|
-
publish_stream(&mut Fails(false), &destination, &object),
|
|
1072
|
+
publish_stream(&mut Fails(false), &destination, &object, None),
|
|
954
1073
|
Err(SoftFailure::Unavailable)
|
|
955
1074
|
);
|
|
956
1075
|
assert!(!destination.exists());
|
|
@@ -962,3 +1081,112 @@ mod backfill_tests {
|
|
|
962
1081
|
fs::remove_dir_all(root).unwrap();
|
|
963
1082
|
}
|
|
964
1083
|
}
|
|
1084
|
+
|
|
1085
|
+
#[cfg(test)]
|
|
1086
|
+
mod exit_wait_tests {
|
|
1087
|
+
use super::*;
|
|
1088
|
+
use std::sync::Arc;
|
|
1089
|
+
use std::time::{Duration, Instant};
|
|
1090
|
+
|
|
1091
|
+
#[test]
|
|
1092
|
+
fn advancing_copy_outlives_the_initial_silence_allowance() {
|
|
1093
|
+
struct Paced<'a>(&'a [u8]);
|
|
1094
|
+
impl Read for Paced<'_> {
|
|
1095
|
+
fn read(&mut self, out: &mut [u8]) -> std::io::Result<usize> {
|
|
1096
|
+
std::thread::sleep(Duration::from_millis(5));
|
|
1097
|
+
let take = out.len().min(4096);
|
|
1098
|
+
self.0.read(&mut out[..take])
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
let root = std::env::temp_dir().join(format!(
|
|
1102
|
+
"tensorfs-exit-moving-{}-{}",
|
|
1103
|
+
std::process::id(),
|
|
1104
|
+
store::now_nanos_unique()
|
|
1105
|
+
));
|
|
1106
|
+
let data = vec![7; 256 << 10];
|
|
1107
|
+
let object = ObjectRef::of(&data);
|
|
1108
|
+
let destination = root.join(&object.sha256);
|
|
1109
|
+
let activity = Arc::new(BackfillActivity::new(Ledger::with_resolution(
|
|
1110
|
+
Duration::from_millis(10),
|
|
1111
|
+
)));
|
|
1112
|
+
activity.offered();
|
|
1113
|
+
let observed = activity.clone();
|
|
1114
|
+
let target = destination.clone();
|
|
1115
|
+
let wanted = object.clone();
|
|
1116
|
+
let started = Instant::now();
|
|
1117
|
+
let copy = std::thread::spawn(move || {
|
|
1118
|
+
let result =
|
|
1119
|
+
publish_stream(&mut Paced(&data), &target, &wanted, Some(observed.as_ref()));
|
|
1120
|
+
observed.finished();
|
|
1121
|
+
result
|
|
1122
|
+
});
|
|
1123
|
+
assert!(activity.wait(), "a moving copy was abandoned");
|
|
1124
|
+
assert_eq!(copy.join().unwrap(), Ok(true));
|
|
1125
|
+
assert!(
|
|
1126
|
+
started.elapsed() > Duration::from_millis(60),
|
|
1127
|
+
"copy never outlived the initial silence floor"
|
|
1128
|
+
);
|
|
1129
|
+
assert_eq!(
|
|
1130
|
+
verify_path(&destination, CacheKind::Blob, &object, None),
|
|
1131
|
+
Ok(())
|
|
1132
|
+
);
|
|
1133
|
+
fs::remove_dir_all(root).unwrap();
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
#[test]
|
|
1137
|
+
fn stalled_optional_copy_stops_waiting_without_joining_its_worker() {
|
|
1138
|
+
let root = std::env::temp_dir().join(format!(
|
|
1139
|
+
"tensorfs-exit-stalled-{}-{}",
|
|
1140
|
+
std::process::id(),
|
|
1141
|
+
store::now_nanos_unique()
|
|
1142
|
+
));
|
|
1143
|
+
let source = Store::init(&root.join("source")).unwrap();
|
|
1144
|
+
let object = source
|
|
1145
|
+
.put_stream(&mut b"still local".as_slice(), None, &Fault::default())
|
|
1146
|
+
.unwrap()
|
|
1147
|
+
.obj;
|
|
1148
|
+
let cache = RepoObjectCache::new(root.join("cache"));
|
|
1149
|
+
let fifo = cache.path(CacheKind::Blob, &object.sha256).unwrap();
|
|
1150
|
+
fs::create_dir_all(fifo.parent().unwrap()).unwrap();
|
|
1151
|
+
assert!(std::process::Command::new("mkfifo")
|
|
1152
|
+
.arg(&fifo)
|
|
1153
|
+
.status()
|
|
1154
|
+
.unwrap()
|
|
1155
|
+
.success());
|
|
1156
|
+
let activity = Arc::new(BackfillActivity::new(Ledger::with_resolution(
|
|
1157
|
+
Duration::from_millis(10),
|
|
1158
|
+
)));
|
|
1159
|
+
activity.offered();
|
|
1160
|
+
let observed = activity.clone();
|
|
1161
|
+
let wanted = object.clone();
|
|
1162
|
+
let store = source.clone();
|
|
1163
|
+
let worker = std::thread::spawn(move || {
|
|
1164
|
+
let result =
|
|
1165
|
+
cache.backfill_observed(&store, CacheKind::Blob, &wanted, Some(observed.as_ref()));
|
|
1166
|
+
observed.finished();
|
|
1167
|
+
result
|
|
1168
|
+
});
|
|
1169
|
+
assert!(
|
|
1170
|
+
!activity.wait(),
|
|
1171
|
+
"blocked optional cache never produced a no-progress verdict"
|
|
1172
|
+
);
|
|
1173
|
+
assert!(
|
|
1174
|
+
!worker.is_finished(),
|
|
1175
|
+
"the wait returned by joining the stalled cache worker"
|
|
1176
|
+
);
|
|
1177
|
+
assert!(
|
|
1178
|
+
source.record_valid(&object.sha256).is_ok(),
|
|
1179
|
+
"cache stall invalidated completed local bytes"
|
|
1180
|
+
);
|
|
1181
|
+
let unblock = OpenOptions::new()
|
|
1182
|
+
.read(true)
|
|
1183
|
+
.write(true)
|
|
1184
|
+
.custom_flags(0x800)
|
|
1185
|
+
.open(&fifo)
|
|
1186
|
+
.unwrap();
|
|
1187
|
+
assert_eq!(worker.join().unwrap().unwrap(), CacheWrite::Unavailable);
|
|
1188
|
+
drop(unblock);
|
|
1189
|
+
assert!(activity.wait());
|
|
1190
|
+
fs::remove_dir_all(root).unwrap();
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/examples/optional-cache-backfill.md
RENAMED
|
File without changes
|
|
File without changes
|
{tensorfs-0.3.30 → tensorfs-0.3.31}/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
|
|
File without changes
|
{tensorfs-0.3.30 → tensorfs-0.3.31}/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.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor
RENAMED
|
File without changes
|
{tensorfs-0.3.30 → tensorfs-0.3.31}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json
RENAMED
|
File without changes
|
{tensorfs-0.3.30 → tensorfs-0.3.31}/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
|
|
File without changes
|
|
File without changes
|