Skip to content

v0.3.0: Durable Commit and explicit fsync/recovery guarantees #69

Description

@yifanxuaaa

Problem and goal

LayerFS currently acknowledges live-process visibility without guaranteeing that an acknowledged snapshot survives an OS crash or power loss. v0.3.0 should introduce an explicit, tested durability contract, starting with durable public Commit: after Commit returns successfully, the published snapshot, its dependencies, and the corresponding history/head changes survive the supported crash and recovery scenarios.

Related: #52 (v0.3.0 cloud native integration and S3-compatible persistence). This issue owns the local durability foundation and its acknowledgement contract; coordinate remote replication semantics with #52.

Current behavior

  • The Store configures SQLite journal_mode=MEMORY, synchronous=OFF, and locking_mode=EXCLUSIVE, and rejects WAL Stores during opening.
  • Workspace fsync currently validates spool state and completes capture; it does not provide a disk persistence guarantee. Spool segment files are unlinked after opening, so adding sync_all() alone would not make live edits recoverable.
  • A public Commit can include multiple object-admission transactions, a staged-root transaction, and final publication. Durability cannot be estimated or implemented as one unconditional fsync per public operation.

Relevant code: crates/layerfs-layerstack-store/src/schema.rs, objects.rs, workspace.rs, staging.rs; crates/layerfs-workspace/src/file_io.rs and lifecycle.rs.

Scope and acceptance

  • Define acknowledgement guarantees for ordinary writes, application file/directory fsync, public Commit, initialization/Add, and remote replication. State which data and metadata each boundary protects, and distinguish process loss, OS crash/power loss, and loss of the storage device or host.
  • Implement durable public Commit first. Persist all referenced canonical objects and required metadata before acknowledging the atomically published result; preserve conditional head updates, retained history, and v0.2 reconciliation semantics.
  • Select the smallest recoverable SQLite journal/synchronization configuration. Evaluate WAL + FULL and an appropriate disk-backed rollback-journal alternative; do not merely change synchronous while leaving MEMORY journaling in place. Specify platform flush semantics, checkpointing, locking, Store creation/directory persistence, and migration/reopen compatibility.
  • Handle I/O and synchronization failures without reporting false durable success. Define recovery and retry behavior when publication succeeds but its response is lost, and when the persistence outcome is uncertain.
  • Make an explicit, separate decision on durable live-workspace file/directory fsync. If included, persist the recoverable edit representation and required metadata as well as bytes. If deferred, document the weaker uncommitted-workspace contract clearly; durable Commit must not imply that every successful application fsync is power-loss durable.
  • Keep memory, dirty state, journal/WAL growth, and retry work bounded. Preserve canonical identities and protect published dependencies during recovery and cleanup.

Correctness proof

  • Exercise failures before/after object admission, staging, publication, synchronization, and acknowledgement; cover synchronization errors and lost replies.
  • Reopen after failure and verify database integrity, acknowledged roots, history/heads, canonical IDs, and file bytes. Never expose a published root with missing dependencies or silently lose an acknowledged durable snapshot.
  • Prove migration and supported reopen behavior for existing Stores, including recovery with required journal/WAL sidecars.
  • Distinguish process-kill tests from evidence for OS-crash/power-loss behavior. Freeze the supported platform/storage assumptions and retain reproducible failure evidence.

Performance validation

Measure the existing benchmark workloads under separately named profiles: current visibility-only baseline, durable Store publication, and durable Store plus live-workspace fsync if implemented. Preserve operation counts, fixtures, public calls, and timing boundaries.

