tensorfs 0.3.43__tar.gz → 0.3.44__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.43 → tensorfs-0.3.44}/Cargo.lock +2 -2
- {tensorfs-0.3.43 → tensorfs-0.3.44}/Cargo.toml +1 -1
- {tensorfs-0.3.43 → tensorfs-0.3.44}/PKG-INFO +5 -1
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/http.rs +37 -3
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/tests.rs +130 -4
- {tensorfs-0.3.43 → tensorfs-0.3.44}/pyproject.toml +4 -1
- {tensorfs-0.3.43 → tensorfs-0.3.44}/python/tensorfs/__init__.py +25 -3
- tensorfs-0.3.44/python/tensorfs/_tensors.py +498 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/Cargo.toml +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/build.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/examples/h3-swiglu-layout.md +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/examples/optional-cache-backfill.md +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/examples/resident-conversion.md +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/examples/resident_conversion_profile.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/b64.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/bin/tfs/ckpt.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/bin/tfs/conform.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/bin/tfs/fetch.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/bin/tfs/ingest.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/bin/tfs/main.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/bin/tfs/rd.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/bin/tfs/repo.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/bin/tfs/snap.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/bin/tfs/watch.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/cache_roots.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/canon.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/capability.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/catalog.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/cbor.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/checkpoint.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/checkpoint_root.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/corpus.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/derived/adoption.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/derived/estimate.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/derived/graft_tests.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/derived/progress.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/derived/resume_tests.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/derived/retention.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/derived/roots.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/derived.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/dtype.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/durability.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/err.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/fetch.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/fill.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/fit.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/gc.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/header.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/home.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ids.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/carrier.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/convert.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/fingerprint.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/gguf.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/h3_lora.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/journal.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/mod.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/preflight.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/source.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/stamp.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/ingest/transaction.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/jcs.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/lib.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/limits.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/machine.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/manifest.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/meta.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/project.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/providers.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/read.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/receipt.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/reclaim.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/registry.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/relation.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/repo_cache.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/repository.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/sha256.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/source_artifact.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/spec.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/staging.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/storage.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/store.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/api.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/hub.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/ledger.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/mod.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/policy.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/prefix.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/pull.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/push.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/transport/ranged.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/src/vectors.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/checkpoint_roots.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/fetch_url_progress.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/fit.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/h3_lora.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/h3_swiglu.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/home.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/import_tree.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/instrument_profiles.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/liveness.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/read_plan.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/reclaim.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/reclaim_run.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/repo_cache.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/repo_store.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/resident_admission.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/sha256_backends.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/sharded_cas.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/source_artifacts.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/source_plan.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/source_prefixes.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/source_preflight.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/staging.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/store_boundaries.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/tensor_schema.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/version.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-core/tests/write_through.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-py/Cargo.toml +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-py/src/docs.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-py/src/errors.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-py/src/lib.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/crates/tensorfs-py/src/store.rs +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/python/tensorfs/_derived_channel.py +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/python/tensorfs/_ext.pyi +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/python/tensorfs/_source_channel.py +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/python/tensorfs/derived.py +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/python/tensorfs/errors.py +0 -0
- {tensorfs-0.3.43 → tensorfs-0.3.44}/python/tensorfs/py.typed +0 -0
|
@@ -421,7 +421,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
421
421
|
|
|
422
422
|
[[package]]
|
|
423
423
|
name = "tensorfs-core"
|
|
424
|
-
version = "0.3.
|
|
424
|
+
version = "0.3.44"
|
|
425
425
|
dependencies = [
|
|
426
426
|
"fs2",
|
|
427
427
|
"libc",
|
|
@@ -435,7 +435,7 @@ dependencies = [
|
|
|
435
435
|
|
|
436
436
|
[[package]]
|
|
437
437
|
name = "tensorfs-py"
|
|
438
|
-
version = "0.3.
|
|
438
|
+
version = "0.3.44"
|
|
439
439
|
dependencies = [
|
|
440
440
|
"pyo3",
|
|
441
441
|
"tensorfs-core",
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tensorfs
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.44
|
|
4
4
|
Classifier: Programming Language :: Python :: 3
|
|
5
5
|
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
6
6
|
Classifier: Programming Language :: Rust
|
|
7
|
+
Requires-Dist: torch>=2.11 ; extra == 'torch'
|
|
8
|
+
Requires-Dist: torchao==0.18.0 ; extra == 'torch'
|
|
9
|
+
Requires-Dist: numpy>=1.26 ; extra == 'torch'
|
|
10
|
+
Provides-Extra: torch
|
|
7
11
|
Summary: TensorFS — canonical tensor artifact substrate
|
|
8
12
|
Requires-Python: >=3.12
|
|
@@ -21,6 +21,11 @@
|
|
|
21
21
|
//! exactly zero bytes. A CONTROL-plane exchange now answers to `Judge::Transfer`: the
|
|
22
22
|
//! pull's own progress, which is a measurement, and never to this call's own duration,
|
|
23
23
|
//! which would be a constant.
|
|
24
|
+
//!
|
|
25
|
+
//! Object GETs also apply their existing measured stream-silence rule while
|
|
26
|
+
//! handshaking, writing and reading headers (h3a-070). Previously Judge::Stream
|
|
27
|
+
//! was ignored in those phases, so body retries were unreachable for a silent
|
|
28
|
+
//! TLS peer. Phase progress resets that phase's clock; no object wall limit is added.
|
|
24
29
|
|
|
25
30
|
use crate::err::{refuse, Code, Refusal, Result};
|
|
26
31
|
use crate::transport::ledger::{Deadline, Ledger};
|
|
@@ -122,6 +127,24 @@ fn transfer_verdict(
|
|
|
122
127
|
)
|
|
123
128
|
}
|
|
124
129
|
|
|
130
|
+
/// Object-stream silence is judged before the body too. Each phase starts with
|
|
131
|
+
/// its own window and advances only when transport bytes actually move; slow
|
|
132
|
+
/// progress may span arbitrarily many windows without exhausting a wall budget.
|
|
133
|
+
fn stream_verdict(judge: Judge, ledger: &Ledger, advanced: Instant, phase: &str) -> Result<()> {
|
|
134
|
+
if judge != Judge::Stream || advanced.elapsed() <= ledger.patience() {
|
|
135
|
+
return Ok(());
|
|
136
|
+
}
|
|
137
|
+
refuse(
|
|
138
|
+
Code::TRANSFER_FAILED,
|
|
139
|
+
format!(
|
|
140
|
+
"the object stream stopped during {phase}: silent {:.1} s against the \
|
|
141
|
+
{:.1} s this pull's measured pace allows",
|
|
142
|
+
advanced.elapsed().as_secs_f64(),
|
|
143
|
+
ledger.patience().as_secs_f64(),
|
|
144
|
+
),
|
|
145
|
+
)
|
|
146
|
+
}
|
|
147
|
+
|
|
125
148
|
pub struct Client {
|
|
126
149
|
tls: Arc<rustls::ClientConfig>,
|
|
127
150
|
}
|
|
@@ -283,6 +306,7 @@ fn tls_handshake(
|
|
|
283
306
|
fail: Code,
|
|
284
307
|
) -> Result<()> {
|
|
285
308
|
let sample = ledger.sample();
|
|
309
|
+
let mut advanced = Instant::now();
|
|
286
310
|
while conn.is_handshaking() {
|
|
287
311
|
transfer_verdict(judge, ledger, since, "a TLS handshake")?;
|
|
288
312
|
let tick = match deadline.remaining()? {
|
|
@@ -293,8 +317,11 @@ fn tls_handshake(
|
|
|
293
317
|
let _ = sock.set_write_timeout(Some(tick.max(Duration::from_millis(1))));
|
|
294
318
|
if conn.wants_write() {
|
|
295
319
|
match conn.write_tls(sock) {
|
|
320
|
+
Ok(n) if n > 0 => advanced = Instant::now(),
|
|
296
321
|
Ok(_) => {}
|
|
297
|
-
Err(e) if would_block(&e) => {
|
|
322
|
+
Err(e) if would_block(&e) => {
|
|
323
|
+
stream_verdict(judge, ledger, advanced, "TLS handshake")?;
|
|
324
|
+
}
|
|
298
325
|
Err(e) => return refuse(fail, format!("TLS handshake write: {e}")),
|
|
299
326
|
}
|
|
300
327
|
continue;
|
|
@@ -302,12 +329,15 @@ fn tls_handshake(
|
|
|
302
329
|
match conn.read_tls(sock) {
|
|
303
330
|
Ok(0) => return refuse(fail, "origin closed the connection mid-handshake"),
|
|
304
331
|
Ok(_) => {
|
|
332
|
+
advanced = Instant::now();
|
|
305
333
|
conn.process_new_packets().map_err(|e| Refusal {
|
|
306
334
|
code: fail,
|
|
307
335
|
detail: format!("TLS refused: {e}"),
|
|
308
336
|
})?;
|
|
309
337
|
}
|
|
310
|
-
Err(e) if would_block(&e) => {
|
|
338
|
+
Err(e) if would_block(&e) => {
|
|
339
|
+
stream_verdict(judge, ledger, advanced, "TLS handshake")?;
|
|
340
|
+
}
|
|
311
341
|
Err(e) => return refuse(fail, format!("TLS handshake read: {e}")),
|
|
312
342
|
}
|
|
313
343
|
}
|
|
@@ -733,6 +763,7 @@ fn write_all(
|
|
|
733
763
|
Err(e) => return refuse(fail, format!("the connection failed mid-write: {e}")),
|
|
734
764
|
}
|
|
735
765
|
}
|
|
766
|
+
stream_verdict(judge, ledger, advanced, "request write")?;
|
|
736
767
|
if let Some(bound) = silence {
|
|
737
768
|
if advanced.elapsed() > bound {
|
|
738
769
|
return refuse(
|
|
@@ -811,6 +842,7 @@ fn read_step_ticking(
|
|
|
811
842
|
since: Instant,
|
|
812
843
|
fail: Code,
|
|
813
844
|
) -> Result<usize> {
|
|
845
|
+
let advanced = Instant::now();
|
|
814
846
|
loop {
|
|
815
847
|
transfer_verdict(judge, ledger, since, "a response head")?;
|
|
816
848
|
let tick = match deadline.remaining()? {
|
|
@@ -820,7 +852,9 @@ fn read_step_ticking(
|
|
|
820
852
|
wire.set_read_timeout(tick);
|
|
821
853
|
match wire.read_step(buf) {
|
|
822
854
|
Ok(n) => return Ok(n),
|
|
823
|
-
Err(e) if would_block(&e) => {
|
|
855
|
+
Err(e) if would_block(&e) => {
|
|
856
|
+
stream_verdict(judge, ledger, advanced, "response head")?;
|
|
857
|
+
}
|
|
824
858
|
Err(e) => return refuse(fail, format!("the connection failed: {e}")),
|
|
825
859
|
}
|
|
826
860
|
}
|
|
@@ -57,6 +57,8 @@ impl Request {
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
enum Serve {
|
|
60
|
+
/// Read the request, then hold the socket without sending response headers.
|
|
61
|
+
StallHead,
|
|
60
62
|
/// Content-Length framed whole answer.
|
|
61
63
|
Whole {
|
|
62
64
|
status: u16,
|
|
@@ -146,6 +148,11 @@ fn handle(mut socket: TcpStream, dispatch: Dispatch, hits: Arc<Mutex<HashMap<Str
|
|
|
146
148
|
.entry(request.path.clone())
|
|
147
149
|
.or_insert(0) += 1;
|
|
148
150
|
match dispatch(&request) {
|
|
151
|
+
Serve::StallHead => {
|
|
152
|
+
let mut sink = [0u8; 16];
|
|
153
|
+
let _ = socket.set_read_timeout(Some(Duration::from_secs(30)));
|
|
154
|
+
let _ = socket.read(&mut sink);
|
|
155
|
+
}
|
|
149
156
|
Serve::Whole {
|
|
150
157
|
status,
|
|
151
158
|
body,
|
|
@@ -915,6 +922,120 @@ fn black_holed_stream_is_stalled_by_the_ledger_and_reasked() {
|
|
|
915
922
|
let _ = std::fs::remove_dir_all(root);
|
|
916
923
|
}
|
|
917
924
|
|
|
925
|
+
fn prebody_silence_is_judged(https: bool) {
|
|
926
|
+
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
|
927
|
+
let port = listener.local_addr().unwrap().port();
|
|
928
|
+
std::thread::spawn(move || {
|
|
929
|
+
let (mut socket, _) = listener.accept().unwrap();
|
|
930
|
+
socket
|
|
931
|
+
.set_read_timeout(Some(Duration::from_secs(3)))
|
|
932
|
+
.unwrap();
|
|
933
|
+
// Accept HTTP request bytes or a TLS ClientHello, but never answer.
|
|
934
|
+
let mut bytes = [0u8; 8192];
|
|
935
|
+
while matches!(socket.read(&mut bytes), Ok(n) if n > 0) {}
|
|
936
|
+
});
|
|
937
|
+
let scheme = if https { "https" } else { "http" };
|
|
938
|
+
let checked = local_policy()
|
|
939
|
+
.check(&format!("{scheme}://127.0.0.1:{port}/object"))
|
|
940
|
+
.unwrap();
|
|
941
|
+
let ledger = Ledger::with_resolution(Duration::from_millis(25));
|
|
942
|
+
ledger.moved(1024); // A pull that has already moved bytes, as on the real pod.
|
|
943
|
+
let response = super::http::request(
|
|
944
|
+
&super::http::Client::new(),
|
|
945
|
+
&checked,
|
|
946
|
+
"GET",
|
|
947
|
+
&[],
|
|
948
|
+
None,
|
|
949
|
+
Deadline::after_seconds(Some(1.0)),
|
|
950
|
+
&ledger,
|
|
951
|
+
super::http::Judge::Stream,
|
|
952
|
+
None,
|
|
953
|
+
Code::TRANSFER_FAILED,
|
|
954
|
+
);
|
|
955
|
+
let refusal = match response {
|
|
956
|
+
Err(refusal) => refusal,
|
|
957
|
+
Ok(_) => panic!("silent origin unexpectedly answered"),
|
|
958
|
+
};
|
|
959
|
+
assert_eq!(refusal.code, Code::TRANSFER_FAILED, "{refusal}");
|
|
960
|
+
assert!(
|
|
961
|
+
refusal.detail.contains(if https {
|
|
962
|
+
"TLS handshake"
|
|
963
|
+
} else {
|
|
964
|
+
"response head"
|
|
965
|
+
}),
|
|
966
|
+
"{refusal}"
|
|
967
|
+
);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
#[test]
|
|
971
|
+
fn an_object_stalled_before_headers_is_judged_by_stream_progress() {
|
|
972
|
+
prebody_silence_is_judged(false);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
#[test]
|
|
976
|
+
fn an_object_stalled_during_tls_is_judged_by_stream_progress() {
|
|
977
|
+
prebody_silence_is_judged(true);
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
#[test]
|
|
981
|
+
fn a_prebody_stall_is_reasked_without_discarding_completed_objects() {
|
|
982
|
+
let root = temporary("head-stall-retry");
|
|
983
|
+
let store = Store::init(&root).unwrap();
|
|
984
|
+
let checkpoint = Arc::new(checkpoint(2, 64 * 1024));
|
|
985
|
+
let victim = checkpoint.objects[0].sha256.clone();
|
|
986
|
+
let selected = victim.clone();
|
|
987
|
+
let origin = hub_origin(Arc::clone(&checkpoint), move |hex, attempt| {
|
|
988
|
+
(hex == selected && attempt == 1).then_some(Serve::StallHead)
|
|
989
|
+
});
|
|
990
|
+
let policy = SourcePolicy::default();
|
|
991
|
+
let report = pull(&request_for(&store, &origin.base, &policy)).unwrap();
|
|
992
|
+
assert_eq!(report.fetched, 4);
|
|
993
|
+
for object in &checkpoint.objects {
|
|
994
|
+
assert!(store.record_valid(&object.sha256).is_ok());
|
|
995
|
+
assert_eq!(
|
|
996
|
+
origin.hits(&format!("/obj/{}", object.sha256)),
|
|
997
|
+
if object.sha256 == victim { 2 } else { 1 }
|
|
998
|
+
);
|
|
999
|
+
}
|
|
1000
|
+
let _ = std::fs::remove_dir_all(root);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
#[test]
|
|
1004
|
+
fn fragmented_headers_can_progress_across_multiple_stream_windows() {
|
|
1005
|
+
let listener = TcpListener::bind("127.0.0.1:0").unwrap();
|
|
1006
|
+
let port = listener.local_addr().unwrap().port();
|
|
1007
|
+
std::thread::spawn(move || {
|
|
1008
|
+
let (mut socket, _) = listener.accept().unwrap();
|
|
1009
|
+
read_request(&mut socket).unwrap();
|
|
1010
|
+
socket.write_all(b"HTTP/1.1 200 OK\r\n").unwrap();
|
|
1011
|
+
for _ in 0..10 {
|
|
1012
|
+
std::thread::sleep(Duration::from_millis(50));
|
|
1013
|
+
socket.write_all(b"X-Progress: yes\r\n").unwrap();
|
|
1014
|
+
}
|
|
1015
|
+
socket.write_all(b"Content-Length: 3\r\n\r\nabc").unwrap();
|
|
1016
|
+
});
|
|
1017
|
+
let checked = local_policy()
|
|
1018
|
+
.check(&format!("http://127.0.0.1:{port}/object"))
|
|
1019
|
+
.unwrap();
|
|
1020
|
+
let ledger = Ledger::with_resolution(Duration::from_millis(25));
|
|
1021
|
+
let started = std::time::Instant::now();
|
|
1022
|
+
let response = super::http::request(
|
|
1023
|
+
&super::http::Client::new(),
|
|
1024
|
+
&checked,
|
|
1025
|
+
"GET",
|
|
1026
|
+
&[],
|
|
1027
|
+
None,
|
|
1028
|
+
Deadline::none(),
|
|
1029
|
+
&ledger,
|
|
1030
|
+
super::http::Judge::Stream,
|
|
1031
|
+
None,
|
|
1032
|
+
Code::TRANSFER_FAILED,
|
|
1033
|
+
)
|
|
1034
|
+
.unwrap();
|
|
1035
|
+
assert_eq!(response.read_capped(3).unwrap(), b"abc");
|
|
1036
|
+
assert!(started.elapsed() > ledger.floor() * 2);
|
|
1037
|
+
}
|
|
1038
|
+
|
|
918
1039
|
#[test]
|
|
919
1040
|
fn slow_origin_teaches_the_ledger_and_is_left_alone() {
|
|
920
1041
|
let root = temporary("slow");
|
|
@@ -923,21 +1044,26 @@ fn slow_origin_teaches_the_ledger_and_is_left_alone() {
|
|
|
923
1044
|
let slow = checkpoint.objects[0].sha256.clone();
|
|
924
1045
|
let slow_for = slow.clone();
|
|
925
1046
|
let bodies = checkpoint.bodies.clone();
|
|
926
|
-
// 400 ms
|
|
927
|
-
//
|
|
1047
|
+
// A successful 400 ms first answer teaches this origin's latency; subsequent
|
|
1048
|
+
// 800 ms gaps exceed the initial floor but remain below that measured term.
|
|
1049
|
+
// Unlike the old body-only rule,
|
|
1050
|
+
// initial pre-header silence is now bounded, so this fixture gives the first
|
|
1051
|
+
// answer a sufficient 600 ms floor. Production timing constants are unchanged.
|
|
928
1052
|
let origin = hub_origin(Arc::clone(&checkpoint), move |hex, _| {
|
|
929
1053
|
if hex == slow_for {
|
|
930
1054
|
return Some(Serve::Drip {
|
|
931
1055
|
body: bodies[hex].clone(),
|
|
932
1056
|
chunk: 1024,
|
|
933
|
-
gap: Duration::from_millis(
|
|
1057
|
+
gap: Duration::from_millis(800),
|
|
934
1058
|
ttfb: Duration::from_millis(400),
|
|
935
1059
|
});
|
|
936
1060
|
}
|
|
937
1061
|
None
|
|
938
1062
|
});
|
|
939
1063
|
let policy = SourcePolicy::default();
|
|
940
|
-
let
|
|
1064
|
+
let mut request = request_for(&store, &origin.base, &policy);
|
|
1065
|
+
request.sample_seconds = 0.1;
|
|
1066
|
+
let report = pull(&request).unwrap();
|
|
941
1067
|
assert_eq!(report.fetched, 3);
|
|
942
1068
|
assert_eq!(origin.hits(&format!("/obj/{slow}")), 1); // one ask; never condemned
|
|
943
1069
|
let _ = std::fs::remove_dir_all(root);
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "tensorfs"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.44"
|
|
8
8
|
description = "TensorFS — canonical tensor artifact substrate"
|
|
9
9
|
requires-python = ">=3.12"
|
|
10
10
|
classifiers = [
|
|
@@ -13,6 +13,9 @@ classifiers = [
|
|
|
13
13
|
"Programming Language :: Rust",
|
|
14
14
|
]
|
|
15
15
|
|
|
16
|
+
[project.optional-dependencies]
|
|
17
|
+
torch = ["torch>=2.11", "torchao==0.18.0", "numpy>=1.26"]
|
|
18
|
+
|
|
16
19
|
[tool.maturin]
|
|
17
20
|
manifest-path = "crates/tensorfs-py/Cargo.toml"
|
|
18
21
|
module-name = "tensorfs._ext"
|
|
@@ -4,8 +4,9 @@ Marshal only. Every byte-level fact — hashing, canonical bytes, geometry, veri
|
|
|
4
4
|
leases, the reader pool — comes from the extension, and `scripts/fence.py` enforces that
|
|
5
5
|
this package computes none of them. What lives here is types and names.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
The default inference reader returns owned Torch tensors (install `tensorfs[torch]`).
|
|
8
|
+
Torch loads only when tensor access is requested. Storage and metadata remain lightweight.
|
|
9
|
+
The lower-level read interface still fills caller-owned buffers::
|
|
9
10
|
|
|
10
11
|
import torch, tensorfs
|
|
11
12
|
|
|
@@ -35,7 +36,21 @@ from dataclasses import dataclass
|
|
|
35
36
|
from typing import Literal, TypedDict, cast
|
|
36
37
|
|
|
37
38
|
from . import errors
|
|
38
|
-
from .derived import
|
|
39
|
+
from .derived import (
|
|
40
|
+
Derivation,
|
|
41
|
+
OutputCapability,
|
|
42
|
+
SourceCapability,
|
|
43
|
+
SourceInspection,
|
|
44
|
+
derive,
|
|
45
|
+
)
|
|
46
|
+
from ._tensors import (
|
|
47
|
+
TensorAccessError,
|
|
48
|
+
TensorAllocation,
|
|
49
|
+
TensorReader,
|
|
50
|
+
from_parts,
|
|
51
|
+
physical_tensors,
|
|
52
|
+
tensors,
|
|
53
|
+
)
|
|
39
54
|
from ._ext import (
|
|
40
55
|
_fit,
|
|
41
56
|
Batch,
|
|
@@ -182,12 +197,19 @@ def fit(
|
|
|
182
197
|
),
|
|
183
198
|
)
|
|
184
199
|
|
|
200
|
+
|
|
185
201
|
#: The closed carrier/logical dtype enum and its element sizes. The ONE export of dtype
|
|
186
202
|
#: knowledge: a consumer sizing a `torch.frombuffer` reads it from here, never from a
|
|
187
203
|
#: table of its own.
|
|
188
204
|
DTYPES: dict[str, int] = dtypes()
|
|
189
205
|
|
|
190
206
|
__all__ = [
|
|
207
|
+
"TensorAccessError",
|
|
208
|
+
"TensorAllocation",
|
|
209
|
+
"TensorReader",
|
|
210
|
+
"from_parts",
|
|
211
|
+
"physical_tensors",
|
|
212
|
+
"tensors",
|
|
191
213
|
"Derivation",
|
|
192
214
|
"OutputCapability",
|
|
193
215
|
"SourceCapability",
|