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.
What problem would this address?
Cache(as of this moment) holds the whole database in memory and rewrites the entire.sdbfile on every write viaflush(). 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>.walthat 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 (likeinsert,select,delete,load) keep their existing signatures, so that nothing that calls intoCache, needs to change.A few scope decisions:
cache.lockonly 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 updateddocs/persistence.mdto 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 becausemain.py(currently) still schedules it as a background task after each write. And durability now happens synchronously insoinsert/deleteinstead, which is a strictly stronger guarantee than before when usingflush(), 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.