Skip to content

Torn audit entry throws RangeError out of transaction-log iteration instead of degrading #1977

Description

@heskew

Summary

A torn audit entry causes readAuditEntry to throw a RangeError out of transaction-log iteration, surfacing as an unhandled rejection rather than being handled as recognizable corruption.

Observed on 5.1.x, on both the main thread and a worker:

Reading audit entry error RangeError: Audit entry username length 436207616
  exceeds remaining buffer (position 116, byteLength 117)
    at readAuditEntry (.../core/resources/auditStore.js:543)
    at ExtendedIterable.<anonymous> (.../core/resources/RocksTransactionLogStore.js:436)

A declared username length of 436,207,616 against a 117-byte buffer is not an ambiguous edge case — it is a corrupt record. Throwing from inside the iterator means the corruption takes out whatever was iterating.

How the record got torn

The instance had exhausted its filesystem quota, so writes were being refused mid-operation. That left a partially written entry, which persisted across the restart. Any abrupt write failure — full disk, quota, power loss, crash — can produce the same tear.

The replay path already handles this correctly

The replication replay path degrades gracefully on the analogous condition, at warn level, and keeps the process healthy:

[warn]: Stopping transaction log "local" at a corrupt entry during replay
  RangeError: Corrupt transaction log entry at position ...:
  declared length 1688494450 overruns the log (limit=...)

So there are two readers of potentially torn records and only one of them degrades gracefully. The audit reader should behave like the replay reader.

Suggested direction

Treat an implausible declared length as corruption at the read boundary: log it, and stop or skip cleanly the way replay does, rather than propagating a RangeError to the caller. A bounds check against the remaining buffer before trusting the length field is enough to distinguish "corrupt record" from "programming error," which is the distinction the current code can't make.

Worth deciding as part of this whether a corrupt entry should stop iteration (replay's choice, safest for ordering guarantees) or skip and continue — they have different implications for audit completeness, and the answer may differ from replay's.


🤖 Filed by Claude on behalf of @heskew

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:storageStorage engine, LMDB/RocksDB, compactionbugSomething isn't working

    Type

    Fields

    Priority

    P1

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions