WAL + incremental vacuum for space happiness

This commit is contained in:
Jack Kingsman
2026-02-21 00:04:27 -08:00
parent 9e3b1d03a9
commit 6d0505ade6
3 changed files with 144 additions and 19 deletions
+11
View File
@@ -79,6 +79,17 @@ class Database:
Path(self.db_path).parent.mkdir(parents=True, exist_ok=True)
self._connection = await aiosqlite.connect(self.db_path)
self._connection.row_factory = aiosqlite.Row
# WAL mode: faster writes, concurrent readers during writes, no journal file churn.
# Persists in the DB file but we set it explicitly on every connection.
await self._connection.execute("PRAGMA journal_mode = WAL")
# Incremental auto-vacuum: freed pages are reclaimable via
# PRAGMA incremental_vacuum without a full VACUUM. Must be set before
# the first table is created (for new databases); for existing databases
# migration 20 handles the one-time VACUUM to restructure the file.
await self._connection.execute("PRAGMA auto_vacuum = INCREMENTAL")
await self._connection.executescript(SCHEMA)
await self._connection.commit()
logger.debug("Database schema initialized")
+47 -6
View File
@@ -170,16 +170,17 @@ async def run_migrations(conn: aiosqlite.Connection) -> int:
await set_version(conn, 19)
applied += 1
# Migration 20: Enable WAL journal mode and incremental auto-vacuum
if version < 20:
logger.info("Applying migration 20: enable WAL mode and incremental auto-vacuum")
await _migrate_020_enable_wal_and_auto_vacuum(conn)
await set_version(conn, 20)
applied += 1
if applied > 0:
logger.info(
"Applied %d migration(s), schema now at version %d", applied, await get_version(conn)
)
# Reclaim disk space after table-rebuild migrations
if version < 19:
logger.info("Running VACUUM to reclaim disk space (this may take a moment)...")
await conn.execute("VACUUM")
logger.info("VACUUM complete")
else:
logger.debug("Schema up to date at version %d", version)
@@ -1211,3 +1212,43 @@ async def _migrate_019_drop_messages_unique_constraint(conn: aiosqlite.Connectio
await conn.commit()
logger.info("messages table rebuilt without UNIQUE constraint")
async def _migrate_020_enable_wal_and_auto_vacuum(conn: aiosqlite.Connection) -> None:
"""
Enable WAL journal mode and incremental auto-vacuum.
WAL (Write-Ahead Logging):
- Faster writes: appends to a WAL file instead of rewriting the main DB
- Concurrent reads during writes (readers don't block writers)
- No journal file create/delete churn on every commit
Incremental auto-vacuum:
- Pages freed by DELETE become reclaimable without a full VACUUM
- Call PRAGMA incremental_vacuum to reclaim on demand
- Less overhead than FULL auto-vacuum (which reorganizes on every commit)
auto_vacuum mode change requires a VACUUM to restructure the file.
The VACUUM is performed before switching to WAL so it runs under the
current journal mode; WAL is then set as the final step.
"""
# Check current auto_vacuum mode
cursor = await conn.execute("PRAGMA auto_vacuum")
row = await cursor.fetchone()
current_auto_vacuum = row[0] if row else 0
if current_auto_vacuum != 2: # 2 = INCREMENTAL
logger.info("Switching auto_vacuum to INCREMENTAL (requires VACUUM)...")
await conn.execute("PRAGMA auto_vacuum = INCREMENTAL")
await conn.execute("VACUUM")
logger.info("VACUUM complete, auto_vacuum set to INCREMENTAL")
else:
logger.debug("auto_vacuum already INCREMENTAL, skipping VACUUM")
# Enable WAL mode (idempotent — returns current mode)
cursor = await conn.execute("PRAGMA journal_mode = WAL")
row = await cursor.fetchone()
mode = row[0] if row else "unknown"
logger.info("Journal mode set to %s", mode)
await conn.commit()