fix(prefix-cache): reuse slots after aborted snapshots - #563
Conversation
There was a problem hiding this comment.
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; |
There was a problem hiding this comment.
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 || |
There was a problem hiding this comment.
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>
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
InlinePrefixCacheStateproduction code;round-robin starting point;
cancellation, abort, and clear behavior in the extracted state core;
commit/prepare/abort/prepare hole reuse without invalidating the committed
snapshot;
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
test_prefix_cache_state;prefix_cache.cpp;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.