Skip to content

fix: wal replay recovery - #314

Draft
chrollo-lucifer-12 wants to merge 10 commits into
danieldeer:mainfrom
chrollo-lucifer-12:fix/wal-replay-recovery
Draft

chrollo-lucifer-12 wants to merge 10 commits into
danieldeer:mainfrom
chrollo-lucifer-12:fix/wal-replay-recovery

Conversation

@chrollo-lucifer-12

Copy link
Copy Markdown

Description

This pr addresses the problems discussed in issue #306

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update

Key Changes

  • close() now closes the file and sets self._file = None.
  • Added _ensure_open() so the WAL can reopen its file lazily after being closed.
  • Update replay and append to _ensure_open()
  • Wrapped write(), flush(), and fsync() in try/except.If any of them fails, the file is closed and the handle is cleared.The next append() then repairs any partial/torn data before reopening and writing.
  • Cache.load() close the previous wal before creating a new one.

Validation

Added a benchmark test for wal appends.
Running the test shows the following result after doing the said updates:

------------------------------------------------ benchmark 'wal': 1 tests ------------------------------------------------
Name (time in us) Min Max Mean StdDev Median IQR Outliers OPS (Kops/s) Rounds Iterations

test_wal_append 753.3000 946.1000 809.6280 40.9104 797.3000 57.8000 9;2 1.2351 50 1

Checklist

  • I reviewed the diff and kept it focused on the described change.
  • Python changes pass uv run ruff check . and uv run ruff format --check ., or this PR does not change Python.
  • Changed behavior is covered by appropriate tests, including a regression test for bug fixes; any limitations are explained under Validation.
  • Validation lists the checks or manual flows I ran and their results.
  • Relevant documentation is updated, or no documentation change is needed.

@chrollo-lucifer-12 chrollo-lucifer-12 changed the title Fix/wal replay recovery fix: wal replay recovery Sep 30, 2026
Comment thread src/seriousdb/wal.py
@VihaanReddyM

Copy link
Copy Markdown
Collaborator

@chrollo-lucifer-12 any updates?

@mattsva

mattsva commented Oct 8, 2026

Copy link
Copy Markdown
Collaborator

@chrollo-lucifer-12 please update it and fix conflicts

@chrollo-lucifer-12

Copy link
Copy Markdown
Author

@chrollo-lucifer-12 please update it and fix conflicts

Sorry for the delay I'll do it today, i was preoccupied with something

@chrollo-lucifer-12

Copy link
Copy Markdown
Author

@mattsva @VihaanReddyM i have resolved the conflicts

@mattsva mattsva left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ive used AI (Claude Sonnet 5.5) to help me debug the CI errors. This is about what we got with your changes I guess.

Note this is just a help, first please make all the CI jobs run smoothly, maybe those AI review helps you, if not, its ok, dont take it too serious.

Please fix @chrollo-lucifer-12, afterwards Iam glade to first review the code another time, and then merge it, if it is fine.

Comment thread src/seriousdb/wal.py Outdated
Comment thread src/seriousdb/wal.py
Comment thread src/seriousdb/wal.py Outdated
Comment thread src/seriousdb/wal.py Outdated
Comment thread src/seriousdb/wal.py Outdated
Comment thread src/seriousdb/wal.py Outdated
Comment thread benchmarks/test_wal.py
@mattsva
mattsva marked this pull request as draft October 12, 2026 12:17
@chrollo-lucifer-12

Copy link
Copy Markdown
Author

Ive used AI (Claude Sonnet 5.5) to help me debug the CI errors. This is about what we got with your changes I guess.

Note this is just a help, first please make all the CI jobs run smoothly, maybe those AI review helps you, if not, its ok, dont take it too serious.

Please fix @chrollo-lucifer-12, afterwards Iam glade to first review the code another time, and then merge it, if it is fine.

thanks for the review i will do the changes

@chrollo-lucifer-12

Copy link
Copy Markdown
Author

@mattsva i have done the requested changes, all tests now pass including the tests and ty typecheck. But in ruff checks i get "Use a context manager for opening files".

@mattsva mattsva left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Over all it is good from code quality. It is not mergeable yet, but it is pretty good.
Please fix the changes, that are overall mainly formatting things, i am not sure if you need to add something to the pyproject.toml to make the SIM115 be quite, but that would be okay too.
Also add the better benchmark, so we can really see how big of a change this is.

At all, really good work @chrollo-lucifer-12 .

Any more questions?

Comment thread src/seriousdb/wal.py Outdated
def __init__(self, filename: str):
self.filename = filename
self._offset = os.path.getsize(filename) if os.path.isfile(filename) else 0
self._file = open(filename, "a+b")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ruff SIM115 fails here. The handle is kept open on purpose and closed in close(), so please silence it with a reason:
self._file = open(filename, "a+b") # noqa: SIM115 - kept open, closed in close() or something like this.

Comment thread src/seriousdb/wal.py Outdated
def _ensure_open(self):
"""Return the open WAL file, reopening it if necessary."""
if self._file is None:
self._file = open(self.filename, "a+b")

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: add # noqa: SIM115 - reopened lazily, closed in close().

Comment thread src/seriousdb/wal.py Outdated
def close(self) -> None:
"""Close the WAL file if it is open."""
f, self._file = self._file, None
if f is not None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess indentation is off (9 spaces), so ruff format --check fails. Running uv run ruff format . fixes it.

(I guess it is somewhere around here, but uv run ruff format . should definitely catch it)

Comment thread src/seriousdb/wal.py Outdated
if line:
try:
entries.append(WalEntry.from_dict(json.loads(line.decode())))
except (json.JSONDecodeError, UnicodeDecodeError, TypeError, ValueError) as e:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is too long for ruff format. uv run ruff format . will wrap it.

Comment thread src/seriousdb/wal.py
self._offset = good_offset
return entries

def clear(self) -> None:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If self.close() raises, the temp file created above is left behind, because close() runs outside the try block. Please move it inside the try so the temp file is always cleaned up.
It may be good to have a test for those things.

Comment thread src/seriousdb/wal.py
actual_size = os.path.getsize(self.filename)
if actual_size > self._offset:
with open(self.filename, "r+b") as f:
f.truncate(self._offset)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing newline at end of file. ruff format adds it. (On all files)

Comment thread src/seriousdb/cache.py
except OSError:
pass
raise
old_wal = self.wal

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Closing the old WAL after the swap is fine, and safer than closing it first, since a failed load keeps the old state. Please add a test that the old WAL's handle is closed after a second load().

This is optinal, but I guess/hope it helps

Comment thread benchmarks/test_wal.py
@@ -0,0 +1,46 @@
"""Benchmarks for write-ahead log persistence."""

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This measures only the first append on a fresh file, so it mostly shows fsync cost. Since the change is about reusing the handle, please add a second benchmark that appends many entries to one open WAL, and post before/after numbers (command, OS, Python version, workload).

Make it more telling, please.

@chrollo-lucifer-12

Copy link
Copy Markdown
Author

Over all it is good from code quality. It is not mergeable yet, but it is pretty good. Please fix the changes, that are overall mainly formatting things, i am not sure if you need to add something to the pyproject.toml to make the SIM115 be quite, but that would be okay too. Also add the better benchmark, so we can really see how big of a change this is.

At all, really good work @chrollo-lucifer-12 .

Any more questions?

thanks, i dont ususally write python more into golang.

@chrollo-lucifer-12

Copy link
Copy Markdown
Author

Benchmarked WAL handle reuse on Linux (WSL2), Python 3.12.3, using pytest-benchmark.

Command:
uv run pytest benchmarks/test_wal.py --benchmark-only

Workload: one append to a fresh WAL, and 1,000 appends using a single open WAL.

Benchmark | Before | After -- | -- | -- Single append | 1.845 ms | 1.680 ms 1,000 appends | 1,444.807 ms | 1,353.096 ms Average per append (batch) | 1.445 ms | 1.353 ms

The repeated-append benchmark improved by approximately 6.4%.

these are the benchmarks

@chrollo-lucifer-12

Copy link
Copy Markdown
Author

the bottleneck is still fsync, maybe we can try fsync in batches ? @mattsva

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants