Skip to content

Commit 145b1c8

Browse files
authored
fix: checkpoint WAL on database dispose (#6)
Add WAL checkpoint (TRUNCATE) before disposing SQLite file-based databases. This ensures: - Data is flushed to the main database file - External readers can see committed data - Clean shutdown without hanging Bump version to 0.6.0
1 parent 0a2a682 commit 145b1c8

4 files changed

Lines changed: 32 additions & 2 deletions

File tree

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "servicekit"
3-
version = "0.5.4"
3+
version = "0.6.0"
44
description = "Async SQLAlchemy framework with FastAPI integration - reusable foundation for building data services"
55
readme = "README.md"
66
authors = [{ name = "Morten Hansen", email = "morten@winterop.com" }]

src/servicekit/database.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,16 @@ async def init(self) -> None:
179179
# For file-based databases, use Alembic migrations
180180
await super().init()
181181

182+
async def dispose(self) -> None:
183+
"""Dispose database with WAL checkpoint for file-based databases."""
184+
if not self.is_in_memory():
185+
try:
186+
async with self.engine.begin() as conn:
187+
await conn.exec_driver_sql("PRAGMA wal_checkpoint(TRUNCATE);")
188+
except Exception:
189+
pass # Don't fail dispose on checkpoint error
190+
await super().dispose()
191+
182192

183193
class SqliteDatabaseBuilder:
184194
"""Builder for SQLite database configuration with fluent API."""

tests/test_database.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,23 @@ def test_builder_without_url_raises_error(self) -> None:
346346
builder = SqliteDatabaseBuilder()
347347
with pytest.raises(ValueError, match="Database URL not configured"):
348348
builder.build()
349+
350+
async def test_dispose_checkpoints_wal(self, tmp_path: Path) -> None:
351+
"""Test that dispose checkpoints WAL for file-based databases."""
352+
db_path = tmp_path / "test.db"
353+
db = SqliteDatabaseBuilder.from_file(str(db_path)).build()
354+
await db.init()
355+
356+
# Write some data to create WAL entries
357+
async with db.session() as session:
358+
await session.execute(text("CREATE TABLE test_wal (id INTEGER)"))
359+
await session.execute(text("INSERT INTO test_wal VALUES (1)"))
360+
await session.commit()
361+
362+
# WAL file path
363+
wal_path = db_path.parent / f"{db_path.name}-wal"
364+
365+
await db.dispose()
366+
367+
# After dispose with TRUNCATE checkpoint, WAL should be empty or removed
368+
assert not wal_path.exists() or wal_path.stat().st_size == 0

uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)