tensorfs 0.3.61__tar.gz → 0.3.63__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.61 → tensorfs-0.3.63}/Cargo.lock +2 -2
- {tensorfs-0.3.61 → tensorfs-0.3.63}/Cargo.toml +1 -1
- {tensorfs-0.3.61 → tensorfs-0.3.63}/PKG-INFO +1 -1
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/asset_update.rs +3 -8
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/bin/tfs/main.rs +8 -6
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/bin/tfs/repo.rs +10 -19
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/bin/tfs/snap.rs +4 -1
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/canon.rs +25 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/checkpoint.rs +7 -10
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/checkpoint_root.rs +21 -3
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/derived/companion_tests.rs +1 -1
- tensorfs-0.3.63/crates/tensorfs-core/src/derived/part_rows_tests.rs +169 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/derived/resume_tests.rs +2 -1
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/derived/retention.rs +18 -1
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/derived/roots.rs +140 -1
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/derived.rs +204 -88
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/transaction.rs +27 -5
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/lib.rs +3 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/manifest.rs +50 -13
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/meta.rs +199 -7
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/project.rs +2 -2
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/source_artifact.rs +20 -3
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/storage.rs +3 -3
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/http.rs +26 -19
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/http_reuse_tests.rs +84 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/push.rs +5 -1
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/additive_fields.rs +43 -13
- tensorfs-0.3.63/crates/tensorfs-core/tests/anima_hf_profile.rs +166 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/checkpoint_roots.rs +26 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/qwen_image21_profile.rs +5 -4
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-py/src/store.rs +8 -1
- {tensorfs-0.3.61 → tensorfs-0.3.63}/pyproject.toml +1 -1
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/Cargo.toml +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/build.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/examples/h3-swiglu-layout.md +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/examples/optional-cache-backfill.md +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/examples/resident-conversion.md +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/examples/resident_conversion_profile.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/b64.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/bin/tfs/ckpt.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/bin/tfs/conform.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/bin/tfs/fetch.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/bin/tfs/ingest.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/bin/tfs/rd.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/bin/tfs/watch.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/cache_roots.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/capability.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/catalog.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/cbor.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/corpus.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/derived/adoption.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/derived/estimate.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/derived/graft_tests.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/derived/progress.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/dtype.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/durability.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/err.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/fetch.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/fill.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/fit.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/gc.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/header.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/home.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ids.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/carrier.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/convert.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/custody.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/fingerprint.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/gguf.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/h3_lora.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/journal.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/mod.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/preflight.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/source.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/ingest/stamp.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/jcs.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/limits.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/machine.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/providers.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/read.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/receipt.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/reclaim.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/registry.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/relation.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/repo_cache/identity.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/repo_cache.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/repository.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/sha256.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/spec.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/staging.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/stats.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/store.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/api.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/hub.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/ledger.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/memory.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/mod.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/pace.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/policy.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/prefix.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/pull.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/ranged.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/speculate.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/transport/tests.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/src/vectors.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/fd_headroom.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/fetch_url_progress.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/fit.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/fixtures/repo_apply/blobs.jsonl +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_list.jsonl +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_repository.json +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/fixtures/repo_apply/expected_rows.jsonl +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/fixtures/repo_apply/header.cbor +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/fixtures/repo_apply/manifest.json +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/fixtures/repo_apply/mutation.json +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/foreign_json.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/h3_lora.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/h3_swiglu.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/home.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/import_tree.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/instrument_profiles.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/liveness.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/read_plan.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/reclaim.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/reclaim_run.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/repo_cache.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/repo_store.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/resident_admission.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/sha256_backends.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/sharded_cas.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/source_artifacts.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/source_download.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/source_plan.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/source_prefixes.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/source_preflight.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/staging.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/store_boundaries.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/tensor_schema.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/transfer_memory.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/trust_index.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/version.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-core/tests/write_through.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-py/Cargo.toml +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-py/src/docs.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-py/src/errors.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-py/src/ingest.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/crates/tensorfs-py/src/lib.rs +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/python/tensorfs/__init__.py +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/python/tensorfs/_derived_channel.py +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/python/tensorfs/_ext.pyi +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/python/tensorfs/_source_channel.py +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/python/tensorfs/_tensors.py +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/python/tensorfs/derived.py +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/python/tensorfs/errors.py +0 -0
- {tensorfs-0.3.61 → tensorfs-0.3.63}/python/tensorfs/py.typed +0 -0
|
@@ -439,7 +439,7 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
|
|
439
439
|
|
|
440
440
|
[[package]]
|
|
441
441
|
name = "tensorfs-core"
|
|
442
|
-
version = "0.3.
|
|
442
|
+
version = "0.3.63"
|
|
443
443
|
dependencies = [
|
|
444
444
|
"fs2",
|
|
445
445
|
"libc",
|
|
@@ -454,7 +454,7 @@ dependencies = [
|
|
|
454
454
|
|
|
455
455
|
[[package]]
|
|
456
456
|
name = "tensorfs-py"
|
|
457
|
-
version = "0.3.
|
|
457
|
+
version = "0.3.63"
|
|
458
458
|
dependencies = [
|
|
459
459
|
"pyo3",
|
|
460
460
|
"tensorfs-core",
|
|
@@ -11,7 +11,7 @@ use crate::checkpoint;
|
|
|
11
11
|
use crate::err::{refuse, Code, Refusal, Result};
|
|
12
12
|
use crate::header::Asset;
|
|
13
13
|
use crate::ids::ObjectRef;
|
|
14
|
-
use crate::manifest::
|
|
14
|
+
use crate::manifest::Manifest;
|
|
15
15
|
use crate::store::Store;
|
|
16
16
|
|
|
17
17
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
@@ -42,13 +42,7 @@ fn source_object(manifest: &Manifest, path: &str) -> Result<ObjectRef> {
|
|
|
42
42
|
.entries()
|
|
43
43
|
.iter()
|
|
44
44
|
.find_map(|(candidate, entry)| {
|
|
45
|
-
(candidate == path).then(||
|
|
46
|
-
Entry::File(object) => Ok(object.clone()),
|
|
47
|
-
Entry::CozyTensors(_) => Err(Refusal {
|
|
48
|
-
code: Code::WRONG_TYPE,
|
|
49
|
-
detail: format!("asset source {path:?} is a CozyTensors header, not a file"),
|
|
50
|
-
}),
|
|
51
|
-
})
|
|
45
|
+
(candidate == path).then(|| entry.materializable(path).cloned())
|
|
52
46
|
})
|
|
53
47
|
.unwrap_or_else(|| {
|
|
54
48
|
Err(Refusal {
|
|
@@ -187,6 +181,7 @@ mod tests {
|
|
|
187
181
|
use crate::checkpoint;
|
|
188
182
|
use crate::header::{Body, Header, Part, Tensor};
|
|
189
183
|
use crate::manifest::Draft;
|
|
184
|
+
use crate::manifest::Entry;
|
|
190
185
|
use crate::store::Fault;
|
|
191
186
|
use crate::{dtype::Dtype, registry};
|
|
192
187
|
|
|
@@ -2207,16 +2207,17 @@ fn build_corpus() -> TfsResult<(Vec<Case>, Vec<EncodingSpec>)> {
|
|
|
2207
2207
|
"carrier no longer equals the logical dtype",
|
|
2208
2208
|
));
|
|
2209
2209
|
|
|
2210
|
-
//
|
|
2210
|
+
// A kind a newer TensorFS wrote is opaque here: it parses, and only materializing refuses.
|
|
2211
2211
|
let manifest_bytes = snap2.canonical_bytes();
|
|
2212
|
-
cases.push(
|
|
2213
|
-
"manifest
|
|
2214
|
-
"entry-kind-
|
|
2212
|
+
cases.push(ok_case(
|
|
2213
|
+
"manifest",
|
|
2214
|
+
"entry-kind-future",
|
|
2215
2215
|
"manifest",
|
|
2216
2216
|
sub(&manifest_bytes, "\"kind\":\"file\"", "\"kind\":\"symlink\""),
|
|
2217
|
-
"
|
|
2218
|
-
"only file and cozytensors entries exist",
|
|
2217
|
+
"an entry kind from a newer TensorFS is carried opaquely",
|
|
2219
2218
|
));
|
|
2219
|
+
|
|
2220
|
+
// --- location-typed Manifest reds
|
|
2220
2221
|
let mut draft = Draft::of(&snap);
|
|
2221
2222
|
draft.entries.clear();
|
|
2222
2223
|
cases.push(red_case(
|
|
@@ -2259,6 +2260,7 @@ fn build_corpus() -> TfsResult<(Vec<Case>, Vec<EncodingSpec>)> {
|
|
|
2259
2260
|
// --- the border's own documents (tfs-003)
|
|
2260
2261
|
let g = "ingest";
|
|
2261
2262
|
let session = tensorfs_core::ingest::transaction::IngestSession {
|
|
2263
|
+
tensorfs: None,
|
|
2262
2264
|
session: "e3b0c44298fc1c14".to_string(),
|
|
2263
2265
|
tenant: "acme".to_string(),
|
|
2264
2266
|
candidates: vec![ObjectRef::of(b"a candidate header")],
|
|
@@ -9,7 +9,7 @@ use tensorfs_core::err::{Code, Refusal};
|
|
|
9
9
|
use tensorfs_core::gc;
|
|
10
10
|
use tensorfs_core::header::{Body, Header};
|
|
11
11
|
use tensorfs_core::ids::{hex64, prefixed, Doc, ObjectRef};
|
|
12
|
-
use tensorfs_core::manifest::
|
|
12
|
+
use tensorfs_core::manifest::Manifest;
|
|
13
13
|
use tensorfs_core::reclaim;
|
|
14
14
|
use tensorfs_core::repository::{self, Mutation, Repository};
|
|
15
15
|
use tensorfs_core::storage::{
|
|
@@ -738,13 +738,7 @@ pub fn cmd_manifest_inspect(path: &Path, flags: &Flags) -> ExitCode {
|
|
|
738
738
|
.iter()
|
|
739
739
|
.map(|(path, entry)| {
|
|
740
740
|
tensorfs_core::canon::write(&Value::obj(vec![
|
|
741
|
-
(
|
|
742
|
-
"kind",
|
|
743
|
-
Value::str(match entry {
|
|
744
|
-
Entry::File(_) => "file",
|
|
745
|
-
Entry::CozyTensors(_) => "cozytensors",
|
|
746
|
-
}),
|
|
747
|
-
),
|
|
741
|
+
("kind", Value::str(entry.kind())),
|
|
748
742
|
("length", Value::uint(entry.blob().length)),
|
|
749
743
|
("path", Value::str(path.clone())),
|
|
750
744
|
("sha256", Value::str(entry.blob().sha256.clone())),
|
|
@@ -855,7 +849,7 @@ fn closure_refs(
|
|
|
855
849
|
// Runtime roles precede ordinary snapshot paths so an aliased README/sample path cannot
|
|
856
850
|
// relabel a required config, model asset, or tensor part as an ignorable file.
|
|
857
851
|
for (_, entry) in manifest.entries() {
|
|
858
|
-
if let
|
|
852
|
+
if let Some(object) = entry.content() {
|
|
859
853
|
insert(&mut refs, "file", object)?;
|
|
860
854
|
}
|
|
861
855
|
}
|
|
@@ -1206,7 +1200,7 @@ pub fn cmd_reclaim_run(root: &Path, flags: &Flags) -> ExitCode {
|
|
|
1206
1200
|
mod tests {
|
|
1207
1201
|
use super::*;
|
|
1208
1202
|
use std::collections::HashSet;
|
|
1209
|
-
use tensorfs_core::manifest::Draft;
|
|
1203
|
+
use tensorfs_core::manifest::{Draft, Entry};
|
|
1210
1204
|
|
|
1211
1205
|
#[test]
|
|
1212
1206
|
fn inspect_closure_preserves_runtime_roles_over_aliased_files() {
|
|
@@ -1246,10 +1240,7 @@ mod tests {
|
|
|
1246
1240
|
let file_ids: HashSet<&str> = manifest
|
|
1247
1241
|
.entries()
|
|
1248
1242
|
.iter()
|
|
1249
|
-
.filter_map(|(_, entry)|
|
|
1250
|
-
Entry::File(object) => Some(object.sha256.as_str()),
|
|
1251
|
-
Entry::CozyTensors(_) => None,
|
|
1252
|
-
})
|
|
1243
|
+
.filter_map(|(_, entry)| entry.content().map(|object| object.sha256.as_str()))
|
|
1253
1244
|
.collect();
|
|
1254
1245
|
|
|
1255
1246
|
assert!(header.assets.iter().any(|(_, asset)| asset
|
|
@@ -1268,11 +1259,11 @@ mod tests {
|
|
|
1268
1259
|
.filter(|(kind, _)| *kind != "file")
|
|
1269
1260
|
.map(|(_, object)| object.sha256.as_str())
|
|
1270
1261
|
.collect();
|
|
1271
|
-
assert!(manifest
|
|
1272
|
-
|
|
1262
|
+
assert!(manifest
|
|
1263
|
+
.entries()
|
|
1264
|
+
.iter()
|
|
1265
|
+
.any(|(_, entry)| entry.content().is_some_and(|object| {
|
|
1273
1266
|
!runtime_ids.contains(object.sha256.as_str()) && refs[&object.sha256].0 == "file"
|
|
1274
|
-
}
|
|
1275
|
-
Entry::CozyTensors(_) => false,
|
|
1276
|
-
}));
|
|
1267
|
+
})));
|
|
1277
1268
|
}
|
|
1278
1269
|
}
|
|
@@ -104,7 +104,7 @@ fn referenced_blobs(header: &Header, draft: &Draft) -> Result<BTreeMap<String, u
|
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
106
|
for (_, entry) in &draft.entries {
|
|
107
|
-
if let
|
|
107
|
+
if let Some(object) = entry.content() {
|
|
108
108
|
add_ref(&mut refs, object, "manifest file")?;
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -257,6 +257,9 @@ pub fn cmd_show(root: &Path, hex: &str) -> ExitCode {
|
|
|
257
257
|
blob.id(),
|
|
258
258
|
blob.length
|
|
259
259
|
),
|
|
260
|
+
Entry::Other { kind, blob } => {
|
|
261
|
+
println!(" {kind:<12} {path} {} ({} B)", blob.id(), blob.length)
|
|
262
|
+
}
|
|
260
263
|
}
|
|
261
264
|
}
|
|
262
265
|
ExitCode::SUCCESS
|
|
@@ -456,6 +456,31 @@ impl<'a> Fields<'a> {
|
|
|
456
456
|
}
|
|
457
457
|
}
|
|
458
458
|
|
|
459
|
+
/// End a record whose unread field could keep bytes alive (a GC root): refuse, naming the
|
|
460
|
+
/// newer TensorFS that wrote it, rather than drop what that version may rely on.
|
|
461
|
+
pub fn done_written_by(self, writer: Option<&str>) -> Result<()> {
|
|
462
|
+
let Some((k, _)) = self.left.first() else {
|
|
463
|
+
return Ok(());
|
|
464
|
+
};
|
|
465
|
+
let by = writer.map_or_else(
|
|
466
|
+
|| "a newer TensorFS".to_string(),
|
|
467
|
+
|writer| format!("TensorFS {writer}"),
|
|
468
|
+
);
|
|
469
|
+
let upgrade = writer.map_or_else(
|
|
470
|
+
|| "upgrade TensorFS".to_string(),
|
|
471
|
+
|writer| format!("upgrade to tensorfs>={writer}"),
|
|
472
|
+
);
|
|
473
|
+
refuse(
|
|
474
|
+
Code::UNKNOWN_FIELD,
|
|
475
|
+
format!(
|
|
476
|
+
"{}: field {k:?} was written by {by}; this is TensorFS {} — {upgrade}. It is \
|
|
477
|
+
refused rather than dropped because it may keep bytes alive",
|
|
478
|
+
self.what,
|
|
479
|
+
crate::VERSION
|
|
480
|
+
),
|
|
481
|
+
)
|
|
482
|
+
}
|
|
483
|
+
|
|
459
484
|
/// End a document that tolerates additive fields, keeping them for re-emission.
|
|
460
485
|
pub fn rest(self) -> Extra {
|
|
461
486
|
Extra(
|
|
@@ -523,17 +523,14 @@ pub fn walk(store: &Store, snap: &Manifest) -> Result<Walk> {
|
|
|
523
523
|
.map(|r| (r.obj.sha256.clone(), r.obj.length))
|
|
524
524
|
.collect();
|
|
525
525
|
for (path, e) in snap.entries() {
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
});
|
|
534
|
-
}
|
|
526
|
+
if let Some(o) = e.content() {
|
|
527
|
+
if !accounted.contains(&(o.sha256.clone(), o.length)) {
|
|
528
|
+
w.siblings.push(path.clone());
|
|
529
|
+
w.objects.push(Reached {
|
|
530
|
+
kind: "asset",
|
|
531
|
+
obj: o.clone(),
|
|
532
|
+
});
|
|
535
533
|
}
|
|
536
|
-
crate::manifest::Entry::CozyTensors(_) => {}
|
|
537
534
|
}
|
|
538
535
|
}
|
|
539
536
|
Ok(w)
|
|
@@ -22,6 +22,8 @@ pub struct CheckpointRoot {
|
|
|
22
22
|
pub repository: RepositoryName,
|
|
23
23
|
pub manifest: ObjectRef,
|
|
24
24
|
pub released: bool,
|
|
25
|
+
/// The newest TensorFS that wrote this root.
|
|
26
|
+
pub tensorfs: Option<String>,
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
impl Plain for CheckpointRoot {
|
|
@@ -36,11 +38,18 @@ impl Plain for CheckpointRoot {
|
|
|
36
38
|
Value::Bool(value) => *value,
|
|
37
39
|
_ => return refuse(Code::WRONG_TYPE, "checkpoint root release must be boolean"),
|
|
38
40
|
};
|
|
39
|
-
fields
|
|
41
|
+
let tensorfs = fields
|
|
42
|
+
.opt("tensorfs")
|
|
43
|
+
.map(|value| {
|
|
44
|
+
crate::canon::as_str("CheckpointRoot", "tensorfs", value).map(str::to_string)
|
|
45
|
+
})
|
|
46
|
+
.transpose()?;
|
|
47
|
+
fields.done_written_by(tensorfs.as_deref())?;
|
|
40
48
|
if manifest.length == 0 || manifest.length > crate::limits::INT_MAX as u64 {
|
|
41
49
|
return refuse(Code::LENGTH_MISMATCH, "checkpoint root manifest is empty");
|
|
42
50
|
}
|
|
43
51
|
Ok(Self {
|
|
52
|
+
tensorfs,
|
|
44
53
|
owner,
|
|
45
54
|
repository,
|
|
46
55
|
manifest,
|
|
@@ -49,7 +58,7 @@ impl Plain for CheckpointRoot {
|
|
|
49
58
|
}
|
|
50
59
|
|
|
51
60
|
fn to_value(&self) -> Value {
|
|
52
|
-
|
|
61
|
+
let mut fields = vec![
|
|
53
62
|
("owner", Value::str(self.owner.clone())),
|
|
54
63
|
(
|
|
55
64
|
"repository",
|
|
@@ -57,7 +66,11 @@ impl Plain for CheckpointRoot {
|
|
|
57
66
|
),
|
|
58
67
|
("manifest", self.manifest.to_value()),
|
|
59
68
|
("released", Value::Bool(self.released)),
|
|
60
|
-
]
|
|
69
|
+
];
|
|
70
|
+
if let Some(tensorfs) = &self.tensorfs {
|
|
71
|
+
fields.push(("tensorfs", Value::str(tensorfs.clone())));
|
|
72
|
+
}
|
|
73
|
+
Value::obj(fields)
|
|
61
74
|
}
|
|
62
75
|
}
|
|
63
76
|
|
|
@@ -126,6 +139,10 @@ fn lock(store: &Store, owner: &str) -> Result<File> {
|
|
|
126
139
|
}
|
|
127
140
|
|
|
128
141
|
fn write(store: &Store, root: &CheckpointRoot) -> Result<()> {
|
|
142
|
+
let root = &CheckpointRoot {
|
|
143
|
+
tensorfs: Some(crate::VERSION.into()),
|
|
144
|
+
..root.clone()
|
|
145
|
+
};
|
|
129
146
|
let path = path(store, &root.owner)?;
|
|
130
147
|
let temporary = path.with_extension(format!("tmp-{}", crate::meta::now_nanos_unique()));
|
|
131
148
|
let result = (|| {
|
|
@@ -159,6 +176,7 @@ fn identity(owner: &str, source: &str, manifest: ObjectRef) -> Result<Checkpoint
|
|
|
159
176
|
return refuse(Code::LENGTH_MISMATCH, "checkpoint root manifest is empty");
|
|
160
177
|
}
|
|
161
178
|
Ok(CheckpointRoot {
|
|
179
|
+
tensorfs: Some(crate::VERSION.into()),
|
|
162
180
|
owner: prefixed("checkpoint root owner", owner)?,
|
|
163
181
|
repository: repository(source)?,
|
|
164
182
|
manifest,
|
|
@@ -191,7 +191,7 @@ fn ordinary_companions_survive_derivation_without_copying_tensor_backing_files()
|
|
|
191
191
|
.push(("LICENSE".into(), b"explicit licence".to_vec()));
|
|
192
192
|
assert_eq!(
|
|
193
193
|
companion_files(&d, &sources).unwrap()["LICENSE"],
|
|
194
|
-
ObjectRef::of(b"explicit licence")
|
|
194
|
+
Entry::File(ObjectRef::of(b"explicit licence"))
|
|
195
195
|
);
|
|
196
196
|
std::fs::remove_dir_all(store.root()).unwrap();
|
|
197
197
|
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
//! Accepted parts are rows of their own: accepting one never reads or rewrites the others.
|
|
2
|
+
|
|
3
|
+
use super::*;
|
|
4
|
+
use rusqlite::params;
|
|
5
|
+
|
|
6
|
+
fn fixture(label: &str) -> (Store, Meta, String, Declaration) {
|
|
7
|
+
let root = std::env::temp_dir().join(format!(
|
|
8
|
+
"tensorfs-part-rows-{label}-{}",
|
|
9
|
+
crate::meta::now_nanos_unique()
|
|
10
|
+
));
|
|
11
|
+
let store = Store::init(&root).unwrap();
|
|
12
|
+
let meta = Meta::open(&store).unwrap();
|
|
13
|
+
let mut declaration = super::tests::created_declaration();
|
|
14
|
+
let prototype = declaration.components[0].add[0].clone();
|
|
15
|
+
declaration.components[0].add = (0..3)
|
|
16
|
+
.map(|index| {
|
|
17
|
+
let mut tensor = prototype.clone();
|
|
18
|
+
tensor.key = format!("weight_{index}");
|
|
19
|
+
tensor.shape = vec![128];
|
|
20
|
+
tensor.parts[0].shape = vec![128];
|
|
21
|
+
tensor
|
|
22
|
+
})
|
|
23
|
+
.collect();
|
|
24
|
+
declaration.order = (0..3)
|
|
25
|
+
.map(|index| ("model".into(), format!("weight_{index}")))
|
|
26
|
+
.collect();
|
|
27
|
+
declaration.max_new_bytes = 3 * 512;
|
|
28
|
+
(
|
|
29
|
+
store,
|
|
30
|
+
meta,
|
|
31
|
+
format!("sha256:{}", "7a".repeat(32)),
|
|
32
|
+
declaration,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
fn catalog(store: &Store) -> rusqlite::Connection {
|
|
37
|
+
rusqlite::Connection::open(crate::catalog::Catalog::path(store.root())).unwrap()
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
fn add(store: &Store, meta: &Meta, id: &str, writer: &Begin, key: &str, byte: u8) -> Result<()> {
|
|
41
|
+
add_indexed_part(
|
|
42
|
+
store,
|
|
43
|
+
meta,
|
|
44
|
+
id,
|
|
45
|
+
1,
|
|
46
|
+
&writer.additions,
|
|
47
|
+
("model", key, "value"),
|
|
48
|
+
&mut [byte; 512].as_slice(),
|
|
49
|
+
)
|
|
50
|
+
.map(|_| ())
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
fn stored_rows(store: &Store, id: &str) -> (usize, Option<i64>, i64) {
|
|
54
|
+
let connection = catalog(store);
|
|
55
|
+
let (bytes, open): (Vec<u8>, Option<i64>) = connection
|
|
56
|
+
.query_row(
|
|
57
|
+
"SELECT bytes,open_session FROM tensorfs_derived_transactions WHERE id=?1",
|
|
58
|
+
[id],
|
|
59
|
+
|row| Ok((row.get(0)?, row.get(1)?)),
|
|
60
|
+
)
|
|
61
|
+
.unwrap();
|
|
62
|
+
let value = crate::canon::parse_canonical(&bytes, limits::DOC_MAX_BYTES).unwrap();
|
|
63
|
+
let inline = TransactionRow::from_value(&value)
|
|
64
|
+
.unwrap()
|
|
65
|
+
.added_parts
|
|
66
|
+
.len();
|
|
67
|
+
let parts = connection
|
|
68
|
+
.query_row(
|
|
69
|
+
"SELECT count(*) FROM tensorfs_derived_parts WHERE id=?1",
|
|
70
|
+
[id],
|
|
71
|
+
|row| row.get(0),
|
|
72
|
+
)
|
|
73
|
+
.unwrap();
|
|
74
|
+
(inline, open, parts)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#[test]
|
|
78
|
+
fn parts_are_rows_that_resume_refuse_conflicts_and_stay_retained() {
|
|
79
|
+
let (store, meta, id, declaration) = fixture("rows");
|
|
80
|
+
let writer = begin(&store, &meta, &id, 1, declaration.clone(), None).unwrap();
|
|
81
|
+
add(&store, &meta, &id, &writer, "weight_0", 1).unwrap();
|
|
82
|
+
add(&store, &meta, &id, &writer, "weight_1", 2).unwrap();
|
|
83
|
+
// The row itself names no part; each part is one row; the writer's session is indexed.
|
|
84
|
+
assert_eq!(stored_rows(&store, &id), (0, Some(1), 2));
|
|
85
|
+
// The same bytes again are the same accepted part; other bytes conflict.
|
|
86
|
+
add(&store, &meta, &id, &writer, "weight_0", 1).unwrap();
|
|
87
|
+
assert_eq!(stored_rows(&store, &id).2, 2);
|
|
88
|
+
assert_eq!(
|
|
89
|
+
add(&store, &meta, &id, &writer, "weight_0", 9)
|
|
90
|
+
.unwrap_err()
|
|
91
|
+
.code,
|
|
92
|
+
Code::TRANSACTION_CONFLICT
|
|
93
|
+
);
|
|
94
|
+
// A fenced writer cannot add. Once it is gone, GC keeps its accepted bytes through
|
|
95
|
+
// the append-only journal alone, and its successor resumes with both parts.
|
|
96
|
+
fence(&meta, &id, 1).unwrap();
|
|
97
|
+
assert_eq!(
|
|
98
|
+
add(&store, &meta, &id, &writer, "weight_2", 3)
|
|
99
|
+
.unwrap_err()
|
|
100
|
+
.code,
|
|
101
|
+
Code::WRITER_FENCED
|
|
102
|
+
);
|
|
103
|
+
release_guards(&meta, Some(writer.writer_hold), writer.source_leases);
|
|
104
|
+
crate::gc::collect(store.root(), false).unwrap();
|
|
105
|
+
assert!(store.contains(&ObjectRef::of(&[1u8; 512]).sha256));
|
|
106
|
+
assert!(store.contains(&ObjectRef::of(&[2u8; 512]).sha256));
|
|
107
|
+
let resumed = begin(&store, &meta, &id, 2, declaration, None).unwrap();
|
|
108
|
+
assert_eq!(completed(&meta, &id, 2).unwrap().0.len(), 2);
|
|
109
|
+
add_indexed_part(
|
|
110
|
+
&store,
|
|
111
|
+
&meta,
|
|
112
|
+
&id,
|
|
113
|
+
2,
|
|
114
|
+
&resumed.additions,
|
|
115
|
+
("model", "weight_2", "value"),
|
|
116
|
+
&mut [3u8; 512].as_slice(),
|
|
117
|
+
)
|
|
118
|
+
.unwrap();
|
|
119
|
+
let receipt = commit(&store, &meta, &id, 2).unwrap();
|
|
120
|
+
assert_eq!(receipt.added_objects.len(), 3);
|
|
121
|
+
assert_eq!(stored_rows(&store, &id).1, None);
|
|
122
|
+
fence(&meta, &id, 2).unwrap();
|
|
123
|
+
release_guards(&meta, Some(resumed.writer_hold), resumed.source_leases);
|
|
124
|
+
std::fs::remove_dir_all(store.root()).unwrap();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
#[test]
|
|
128
|
+
fn an_older_builds_inline_row_and_a_torn_journal_line_are_read() {
|
|
129
|
+
let (store, meta, id, declaration) = fixture("legacy");
|
|
130
|
+
let writer = begin(&store, &meta, &id, 1, declaration, None).unwrap();
|
|
131
|
+
add(&store, &meta, &id, &writer, "weight_0", 1).unwrap();
|
|
132
|
+
// An older build rewrites the row with its parts inline and no open session.
|
|
133
|
+
let row = meta.derived_row(&id).unwrap().unwrap();
|
|
134
|
+
let connection = catalog(&store);
|
|
135
|
+
connection
|
|
136
|
+
.execute("DELETE FROM tensorfs_derived_parts WHERE id=?1", [&id])
|
|
137
|
+
.unwrap();
|
|
138
|
+
connection
|
|
139
|
+
.execute(
|
|
140
|
+
"UPDATE tensorfs_derived_transactions SET bytes=?2, open_session=NULL WHERE id=?1",
|
|
141
|
+
params![id, crate::canon::write(&row.to_value())],
|
|
142
|
+
)
|
|
143
|
+
.unwrap();
|
|
144
|
+
// A writer that died mid-append left a torn journal line.
|
|
145
|
+
let journal = store
|
|
146
|
+
.root()
|
|
147
|
+
.join("roots/derived")
|
|
148
|
+
.join(format!("{}.parts", id.trim_start_matches("sha256:")));
|
|
149
|
+
let mut torn = std::fs::read(&journal).unwrap();
|
|
150
|
+
torn.extend_from_slice(b"[{\"length\":512,\"sha");
|
|
151
|
+
std::fs::write(&journal, torn).unwrap();
|
|
152
|
+
|
|
153
|
+
assert_eq!(
|
|
154
|
+
add(&store, &meta, &id, &writer, "weight_0", 9)
|
|
155
|
+
.unwrap_err()
|
|
156
|
+
.code,
|
|
157
|
+
Code::TRANSACTION_CONFLICT
|
|
158
|
+
);
|
|
159
|
+
add(&store, &meta, &id, &writer, "weight_1", 2).unwrap();
|
|
160
|
+
assert_eq!(completed(&meta, &id, 1).unwrap().0.len(), 2);
|
|
161
|
+
fence(&meta, &id, 1).unwrap();
|
|
162
|
+
release_guards(&meta, Some(writer.writer_hold), writer.source_leases);
|
|
163
|
+
crate::gc::collect(store.root(), false).unwrap();
|
|
164
|
+
assert!(store.contains(&ObjectRef::of(&[1u8; 512]).sha256));
|
|
165
|
+
assert!(store.contains(&ObjectRef::of(&[2u8; 512]).sha256));
|
|
166
|
+
abandon(&store, &meta, &id).unwrap();
|
|
167
|
+
assert!(!journal.exists());
|
|
168
|
+
std::fs::remove_dir_all(store.root()).unwrap();
|
|
169
|
+
}
|
|
@@ -984,11 +984,12 @@ fn profile_derived_transaction_scaling() {
|
|
|
984
984
|
for index in 0..writes {
|
|
985
985
|
let key = format!("weight_{index:04}");
|
|
986
986
|
let started = Instant::now();
|
|
987
|
-
|
|
987
|
+
add_indexed_part(
|
|
988
988
|
&store,
|
|
989
989
|
&meta,
|
|
990
990
|
&id,
|
|
991
991
|
1,
|
|
992
|
+
&writer.additions,
|
|
992
993
|
("model", &key, "value"),
|
|
993
994
|
&mut [index as u8; 16].as_slice(),
|
|
994
995
|
)
|
|
@@ -22,6 +22,8 @@ pub struct RetainedResult {
|
|
|
22
22
|
pub receipt_digest: String,
|
|
23
23
|
pub manifest: Option<ObjectRef>,
|
|
24
24
|
pub released: bool,
|
|
25
|
+
/// The newest TensorFS that wrote this root.
|
|
26
|
+
pub tensorfs: Option<String>,
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
impl Plain for RetainedResult {
|
|
@@ -40,11 +42,18 @@ impl Plain for RetainedResult {
|
|
|
40
42
|
Value::Bool(value) => *value,
|
|
41
43
|
_ => return refuse(Code::WRONG_TYPE, "retained result release must be boolean"),
|
|
42
44
|
};
|
|
43
|
-
f
|
|
45
|
+
let tensorfs = f
|
|
46
|
+
.opt("tensorfs")
|
|
47
|
+
.map(|value| {
|
|
48
|
+
crate::canon::as_str("RetainedResult", "tensorfs", value).map(str::to_string)
|
|
49
|
+
})
|
|
50
|
+
.transpose()?;
|
|
51
|
+
f.done_written_by(tensorfs.as_deref())?;
|
|
44
52
|
if !released && manifest.is_none() {
|
|
45
53
|
return refuse(Code::MISSING_FIELD, "held result has no manifest");
|
|
46
54
|
}
|
|
47
55
|
Ok(Self {
|
|
56
|
+
tensorfs,
|
|
48
57
|
retention_id,
|
|
49
58
|
transaction_id,
|
|
50
59
|
receipt_digest,
|
|
@@ -63,6 +72,9 @@ impl Plain for RetainedResult {
|
|
|
63
72
|
if let Some(manifest) = &self.manifest {
|
|
64
73
|
fields.push(("manifest", manifest.to_value()));
|
|
65
74
|
}
|
|
75
|
+
if let Some(tensorfs) = &self.tensorfs {
|
|
76
|
+
fields.push(("tensorfs", Value::str(tensorfs.clone())));
|
|
77
|
+
}
|
|
66
78
|
Value::obj(fields)
|
|
67
79
|
}
|
|
68
80
|
}
|
|
@@ -104,6 +116,10 @@ fn read(store: &Store, id: &str) -> Result<Option<RetainedResult>> {
|
|
|
104
116
|
}
|
|
105
117
|
|
|
106
118
|
fn write(store: &Store, root: &RetainedResult) -> Result<()> {
|
|
119
|
+
let root = &RetainedResult {
|
|
120
|
+
tensorfs: Some(crate::VERSION.into()),
|
|
121
|
+
..root.clone()
|
|
122
|
+
};
|
|
107
123
|
let path = path(store, &root.retention_id)?;
|
|
108
124
|
fs::create_dir_all(directory(store)).map_err(io)?;
|
|
109
125
|
let temp = path.with_extension(format!("tmp-{}", crate::meta::now_nanos_unique()));
|
|
@@ -128,6 +144,7 @@ fn write(store: &Store, root: &RetainedResult) -> Result<()> {
|
|
|
128
144
|
|
|
129
145
|
fn identity(transaction: &str, receipt: &str, id: &str) -> Result<RetainedResult> {
|
|
130
146
|
Ok(RetainedResult {
|
|
147
|
+
tensorfs: Some(crate::VERSION.into()),
|
|
131
148
|
retention_id: prefixed("retention", id)?,
|
|
132
149
|
transaction_id: prefixed("transaction", transaction)?,
|
|
133
150
|
receipt_digest: prefixed("receipt", receipt)?,
|