Skip to content

foldToSnapshot overwrites work.json after an unparseable snapshot or unreadable event log, with nothing recorded #2513

Open

Description

@drpaulobernardy

What happened

foldToSnapshot() publishes work.json from readSnapshot() + replayOntoSnapshot(). Both helpers turn "could not read" into "empty/unchanged" without recording it, and the fold then writes the result with a fresh _folded_at:

  • work.json exists but does not parse -> readSnapshot returns { sessions: {} }. After a compaction (log truncated to the suffix, older log in the archive directory), the fold writes a snapshot that only holds sessions from the surviving suffix. Everything folded before the compaction is gone from work.json, and nothing says so.
  • The event log cannot be stat'ed (permissions, EIO) -> replayOntoSnapshot returns the snapshot's sessions with offset: 0; the fold writes _events_offset: 0 and _folded_at: now. If the log cannot be read after a successful stat, it returns quietly with the old offset. Either way the snapshot is stamped "folded just now" over an input that was never read.

The header says a skipped fold is safe because "events stay in the log" and the next writer picks them up. That holds for lock contention (acquireLock fails -> return false), not for these two paths, which proceed and write.

Root cause

hooks/lib/work-events.ts:

  • :448-455 readSnapshot catch returns an empty registry for any error, not only ENOENT.
  • :466-471 and :484-485 replayOntoSnapshot catches return without any report.
  • :564-593 foldToSnapshot has no way to know a read failed, so it publishes.

Suggestion

Report the failure and skip the publish. Only ENOENT is a legitimate quiet case.

// replayOntoSnapshot returns a `problem` when stat/read fail with anything but ENOENT
return { registry, offset, problem: `event log unreadable: ${e.code ?? e.message}` };
...
// foldToSnapshot
const { registry, offset, problem } = replayOntoSnapshot(snapshot, logPath);
if (problem) { statvsAnomaly('work-events.fold-skipped', problem, logPath); return false; }

and in readSnapshot, treat a parse error as a problem (anomaly + fold skipped) while ENOENT returns the empty registry. We carry a local patch with this shape for the log; the snapshot side is the same idea. Read, not run.

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions