thesix 0.4.0

Policy-driven six-tier cache orchestration with an explicit HPA data-continuity contract
docs.rs failed to build thesix-0.4.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Visit the last successful build: thesix-0.3.0

theSix

CI crates.io docs.rs

Policy-driven six-tier cache orchestration for Rust, built around an explicit high-performance availability (HPA) data-continuity contract.

The contract is the source of truth. theSix.toml declares the architecture's invariants, and this file, the crate docs and AGENTS.md derive from it. Where prose and contract could disagree, the contract wins — and cargo xtask contract fails the build if they do.

The boundary

                    Consumer
                        │
                        ▼
        theSix continuity / policy plane
        locality · availability · performance
        consistency · durability · recovery
                        │
    ┌───────────────────┼───────────────────┐
    ▼                   ▼                   ▼
Performance         Continuity           Security
& locality          & recovery           CIA
    └───────────────────┼───────────────────┘
                        ▼
        Heterogeneous storage / cache / authority

Consumers depend on the continuity contract, not on a backend. L0–L6 are replaceable infrastructure: the rung a value lands on is decided by policy, and authority is configured rather than inferred from a tier number.

Quickstart

use std::sync::Arc;
use thesix::{CacheContext, CacheManager, CacheTier, Cachelito, DefaultPolicy,
             IdentityContext, MemoryPool, TierRegistry};

# async fn example() {
let tiers: Vec<Arc<dyn CacheTier<String>>> = vec![
    Arc::new(thesix::L0Stub::<String>::new()),
    Arc::new(thesix::L1Stub::<String>::new()),
    Arc::new(thesix::L2Stub::<String>::new()),
    Arc::new(thesix::L3Stub::<String>::new()),
    Arc::new(thesix::L4Stub::<String>::new()),
    Arc::new(thesix::L5Stub::<String>::new()),
];
let pool = MemoryPool::<String>::new(1024)?;

let manager: CacheManager<String, String, DefaultPolicy> = CacheManager::new(
    DefaultPolicy, Cachelito::new(), TierRegistry::new(), tiers, pool,
);

// Identity carries the tenant. The tenant is part of the key, so two tenants
// using the same key never share an entry.
let ctx = CacheContext::new(IdentityContext::new(
    "alice".into(), vec!["reader".into()], "tenant-1".into(),
));

let key = "order-42".to_string();
let value = manager
    .get_or_fetch(&key, &ctx, || async { Ok("payload".to_string()) })
    .await?;
# Ok::<(), thesix::CacheError>(())
# }

Application code never names a rung. Every operation takes (key, context) and nothing else.

What the crate guarantees

Property Mechanism
Atomicity Two-phase commit with a control-plane intent record. prepare → write → commit; a crash leaves an intent, never a half-visible value.
Isolation Single-flight population ownership; no control guard is ever held across an .await.
Durability honesty DurabilityClass is Volatile, Delegated, or Verified. Only a test that drops a store and reads it back may grant Verified.
Integrity Every stored value carries a content digest. A damaged record is reported as Corrupted, never served.
Tenant isolation The tenant is framed into the key, so a cross-tenant read finds nothing rather than finding a neighbour's value.
Capability honesty CapabilityFlags × OperationalState × DurabilityClass. An unbound rung is reported Unbound, not silently replaced by another.
Bounded control plane Every rung-scanning path is bounded by LAST_CACHE_TIER, so a fallback can never surface authority data.

Atomicity, concretely

prepare(key, generation, rung)  →  entry is Prepared; reads see a miss
write to the rung               →  no payload anywhere in the control plane
commit(token)                   →  Ready, intent cleared, waiters notified
abort(token)                    →  restores the interrupted state

Recovery resolves an outstanding intent by kind: a write aborts (the value is reproducible), a move completes forward (aborting it would discard an already-committed value). Both directions are idempotent, and a move that cannot be resolved without the key is reported rather than silently skipped.

Verification

Every gate is declared in theSix.toml and executed from it.

just                 # the mandatory gates, in contract order
just gates           # the same, spelled out
just quick           # fmt + contract + check + the fast layers
just list            # what runs, and why
just perf            # percentile and boundedness gates
just soak            # endurance gates
just ready           # is this branch mergeable?
just loom            # exhaustive control-plane interleavings
just plan            # print every gate's argv without running it

cargo xtask is the entry point. It refuses to run a gate the contract does not declare, refuses to start if a declared gate has no execution strategy, and refuses to report success for a gate whose tooling is missing — it exits 3 for "could not verify" rather than 0 for "verified".

Test layers

Layer Target What it demonstrates
contract tests/contract The TOML is load-bearing: version, rung count, registries, layer bindings.
unit integration, hierarchy, policy, stampede Round-trips, TTL, generation rejection, single-flight, authz.
negative negative All 19 required failure modes, each verified by its resulting state.
fault_injection fault_injection All 11 faults, each proven to have fired via its ledger.
property property The 9 named invariants over randomised operation sequences.
concurrency concurrency, await_safety, sharding, loom Adversarial races with forced interleavings.
capability capability, l6_authority No rung claims a backend, authority, or durability it lacks.
recovery recovery The full lifecycle; idempotent, repeatable recovery.
durability durability Drop-and-reopen; only proven claims are Verified.
security security Cross-tenant/key access refused; no payload in errors or telemetry.
performance performance Percentiles, shard independence, bounded control plane.
soak soak Slot-table pressure, tenant isolation at volume, recovery scaling.
backends backends, oxigraph_backend Real backends behind feature gates.

Anti-vacuity

A test that cannot prove its own fault fired is decoration. Three mechanisms enforce this:

  1. FaultLedger counts every fault activation. Every negative and fault-injection test asserts its fault actually fired.
  2. Registries in testkit::coverage are compared for equality against the contract's lists, so a required case cannot be dropped from the suite without the contract gate failing.
  3. Capability assertions check a claim against something observable. A rung reporting Healthy must store; a rung reporting Persistent must survive a restart; a rung reporting authoritative must be the configured authority rung.

Build accelerations

.cargo/config.toml uses sccache; the gate runner additionally selects clang + mold when both are installed, and cargo xtask toolchain reports what it found. The crate itself has no build-script requirement and no unsafe.

Backends

Rung Role In-memory default Real backend
L0 request-local yes —
L1 hot-local yes —
L2 local yes —
L3 distributed fallback redis (feature)
L4 persistent fallback sled (feature)
L5 origin / graph fallback oxigraph (feature)
L6 authority unbound application-defined

The L3–L5 defaults store values so the ladder works out of the box, and report BackendKind::InMemoryFallback — they claim neither SHARED nor PERSISTENT, because they are process-local and saying otherwise would be a durability lie. CacheManager::capabilities() tells you what every rung is actually bound to.

License

MIT.