Skip to content

Replace full-file rewrite persistence with a reliable write-ahead log #249

Description

@rayvn-42

What problem would this address?

Cache (as of this moment) holds the whole database in memory and rewrites the entire .sdb file on every write via flush(). That's the main cause for persistency and data loss issues (as stated in #160 and #90). Both of these issues are because of write-time corruption risk and non-atomic full-file rewrites. And in #97 attemepted a WAL-based fix but got halted because of missing tests for WAL, compaction and crash recovery.

What outcome would help?

This issue proposes (and FYI, already has a working, fully tested implementation of the same general direction that #97 was going for). An append only write-ahead log <file>.wal that every write durably appends and fsyncs to immediately and with periodic compaction, appending the WAL file into the JSON file and clearing the WAL. Cache's public methods (like insert, select, delete, load) keep their existing signatures, so that nothing that calls into Cache, needs to change.

A few scope decisions:

  • Compaction trigger is a fixed write counter (defaults to 50), and it is not a size nor a time based trigger, open to suggestions if another approach is preffered.
  • No cross-process locking, as for now the existing cache.lock only ever protects multiple threads withing a single process, multiple processes writing the same file concurrently is a seperate and much more difficult problem, which I don't attempt to solve within this issue. I have also updated docs/persistence.md to state this issue.
  • .sdb's on-disk format remains unchanged as a plain JSON snapshot, only addition is the WAL file.
  • flush() becomes a no-op, and will be kept only because main.py (currently) still schedules it as a background task after each write. And durability now happens synchronously inso insert/delete instead, which is a strictly stronger guarantee than before when using flush(), since no background task is needed.

Additional context

Using #222 as a guide to check in before any major changes take place, since this approach is already built and tested, I'm opening this issue for visibility and to discuss different approaches and methods. I'll be happy to modify and change my code if anything doesn't line up.

Activity

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

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions