Skip to content

fix(prefix-cache): reuse slots after aborted snapshots - #563

Open
dusterbloom wants to merge 1 commit into
Luce-Org:mainfrom
dusterbloom:agent/prefix-cache-abort-hole-fix
Open

fix(prefix-cache): reuse slots after aborted snapshots#563
dusterbloom wants to merge 1 commit into
Luce-Org:mainfrom
dusterbloom:agent/prefix-cache-abort-hole-fix

Conversation

@dusterbloom

@dusterbloom dusterbloom commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Why

PR #561 combined this production correctness change with the formal-verification
platform and three later targets. This draft extracts the first independently
reviewable and revertible unit.

The inline prefix-cache allocator advances its round-robin cursor when it
reserves a slot. If that reservation is aborted, the resulting hole can be
skipped while the cursor points back at a committed slot. A later reservation
could then return the occupied slot instead of the available hole, putting a
still-valid snapshot at risk when the caller frees or reuses the returned slot.

What changed

  • extracts the inline prefix-cache transitions into dependency-free
    InlinePrefixCacheState production code;
  • selects an actually free slot before considering eviction, preserving the
    round-robin starting point;
  • keeps prefix-aware leaf eviction, stale-entry removal, confirmation,
    cancellation, abort, and clear behavior in the extracted state core;
  • adds a native regression for
    commit/prepare/abort/prepare hole reuse without invalidating the committed
    snapshot;
  • registers that dependency-free regression with CMake and the existing hosted
    C++ CI job.

Scope boundary

This PR contains production correctness work and native CI coverage only. It
adds no formal contracts, verifier images, formal workflow, credentials, AI,
or branch-protection change.

The local formal registry will follow in a separate dependent draft. CI
integration remains a later maintainer/security review.

User impact

Aborted inline snapshot creation no longer causes a later reservation to reuse
an occupied slot while a free slot exists. Public APIs and configuration are
unchanged.

Validation

  • warning-enabled C++17 build and execution of test_prefix_cache_state;
  • warning-enabled compilation of production prefix_cache.cpp;
  • CUDA 12.6 CMake configuration;
  • cmake --build ... --target test_prefix_cache_state -j4;
  • ctest -R '^prefix_cache_state$' --no-tests=error;
  • git diff --check.

The full GPU/server matrix is left to hosted CI.

Release sequence

This is PR 1 of the staged replacement for #561. It has no dependency on the
formal-verification rollout and can be reviewed, merged, or reverted on its
own.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 6 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="server/src/server/prefix_cache_state.h">

<violation number="1" location="server/src/server/prefix_cache_state.h:87">
P2: Standalone `InlinePrefixCacheState` instances with capacity >64 can still overwrite an occupied slot when a hole exists, so the class does not maintain the free-slot guarantee promised by its transition logic. Either clamp/reject capacities above 64 at the state boundary or use an occupancy representation that covers the full accepted capacity.</violation>
</file>

<file name="server/src/server/prefix_cache.cpp">

<violation number="1" location="server/src/server/prefix_cache.cpp:255">
P2: An invalid confirmation can leave the pending eviction reservation active while the caller proceeds as if the snapshot committed, so later requests inherit stale reservation state and the cache metadata can diverge from the backend slot. Treating this rejection as an abort (or otherwise clearing the reservation) would keep the lifecycle consistent.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

// PrefixCache clamps capacity to 64. Preserve the legacy round-robin
// behavior for out-of-contract standalone capacities that do not fit the
// occupancy mask.
if (capacity > 64) return next_slot;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Standalone InlinePrefixCacheState instances with capacity >64 can still overwrite an occupied slot when a hole exists, so the class does not maintain the free-slot guarantee promised by its transition logic. Either clamp/reject capacities above 64 at the state boundary or use an occupancy representation that covers the full accepted capacity.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/server/prefix_cache_state.h, line 87:

<comment>Standalone `InlinePrefixCacheState` instances with capacity >64 can still overwrite an occupied slot when a hole exists, so the class does not maintain the free-slot guarantee promised by its transition logic. Either clamp/reject capacities above 64 at the state boundary or use an occupancy representation that covers the full accepted capacity.</comment>

<file context>
@@ -0,0 +1,281 @@
+    // PrefixCache clamps capacity to 64. Preserve the legacy round-robin
+    // behavior for out-of-contract standalone capacities that do not fit the
+    // occupancy mask.
+    if (capacity > 64) return next_slot;
+
+    for (int offset = 0; offset < capacity; ++offset) {
</file context>

entries_size_count_.fetch_sub(1, std::memory_order_relaxed);
}
has_pending_evict_ = false;
if (slot < 0 || slot >= cap_ || target_cut <= 0 ||

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: An invalid confirmation can leave the pending eviction reservation active while the caller proceeds as if the snapshot committed, so later requests inherit stale reservation state and the cache metadata can diverge from the backend slot. Treating this rejection as an abort (or otherwise clearing the reservation) would keep the lifecycle consistent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At server/src/server/prefix_cache.cpp, line 255:

<comment>An invalid confirmation can leave the pending eviction reservation active while the caller proceeds as if the snapshot committed, so later requests inherit stale reservation state and the cache metadata can diverge from the backend slot. Treating this rejection as an abort (or otherwise clearing the reservation) would keep the lifecycle consistent.</comment>

<file context>
@@ -281,68 +236,44 @@ std::pair<int, int> PrefixCache::prepare_inline_snap(
-            entries_size_count_.fetch_sub(1, std::memory_order_relaxed);
-        }
-        has_pending_evict_ = false;
+    if (slot < 0 || slot >= cap_ || target_cut <= 0 ||
+        target_cut > (int)prompt_ids.size()) {
+        std::fprintf(stderr,
</file context>

@dusterbloom
dusterbloom marked this pull request as draft July 31, 2026 14:25
@dusterbloom
dusterbloom marked this pull request as ready for review July 31, 2026 14:59

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 6 files

Re-trigger cubic

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant