test,chore: clear the static and coverage lanes for the cache stack
Static: the cache package.json files array matches the workspace constraint shape, the unused dsh-storage-json devDependency is dropped (tests run on the memory backend), and docs/module-graph.md is regenerated for the new package edge. Coverage: two unreachable branches deleted rather than tested — coldSnapshot's floor-0 tail reuse (a baseSeq-0 restore never throws and an unrelated record still carries a usable watermark) and flushSoft's non-mandatory clean-skip (throttle triggers only fire dirty). New tests close the real gaps: write() on a never-dirty session and the non-JSON unit-state rejection, plugin disposal clearing armed interval timers, cachedSnapshot's all-version-mismatched and cwd-identity arms, the zero-units empty-log cut, the coordinator seek-hook ladder (suffix / not-found / plain failure / abort-reason relay), and the superseded- retirement race proving forget()'s exact-entry guard.
This commit is contained in:
@@ -186,9 +186,10 @@ export class SessionProjectionCache extends Service {
|
||||
restored = this.ctx.sessionProjections.restore(cached, tail.events, floor)
|
||||
} catch {
|
||||
// The recoverable restore failures: an unrelated record, or a row
|
||||
// overreaching the stored log end (or predating the floor). All
|
||||
// resolve identically — discard the cache and refold the full log.
|
||||
const whole = floor === 0 && related ? tail : await persistence.readFrom(id, 0, signal)
|
||||
// overreaching the stored log end (or predating the floor). Both imply
|
||||
// floor > 0 (baseSeq-0 restores never throw and an unrelated record
|
||||
// still carried a usable watermark), so the full log is a fresh read.
|
||||
const whole = await persistence.readFrom(id, 0, signal)
|
||||
restored = this.ctx.sessionProjections.restore({}, whole.events, 0)
|
||||
}
|
||||
await this.putSoft(id, identityOf(tail.meta), restored.checkpoint, 'cold-read write-back')
|
||||
@@ -237,11 +238,12 @@ export class SessionProjectionCache extends Service {
|
||||
}, 'sessionProjectionCache.timers')
|
||||
}
|
||||
|
||||
/** One fail-soft durable checkpoint: skip when clean, log on failure. */
|
||||
/**
|
||||
* One fail-soft durable checkpoint. Every caller has work by construction:
|
||||
* the throttle triggers only fire dirty (markClean clears the timer with
|
||||
* the counter) and the two mandatory points write unconditionally.
|
||||
*/
|
||||
private async flushSoft(session: Session, trigger: string): Promise<void> {
|
||||
const state = this.dirty.get(session)
|
||||
const mandatory = trigger === 'turn/end' || trigger === 'detach'
|
||||
if (!mandatory && (state === undefined || state.pending === 0)) return
|
||||
try {
|
||||
await this.write(session)
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user