Skip to content

Prevent events drain from overwriting concurrently appended events #38

@antfleet-ops

Description

@antfleet-ops

AntFleet two-model review found a data-loss race in events drain when a listener is concurrently appending to the same event file.

Summary

events listen --output appends events directly to the output path. events drain renames that file to a .lock path, processes the locked copy, and later writes remaining events back to the original path. If a listener appends a new event to the original path after the rename but before the drain writeback, the writeback can overwrite the newly appended event.

Evidence

  • src/commands/events.ts: listener output writer appends to opts.output
  • src/commands/events.ts: drain path renames file to file + .lock, then later calls writeFileSync(file, ...)

Impact

This violates the queue-like contract of draining existing events while preserving newly produced events. A concurrently appended event can be silently discarded by the drain writeback.

Reproduction shape

  1. Run acp events listen --output events.ndjson.
  2. Start acp events drain --file events.ndjson --limit 1.
  3. Have a new event appended after renameSync(file, lockFile) but before the drain command writes remaining locked-file events back to events.ndjson.
  4. The newly appended event can be overwritten by writeFileSync(file, ...).

Suggested fix

Make producer and drainer share a real lock protocol, or change the drain writeback so it never clobbers a recreated original file. For example, preserve and merge any current contents at the original path before writing remaining locked-file events, or append remaining lines instead of truncating where appropriate.

Suggested regression test

Add an integration or fs-mocked test that pauses drain after the rename, appends a new event to the original file path, resumes drain, and asserts the new event remains present afterward.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions