[GH-ISSUE #362] [Design Proposal] Shared resource holds across the sandbox lifecycle #92

Open
opened 2026-10-29 14:19:29 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @guozy18 on GitHub (Oct 12, 2026).
Original GitHub issue: https://github.com/kvcache-ai/AgentENV/issues/362

Affected component

Orchestrator / sandbox lifecycle, snapshot and volume content protection

Problem statement

Parent: Database design proposal. This proposal builds on Data model and interfaces; reclamation is covered by Online GC and historical data control, and SQL version coexistence by Database upgrades and compatibility.

Recording layer UUIDs, predecessor links, and content heads in SQL is not enough to protect a running sandbox. Prepared entries, VMs, fork results, paused state, and background readers may continue using the same source. Deleting a public snapshot or changing a volume backing must not remove bytes that these consumers still need.

Usage protection must follow the existing resource ownership chain without adding database access to every creation, derivation, pause, or exit. Protecting only the top-level request would let cancellation, cache eviction, or parent VM exit end protection before the actual I/O finishes.

Use case

The primary workload is many sandboxes publishing their own independent snapshots and restoring them occasionally, with little reuse across sandboxes. A benchmark where every request hits one popular template cannot establish an acceptable database cost: publishing a new S and preparing it for the first time on a node still require database operations.

A snapshot may also serve multiple VMs, derived children, or a long-lived paused consumer. The design must cover both low-reuse working sets and concurrent use of the same S. Cache TTL must not impose a lifetime limit on active consumers.

Current behavior and workarounds

SnapshotManager loads a catalog record and resolves it into runnable input. The snapshot catalog and publication pipeline manage publication through the shared Repository; local resource ownership keeps runtime files and ongoing tasks alive. Snapshot and volume publication already share a layer-storage pipeline. A volume retains its business identity while its backing layer list can change, and a sandbox snapshot captures a fixed copy of its volume layer lists. Local snapshots retain files in their publishing process, and promotion publishes shared artifacts while preserving the snapshot identity.

Local file ownership and byte caches cannot, by themselves, tell the database that remote dependencies are still in use. SQL protection also cannot replace the actual lifecycle of local files, devices, or in-flight I/O.

Desired behavior

  • Acquire valid protection and fix the selected manifest_ref before reading the manifest or consuming managed artifacts. Load the immutable description under that protection, outside the SQL transaction. Subsequent consumers retain fixed inputs rather than being switched to a later public description.
  • Coalesce cold preparation for the same S on the same node. Valid warm acquisition, fork, same-process pause/resume, and ordinary exit add no Catalog SQL or central metadata requests.
  • Delete rejects new cold acquisition while established valid use continues. Final release runs in the background only after every actual responsibility ends; GC does not require pausing user operations.
  • During a database outage, existing consumers and valid prepared entries continue. New cold acquisition or recovery checks may wait or fail; they must not manufacture protection.

Proposed approach

Use the shared content-retention contract from the data-model proposal: immutable descriptions referenced by manifest_ref, retained chain heads, layers identified by the unified existing UUID with fixed predecessor links, and active usage responsibilities. Do not expand every published snapshot into a complete snapshot-to-layer mapping or introduce another layer UUID.

For the snapshot protocol below, S is a public snapshot and H is one shared usage hold tied to S. A sandbox ID is distinct from S. Multiple local responsibilities can share one H; SQL does not record a row for every VM. The detailed S/H logging and recovery rules describe this snapshot protocol; sharing the retention mechanism with volumes does not require every hold to occupy one generic table.

A volume ID remains a mutable business identity. Acquiring a volume backing must fix the selected content and retain it for the consumer, rather than following subsequent changes to that volume's current backing. Exclusive and read-only reservations retain their existing mount and writer-admission roles; they do not, by themselves, replace content protection. The concrete relation between volume records, fixed content, and holds is defined with the data model; this issue does not require a new volume-version ID.

Operations before and after

“Cold snapshot preparation” means the node has no reusable input for that snapshot. It is distinct from starting a sandbox directly from an OCI image.

Operation Before After: flow and database boundary
Cold start from an OCI image Prepare the image and runtime configuration, then start the sandbox Keep the existing flow; this action does not automatically create a public S or H
Cold preparation from S Get record, resolve, return RunnableSnapshot Fix S, durably record H, Pin to fix the protected manifest reference, durably confirm held, then read the manifest, materialize, and deliver
Valid warm reuse Bytes/local inputs may be reused, but load_runnable still starts with get Check Ready validity, share fixed inputs and H, then start; this acquisition adds no SQL
Fork / internal capture Capture runtime state and transfer local artifacts Carry necessary source H and inherited layer UUIDs through the existing lease into internal results and children; preserve sealed prefixes and create identities only for genuinely new layers; do not automatically create a public S or H
Publish a captured result The publication pipeline stores artifacts and commits the catalog record Retain source responsibilities, complete required artifacts and the immutable manifest, then atomically establish S, its manifest reference, new layers, and retained heads; unfinished background readers retain their responsibilities
Local snapshot / promotion The publishing process retains local files; promotion publishes shared artifacts and updates the record for the same S Acquire H together with the original process's file guard; atomically switch the public description on promotion and retain the heads needed by both old and new inputs while preserving old readers' fixed descriptions
Pause, then same-process resume Persist paused inputs and transfer ownership to the runtime Persist holds outside the paused backend payload; acquire the new responsibility before releasing the old one; reuse valid existing holds without SQL
Recovery after restart Follow existing persistent state and recovery rules Inspect the original H read-only, then use saved inputs; Missing/Closed/conflicting results cannot be repaired with a new Pin; do not expand recovery eligibility
Exit / delete a sandbox Stop the VM, devices, and I/O, then release local resources Release only this consumer's responsibility; background Release waits for cache, children, paused state, and I/O to finish; do not call Delete(S)
Publish or replace volume backing The volume enters Uploading, publishes its backing, then updates the layer list and returns to Ready Preserve the current reservation rules; establish protection for the new backing before switching the current content, and retain any old backing still needed by consumers or I/O
Capture a volume into a snapshot, or restore it into a new volume Copy fixed layer descriptors into the captured snapshot or newly created volume Keep source content protected until the target's heads are committed; the target then has its own retention, independent of the original business record

Volume backing handoff and background readers

Suppose V currently retains [A, B], and consumer P has acquired that fixed backing. Publishing [A, C] first establishes retention for the new backing, then changes V's current content. P continues to retain [A, B]; B becomes reclaimable only after P and every other old-content responsibility end. This is a content handoff, not an instruction to allocate a new volume-version ID or to change the volume's reservation semantics.

Likewise, a snapshot captured from V must establish its own heads while the source remains protected. Restoring that snapshot into a new volume performs the reverse handoff. Afterwards, removing the original snapshot or volume cannot invalidate the new owner's content.

Background tasks retain the exact files and source responsibilities they use until their actual I/O ends. This includes startup recording, upload, P2P publication and serving, and external server-side copy. A UUID advertisement or metadata entry is not evidence that the corresponding reads have ended. If P2P serves an independently owned local copy, its own file lease can carry that responsibility; do not retain an unrelated remote hold after it is no longer needed. Conversely, cancellation or request completion cannot release a source still being read.

Rust pseudocode: extend the existing lease

Compose the local artifact lease and necessary source holds in the runnable input:

struct ProtectedArtifacts {
    local: Arc<dyn RuntimeArtifactLease>, // Existing local artifact ownership.
    sources: Vec<SharedHold>,             // Necessary source S/H responsibilities.
}
impl RuntimeArtifactLease for ProtectedArtifacts {}

async fn prepare(s: SnapshotId) -> Result<RunnableSnapshot> {
    // Internally: Sync acquiring -> Catalog.pin(s, h) -> Sync held.
    // Pin fixes the protected manifest_ref before any manifest or artifact read.
    let opened = holds.open(s).await?;
    // Read the fixed manifest under its hold, outside the SQL transaction.
    // Combine local leases and source SharedHold values into RunnableSnapshot.
    resolver.resolve_protected(opened).await
}

let input = prepared.get_or_prepare(s, prepare).await?;
let runtime = start_with_lease(input.clone()).await?;
// Fork/capture results, paused state, and new runtimes retain needed source holds.
// clone/drop transfers local responsibility; no individual Drop closes SQL H.
runtime.stop_and_drain_io().await?;
drop(runtime); // H remains active if the cache or another consumer still holds it.
// Final responsibility ends: Sync releasing -> background Release -> clear log.

Store H logs durably outside temporary artifacts, and scan holds/<H> separately from paused records. Log phases advance monotonically through acquiring -> held -> releasing. A failed or uncertain Sync is not made durable merely because a read sees the value. Keep paused holds outside the backend's opaque payload so a corrupt payload does not discard source responsibilities.

Resolve an uncertain initial acquisition using the original S/H. Reconstruct an already-delivered hold through read-only inspect. One H can support multiple derived inputs; the current public manifest reference cannot replace all of their saved recovery configurations. Cross-process consumers must establish one long-lived release owner; a short-lived proxy cannot release delivered protection when it exits.

Minimum prepared-cache contract

Rule Required behavior
Scope and concurrency Coalesce Loading by node/S; only reusable Distributed inputs enter Ready. Local process-owned files remain separately managed
Access time Use a monotonic clock; initialize last_access on installation and refresh only after successful acquisition of a local hold. Failed queries, ongoing VM execution, fork/capture, pause/resume, and block I/O do not refresh it
Eviction selection Evict Ready entries idle beyond T, or the least recently accessed Ready entries when their count exceeds N. Loading does not participate in TTL/LRU. Measure T/N rather than fixing a time default here
Checks and scanning Check expiry on a hit, and periodically clean idle Ready entries with no subsequent requests. Order acquisition, refresh, and removal atomically; recheck entry identity and access time before removal
Capacity boundary N limits reusable Ready entries, not Loading, evicted inputs still in use, or total node memory. It is not the OverlayBD byte-cache capacity
Cancellation and delivery Cancellation of every waiter does not end I/O. The background task owns successful installation or failure cleanup. A successful result retains H during installation and delivery to waiters, even if Ready is immediately evicted
Acquisition after eviction Eviction releases only the cache's share. A new request cold-acquires a new H rather than finding the old H through a weak index. Existing consumers and permitted derivations continue their original holds
Names and late callbacks Remove cached name bindings with their entry. Late callbacks may affect only their original entry/H, not remove a newer generation by S

Repeated hits refresh access time, so T is not a deadline for revoking access after Delete. An expired Ready entry cannot grant new acquisition merely because the database is unavailable: new acquisition follows the cold Pin result, including retirement and connection failures. Cache TTL is distinct from the admission floor in Historical data control; neither expires active H.

SQL boundaries on cold and warm paths

Cold preparation resolves identity and performs Pin before fetching the protected manifest. Publication commits a manifest reference, retained heads, and any new layer records for the completed result. Final return performs Release. Rebuilding a hold from persistent records uses read-only inspect. Valid warm acquisition, fork, same-process pause/resume, and ordinary exit perform local handoffs only; measure background Release separately.

For low-reuse workloads, measure actual cold-preparation rates, SQL per preparation/publication, hold lifetimes, and connection waits. The prepared cache reduces repeated acquisition; it cannot eliminate the facts that independently published snapshots must record, or replace layer GC and historical-row cleanup. Measure publication work against new layers and retained heads as well as total chain length; inherited prefixes must not be re-registered as complete mappings.

Acceptance criteria

  • Select T/N and scan frequency from measured working sets. Verify source handoffs for background recording/P2P, persistent input fields, and volume-related consumers.
  • Content acquisition: Pin fixes the protected manifest reference before manifest or artifact I/O; a later volume-backing switch does not change an existing consumer's inputs.
  • Shared backing: cover volume-to-snapshot capture, snapshot-to-volume restore, volume cloning, and a backing replacement while old consumers remain. Removing one owner must not reclaim layers still retained by another.
  • Layer identity: fork and unchanged prefixes preserve sealed UUIDs; new memory layers and compaction results carry the correct predecessor. A changed predecessor or conflicting published descriptor is not silently accepted under an existing UUID.
  • Background I/O: cancellation, publication completion, UUID advertisement, or a database connection loss must not release protection while recording, upload, P2P reads, or external copy still accesses the source.
  • Cold/warm: prepare the same S once under concurrency; measure SQL and tail latency separately for low-reuse independent S; verify no added central requests at each valid warm entry point.
  • Cache: test idle cleanup without new requests, excess N, non-eviction of Loading, cancellation of all waiters, delivery racing eviction, and old callbacks racing new entries. Verify that local file guards and byte caches are not released incorrectly.
  • Lifecycle: children and internal results remain readable after parent exit; paused recovery uses the original H; ordinary exit does not wait for final Release; no reclamation precedes the end of all actual responsibilities.
  • Faults: interrupt each Sync/delivery/Release boundary. Preserve responsibilities during database outages, corrupt payloads, reused device IDs, and uncertain server-side copy. Neither not-found nor process exit proves that I/O ended.

Compatibility and operational impact

Area Impact and boundary
API/config changes Business callers continue acquiring inputs through Manager; H is internal. Cache settings require measurement; no new user-facing hold API
Snapshot or storage format changes Catalog fixes the protected manifest reference and retention state; the immutable manifest provides the complete description. Local hold logs and the outer paused record gain versioned holds while retaining the existing RuntimeArtifactLease file/task responsibilities
New host/runtime requirements Reuse durable records and device lifecycle management; no new node-lease service. Recovery must have a single owner
Upgrade and rollback considerations Check old readers, hold records, background recording/P2P, and release paths together. Upgrading only HTTP entry points cannot enable new formats; see Upgrade proposal

Preserve existing volume ownership and lifecycle. Publication and GC must account for snapshot rootfs, memory, attached drives, embedded volume snapshots, and independent volume backings, as well as all actual consumers. VM state, startup packs/manifests, and AENV-owned registry publications remain part of the retention inventory even though they are not layer-chain nodes.

Alternatives considered

  • One H per VM adds registrations and releases without solving fork/paused/I/O handoff correctness. Prefer sharing the actual preparation cycle.
  • File caching alone cannot protect shared repository artifacts. Querying SQL on every warm request adds central access to reuse. This proposal reuses fixed inputs together with protection.
  • Releasing H on cache eviction or expiring it when a node disconnects can end protection while consumers or I/O are still active; these are not acceptable simplifications.

Non-goals

Do not change VM, block-device, or external-image semantics. Do not introduce a new volume product model, volume-version ID, or Local/promotion API. Do not prescribe a generic hold table, repeat the schema, layer-GC protocol, or admission-floor algorithm here, or replace online protection with a maintenance pause.

Contribution

I can provide more design details

Pre-submission checklist

  • I searched existing issues and discussions and did not find a duplicate.
  • I described a concrete problem and use case, not only a proposed technology.
  • I understand that acceptance of the problem does not imply approval of a particular implementation.
Originally created by @guozy18 on GitHub (Oct 12, 2026). Original GitHub issue: https://github.com/kvcache-ai/AgentENV/issues/362 ### Affected component Orchestrator / sandbox lifecycle, snapshot and volume content protection ### Problem statement Parent: [Database design proposal](https://github.com/kvcache-ai/AgentENV/issues/360). This proposal builds on [Data model and interfaces](https://github.com/kvcache-ai/AgentENV/issues/361); reclamation is covered by [Online GC and historical data control](https://github.com/kvcache-ai/AgentENV/issues/364), and SQL version coexistence by [Database upgrades and compatibility](https://github.com/kvcache-ai/AgentENV/issues/365). Recording layer UUIDs, predecessor links, and content heads in SQL is not enough to protect a running sandbox. Prepared entries, VMs, fork results, paused state, and background readers may continue using the same source. Deleting a public snapshot or changing a volume backing must not remove bytes that these consumers still need. Usage protection must follow the existing resource ownership chain without adding database access to every creation, derivation, pause, or exit. Protecting only the top-level request would let cancellation, cache eviction, or parent VM exit end protection before the actual I/O finishes. ### Use case The primary workload is many sandboxes publishing their own independent snapshots and restoring them occasionally, with little reuse across sandboxes. A benchmark where every request hits one popular template cannot establish an acceptable database cost: publishing a new S and preparing it for the first time on a node still require database operations. A snapshot may also serve multiple VMs, derived children, or a long-lived paused consumer. The design must cover both low-reuse working sets and concurrent use of the same S. Cache TTL must not impose a lifetime limit on active consumers. ### Current behavior and workarounds SnapshotManager loads a catalog record and resolves it into runnable input. The snapshot catalog and publication pipeline manage publication through the shared Repository; local resource ownership keeps runtime files and ongoing tasks alive. Snapshot and volume publication already share a layer-storage pipeline. A volume retains its business identity while its backing layer list can change, and a sandbox snapshot captures a fixed copy of its volume layer lists. Local snapshots retain files in their publishing process, and promotion publishes shared artifacts while preserving the snapshot identity. Local file ownership and byte caches cannot, by themselves, tell the database that remote dependencies are still in use. SQL protection also cannot replace the actual lifecycle of local files, devices, or in-flight I/O. ### Desired behavior - Acquire valid protection and fix the selected `manifest_ref` before reading the manifest or consuming managed artifacts. Load the immutable description under that protection, outside the SQL transaction. Subsequent consumers retain fixed inputs rather than being switched to a later public description. - Coalesce cold preparation for the same S on the same node. Valid warm acquisition, fork, same-process pause/resume, and ordinary exit add no Catalog SQL or central metadata requests. - Delete rejects new cold acquisition while established valid use continues. Final release runs in the background only after every actual responsibility ends; GC does not require pausing user operations. - During a database outage, existing consumers and valid prepared entries continue. New cold acquisition or recovery checks may wait or fail; they must not manufacture protection. ### Proposed approach Use the shared content-retention contract from the data-model proposal: immutable descriptions referenced by `manifest_ref`, retained chain heads, layers identified by the unified existing UUID with fixed predecessor links, and active usage responsibilities. Do not expand every published snapshot into a complete snapshot-to-layer mapping or introduce another layer UUID. For the snapshot protocol below, S is a public snapshot and H is one shared usage hold tied to S. A sandbox ID is distinct from S. Multiple local responsibilities can share one H; SQL does not record a row for every VM. The detailed S/H logging and recovery rules describe this snapshot protocol; sharing the retention mechanism with volumes does not require every hold to occupy one generic table. A volume ID remains a mutable business identity. Acquiring a volume backing must fix the selected content and retain it for the consumer, rather than following subsequent changes to that volume's current backing. Exclusive and read-only reservations retain their existing mount and writer-admission roles; they do not, by themselves, replace content protection. The concrete relation between volume records, fixed content, and holds is defined with the data model; this issue does not require a new volume-version ID. ### Operations before and after “Cold snapshot preparation” means the node has no reusable input for that snapshot. It is distinct from starting a sandbox directly from an OCI image. | Operation | Before | After: flow and database boundary | |---|---|---| | Cold start from an OCI image | Prepare the image and runtime configuration, then start the sandbox | Keep the existing flow; this action does not automatically create a public S or H | | Cold preparation from S | Get record, resolve, return RunnableSnapshot | Fix S, durably record H, Pin to fix the protected manifest reference, durably confirm held, then read the manifest, materialize, and deliver | | Valid warm reuse | Bytes/local inputs may be reused, but load_runnable still starts with get | Check Ready validity, share fixed inputs and H, then start; this acquisition adds no SQL | | Fork / internal capture | Capture runtime state and transfer local artifacts | Carry necessary source H and inherited layer UUIDs through the existing lease into internal results and children; preserve sealed prefixes and create identities only for genuinely new layers; do not automatically create a public S or H | | Publish a captured result | The publication pipeline stores artifacts and commits the catalog record | Retain source responsibilities, complete required artifacts and the immutable manifest, then atomically establish S, its manifest reference, new layers, and retained heads; unfinished background readers retain their responsibilities | | Local snapshot / promotion | The publishing process retains local files; promotion publishes shared artifacts and updates the record for the same S | Acquire H together with the original process's file guard; atomically switch the public description on promotion and retain the heads needed by both old and new inputs while preserving old readers' fixed descriptions | | Pause, then same-process resume | Persist paused inputs and transfer ownership to the runtime | Persist holds outside the paused backend payload; acquire the new responsibility before releasing the old one; reuse valid existing holds without SQL | | Recovery after restart | Follow existing persistent state and recovery rules | Inspect the original H read-only, then use saved inputs; Missing/Closed/conflicting results cannot be repaired with a new Pin; do not expand recovery eligibility | | Exit / delete a sandbox | Stop the VM, devices, and I/O, then release local resources | Release only this consumer's responsibility; background Release waits for cache, children, paused state, and I/O to finish; do not call Delete(S) | | Publish or replace volume backing | The volume enters Uploading, publishes its backing, then updates the layer list and returns to Ready | Preserve the current reservation rules; establish protection for the new backing before switching the current content, and retain any old backing still needed by consumers or I/O | | Capture a volume into a snapshot, or restore it into a new volume | Copy fixed layer descriptors into the captured snapshot or newly created volume | Keep source content protected until the target's heads are committed; the target then has its own retention, independent of the original business record | ### Volume backing handoff and background readers Suppose V currently retains `[A, B]`, and consumer P has acquired that fixed backing. Publishing `[A, C]` first establishes retention for the new backing, then changes V's current content. P continues to retain `[A, B]`; B becomes reclaimable only after P and every other old-content responsibility end. This is a content handoff, not an instruction to allocate a new volume-version ID or to change the volume's reservation semantics. Likewise, a snapshot captured from V must establish its own heads while the source remains protected. Restoring that snapshot into a new volume performs the reverse handoff. Afterwards, removing the original snapshot or volume cannot invalidate the new owner's content. Background tasks retain the exact files and source responsibilities they use until their actual I/O ends. This includes startup recording, upload, P2P publication and serving, and external server-side copy. A UUID advertisement or metadata entry is not evidence that the corresponding reads have ended. If P2P serves an independently owned local copy, its own file lease can carry that responsibility; do not retain an unrelated remote hold after it is no longer needed. Conversely, cancellation or request completion cannot release a source still being read. ### Rust pseudocode: extend the existing lease Compose the local artifact lease and necessary source holds in the runnable input: ~~~rust struct ProtectedArtifacts { local: Arc<dyn RuntimeArtifactLease>, // Existing local artifact ownership. sources: Vec<SharedHold>, // Necessary source S/H responsibilities. } impl RuntimeArtifactLease for ProtectedArtifacts {} async fn prepare(s: SnapshotId) -> Result<RunnableSnapshot> { // Internally: Sync acquiring -> Catalog.pin(s, h) -> Sync held. // Pin fixes the protected manifest_ref before any manifest or artifact read. let opened = holds.open(s).await?; // Read the fixed manifest under its hold, outside the SQL transaction. // Combine local leases and source SharedHold values into RunnableSnapshot. resolver.resolve_protected(opened).await } let input = prepared.get_or_prepare(s, prepare).await?; let runtime = start_with_lease(input.clone()).await?; // Fork/capture results, paused state, and new runtimes retain needed source holds. // clone/drop transfers local responsibility; no individual Drop closes SQL H. runtime.stop_and_drain_io().await?; drop(runtime); // H remains active if the cache or another consumer still holds it. // Final responsibility ends: Sync releasing -> background Release -> clear log. ~~~ Store H logs durably outside temporary artifacts, and scan `holds/<H>` separately from paused records. Log phases advance monotonically through `acquiring -> held -> releasing`. A failed or uncertain Sync is not made durable merely because a read sees the value. Keep paused holds outside the backend's opaque payload so a corrupt payload does not discard source responsibilities. Resolve an uncertain initial acquisition using the original S/H. Reconstruct an already-delivered hold through read-only inspect. One H can support multiple derived inputs; the current public manifest reference cannot replace all of their saved recovery configurations. Cross-process consumers must establish one long-lived release owner; a short-lived proxy cannot release delivered protection when it exits. ### Minimum prepared-cache contract | Rule | Required behavior | |---|---| | Scope and concurrency | Coalesce Loading by node/S; only reusable Distributed inputs enter Ready. Local process-owned files remain separately managed | | Access time | Use a monotonic clock; initialize last_access on installation and refresh only after successful acquisition of a local hold. Failed queries, ongoing VM execution, fork/capture, pause/resume, and block I/O do not refresh it | | Eviction selection | Evict Ready entries idle beyond T, or the least recently accessed Ready entries when their count exceeds N. Loading does not participate in TTL/LRU. Measure T/N rather than fixing a time default here | | Checks and scanning | Check expiry on a hit, and periodically clean idle Ready entries with no subsequent requests. Order acquisition, refresh, and removal atomically; recheck entry identity and access time before removal | | Capacity boundary | N limits reusable Ready entries, not Loading, evicted inputs still in use, or total node memory. It is not the OverlayBD byte-cache capacity | | Cancellation and delivery | Cancellation of every waiter does not end I/O. The background task owns successful installation or failure cleanup. A successful result retains H during installation and delivery to waiters, even if Ready is immediately evicted | | Acquisition after eviction | Eviction releases only the cache's share. A new request cold-acquires a new H rather than finding the old H through a weak index. Existing consumers and permitted derivations continue their original holds | | Names and late callbacks | Remove cached name bindings with their entry. Late callbacks may affect only their original entry/H, not remove a newer generation by S | Repeated hits refresh access time, so T is not a deadline for revoking access after Delete. An expired Ready entry cannot grant new acquisition merely because the database is unavailable: new acquisition follows the cold Pin result, including retirement and connection failures. Cache TTL is distinct from the admission floor in Historical data control; neither expires active H. ### SQL boundaries on cold and warm paths Cold preparation resolves identity and performs Pin before fetching the protected manifest. Publication commits a manifest reference, retained heads, and any new layer records for the completed result. Final return performs Release. Rebuilding a hold from persistent records uses read-only inspect. Valid warm acquisition, fork, same-process pause/resume, and ordinary exit perform local handoffs only; measure background Release separately. For low-reuse workloads, measure actual cold-preparation rates, SQL per preparation/publication, hold lifetimes, and connection waits. The prepared cache reduces repeated acquisition; it cannot eliminate the facts that independently published snapshots must record, or replace layer GC and historical-row cleanup. Measure publication work against new layers and retained heads as well as total chain length; inherited prefixes must not be re-registered as complete mappings. ### Acceptance criteria - [ ] Select T/N and scan frequency from measured working sets. Verify source handoffs for background recording/P2P, persistent input fields, and volume-related consumers. - [ ] Content acquisition: Pin fixes the protected manifest reference before manifest or artifact I/O; a later volume-backing switch does not change an existing consumer's inputs. - [ ] Shared backing: cover volume-to-snapshot capture, snapshot-to-volume restore, volume cloning, and a backing replacement while old consumers remain. Removing one owner must not reclaim layers still retained by another. - [ ] Layer identity: fork and unchanged prefixes preserve sealed UUIDs; new memory layers and compaction results carry the correct predecessor. A changed predecessor or conflicting published descriptor is not silently accepted under an existing UUID. - [ ] Background I/O: cancellation, publication completion, UUID advertisement, or a database connection loss must not release protection while recording, upload, P2P reads, or external copy still accesses the source. - [ ] Cold/warm: prepare the same S once under concurrency; measure SQL and tail latency separately for low-reuse independent S; verify no added central requests at each valid warm entry point. - [ ] Cache: test idle cleanup without new requests, excess N, non-eviction of Loading, cancellation of all waiters, delivery racing eviction, and old callbacks racing new entries. Verify that local file guards and byte caches are not released incorrectly. - [ ] Lifecycle: children and internal results remain readable after parent exit; paused recovery uses the original H; ordinary exit does not wait for final Release; no reclamation precedes the end of all actual responsibilities. - [ ] Faults: interrupt each Sync/delivery/Release boundary. Preserve responsibilities during database outages, corrupt payloads, reused device IDs, and uncertain server-side copy. Neither not-found nor process exit proves that I/O ended. ### Compatibility and operational impact | Area | Impact and boundary | |---|---| | API/config changes | Business callers continue acquiring inputs through Manager; H is internal. Cache settings require measurement; no new user-facing hold API | | Snapshot or storage format changes | Catalog fixes the protected manifest reference and retention state; the immutable manifest provides the complete description. Local hold logs and the outer paused record gain versioned holds while retaining the existing RuntimeArtifactLease file/task responsibilities | | New host/runtime requirements | Reuse durable records and device lifecycle management; no new node-lease service. Recovery must have a single owner | | Upgrade and rollback considerations | Check old readers, hold records, background recording/P2P, and release paths together. Upgrading only HTTP entry points cannot enable new formats; see Upgrade proposal | Preserve existing volume ownership and lifecycle. Publication and GC must account for snapshot rootfs, memory, attached drives, embedded volume snapshots, and independent volume backings, as well as all actual consumers. VM state, startup packs/manifests, and AENV-owned registry publications remain part of the retention inventory even though they are not layer-chain nodes. ### Alternatives considered - One H per VM adds registrations and releases without solving fork/paused/I/O handoff correctness. Prefer sharing the actual preparation cycle. - File caching alone cannot protect shared repository artifacts. Querying SQL on every warm request adds central access to reuse. This proposal reuses fixed inputs together with protection. - Releasing H on cache eviction or expiring it when a node disconnects can end protection while consumers or I/O are still active; these are not acceptable simplifications. ### Non-goals Do not change VM, block-device, or external-image semantics. Do not introduce a new volume product model, volume-version ID, or Local/promotion API. Do not prescribe a generic hold table, repeat the schema, layer-GC protocol, or admission-floor algorithm here, or replace online protection with a maintenance pause. ### Contribution I can provide more design details ### Pre-submission checklist - [x] I searched existing issues and discussions and did not find a duplicate. - [x] I described a concrete problem and use case, not only a proposed technology. - [x] I understand that acceptance of the problem does not imply approval of a particular implementation.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
kvcache-ai/AgentENV#92
No description provided.