Prioritize:

  • SDK edit families, including canonical-count and capped length-changing cases: short edit + Commit latency.

  • payload_create_read, init_namespace, store_footprint, dedup_cross_file, and dedup_cdc_locality: admission transaction count, dirty bytes, and journal/write amplification.

  • dedup_workspace_reuse, mixed_load_bearing, and git_tool_workflow: sequential application synchronization.

  • dedup_branch_history, especially unrelated history: 200 file fsync calls per step, or 100,000 at tier 500, plus directory syncs and repeated Commit.

  • Tiny-file churn, namespace/directory operations, workspace locality, read-only execution, and clean Commit: retain batching and identify unnecessary persistence work.

  • Report actual physical flush count/time, SQLite transaction count, Exec/admission/publication latency, p50/p95/p99, throughput, CPU/RSS, and storage growth. Keep preparation and recovery verification outside their unrelated performance timers.

  • Account for journal/WAL sidecars in storage metrics and adapt the current closed-Store preparation/clone compatibility checks; do not reuse incompatible prepared Stores or omit durability files.

  • Run paired measurements on the supported host-store topology, then regress affected family members and reliability proofs. Do not remove workload fsync calls or weaken the promised acknowledgement boundary to pass a target.

Planning expectation from discussion: roughly 30% more time for ordinary writes and around 2x small-edit latency is acceptable as an initial tradeoff, but these are unmeasured budgeting assumptions, not acceptance thresholds or published performance claims. Per-file durable fsync can make history cases much slower. Set numerical release targets from the measured implementation and an explicit decision on that contract.

Done when

The chosen v0.3.0 durability boundary is implemented, crash/recovery evidence demonstrates the stated guarantee, supported Stores migrate/reopen safely, and SDK/CLI documentation and benchmark reports state the actual durability and performance tradeoff. Remote replication guarantees remain aligned with #52 and distinct from local persistence.

Supporting study

Comprehensive Drive9 hybrid-storage study — full document. The local roadmap source is docs/roadmap/0.3/drive9-hybrid-storage-study.md (pending commit/push), linked from the v0.3 roadmap index. Covers SQL/object tiering, local writeback and replay, acknowledgement profiles, publication/GC, observed durability gaps, LayerFS recommendations, and all 18 benchmark families. Source observations are pinned to Drive9 commit cff6d294b452b54a964de2217c137af303602be9; no new performance or crash-test claims.

Storage amplification and retained history

The attached Drive9 study now records the agent-workload finding: borrow acknowledgement and recovery ordering, but do not adopt ordinary whole-file replacement objects as LayerFS's cloud content layout. Preserve the existing CAS/CDC graph and chunk identities.

Drive9 does have reference-sharing copies and delayed GC. An immutable old blob is not automatically recoverable history: it can be referenced state, an unreferenced object waiting through the cleanup delay, or cleanup backlog. GC cannot reclaim versions intentionally retained by the user. Server-side multipart copy avoids unchanged client traffic but still constructs a complete replacement object.

  • For repeated local edits, checkpoints and forks, preserve unchanged canonical payload sharing. Incremental payload growth should follow new unique content, with metadata/layout costs measured separately; do not require a complete replacement file payload for each small edit.
  • Measure new unique payload, structural metadata, gross physical bytes written, net retained allocation, uploaded/copied/replicated bytes, request counts, and temporary duplication separately. A small net footprint after deletion must not hide full-file rewrite amplification.
  • Bound retry, failed-publication, abandoned-work and eligible-GC backlog; measure before/after GC and compaction without deleting retained history or weakening durability to make results pass.
  • Compare equal retained roots, persisted paths and acknowledgement scope. Include metadata-only/no-op cases using absolute byte counts, and unrelated-content controls; CAS cannot eliminate genuinely new data.
  • Evaluate remote chunk/request overhead and any pack dead space without changing canonical IDs or making packing mandatory before evidence supports it. Coordinate the selected remote layout with v0.3.0: Cloud native integration and S3-compatible persistence #52.

Illustration only: 1,000 separately published edits to a 1 GiB file, with all versions retained, add 1,000 GiB of complete replacement-object payload. Assuming 64 KiB of new unique chunk content per edit, CAS/CDC adds 62.5 MiB of payload plus metadata/layout overhead. These are hypothetical inputs, not measured results or fixed CDC amplification bounds. Freeze numerical acceptance targets from the measured implementation.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestfuturePlanned beyond the current release; not a current delivery commitment.v0.3.0LayerFS v0.3.0 scope, delivery, and supporting evidence.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions