You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
An out-of-crank savepoint rollback leaves the run-queue cache ahead of the database #1071
Found reviewing #1021 (sirtimid/crank-rollback-integrity).
What
KernelStore.rollbackSavepoint is a bare pass-through to kdb.rollbackSavepoint, so the out-of-crank path #1021 formalises gets none of the cache reverting that revertStateBeneathRollback now does for the crank path — and the out-of-crank block writes through exactly the caches that matter.
Failure scenario
A peer sends deliver/message. Inside the turn, #handleRemoteDeliver → KernelQueue.enqueueSend → kernelStore.enqueueRun → ctx.runQueue.enqueue, which does incCounter(head): cached head goes N → N+1 in memory and in the DB, and the item is written at queue.run.N.
releaseSavepoint(savepointName) throws — disk full at COMMIT, the very case the rest of fix: keep a crank's store work inside one transaction #1021 is about — so the driver discards the whole transaction and RemoteHandle's catch calls rollbackSavepoint.
The DB is back at head = N with no queue.run.N row. The in-memory head still says N+1, and runQueueLengthCache is one too high.
The next enqueue writes queue.run.N+1 and sets head = N+2, leaving a permanent hole at N.
When the run loop's dequeueRun reaches tail N, provideStoredQueue.dequeue calls kv.getRequired('queue.run.N') and throws no record matching key 'queue.run.N'. Run loop dead, and everything behind the hole stranded.
setRemoteHighestReceivedSeq throwing reaches the same place.
The same rollback also leaves ctx.maybeFreeKrefs holding the krefs RemoteManager → persistPeerRestart → forgetEndpointImports added; that half is mostly benign, since the restored refcounts make collectGarbage skip them.
Pre-existing in kind — handleRemoteMessage rolled back this way on main too. But #1021 adds the remedy for the sibling path and promotes this one to a first-class transaction, so leaving it bare is the gap a reader would least expect.
Suggested fix
Give KernelStore.rollbackSavepoint the cache half of revertStateBeneathRollback: refreshRunQueue(), runQueueLengthCache = -1, refreshCachedValues(). If the candidate set should be restored too, have createSavepoint stash new Set(context.maybeFreeKrefs) keyed by savepoint name for rollbackSavepoint to put back.
Activity
fix(ocap-kernel): revert the in-memory caches a crank rollback leaves…
fix(ocap-kernel): revert the in-memory caches a crank rollback leaves…