Scope Docker WAL guard to SQLite

This commit is contained in:
Nick Sweeting 2026-08-30 15:20:04 -07:00
parent 654f07e579
commit dccd7fd0a3
No known key found for this signature in database
3 changed files with 38 additions and 8 deletions

View File

@ -423,15 +423,14 @@ class DatabaseConfig(BaseConfigSet):
SQLITE_LOCK_RETRY_TIMEOUT: float = Field(default=60.0, alias="ARCHIVEBOX_SQLITE_LOCK_RETRY_TIMEOUT", ge=0)
SQLITE_LOCK_RETRY_INTERVAL: float = Field(default=5.0, alias="ARCHIVEBOX_SQLITE_LOCK_RETRY_INTERVAL", gt=0)
@field_validator("SQLITE_JOURNAL_MODE", mode="after")
@classmethod
def reject_docker_wal(cls, value: str) -> str:
if IN_DOCKER and value.upper() == "WAL":
@model_validator(mode="after")
def reject_docker_sqlite_wal(self):
if IN_DOCKER and self.DATABASE_ENGINE.lower() == "sqlite" and self.SQLITE_JOURNAL_MODE.upper() == "WAL":
raise ValueError(
"SQLITE_JOURNAL_MODE=WAL is unsafe for Docker collections because host bind mounts cross SQLite "
"locking domains; use DELETE (the Docker default) or PostgreSQL",
)
return value
return self
class ArchivingConfig(BaseConfigSet):

View File

@ -268,6 +268,37 @@ def test_docker_rejects_explicit_wal_override(tmp_path):
assert "WAL is unsafe for Docker collections" in result.stderr
def test_docker_postgres_ignores_irrelevant_sqlite_wal_override(tmp_path):
"""The Docker SQLite safety invariant must not reject PostgreSQL.
Operators can switch an existing deployment to PostgreSQL while an old
SQLITE_JOURNAL_MODE setting remains in ArchiveBox.conf or the environment.
PostgreSQL never consumes that SQLite pragma, so rejecting the otherwise
valid configuration would prevent ArchiveBox from starting without making
any database safer.
"""
env = os.environ.copy()
env["IN_DOCKER"] = "True"
result = subprocess.run(
[
sys.executable,
"-c",
(
"from archivebox.config.common import DatabaseConfig;"
"config=DatabaseConfig(DATABASE_ENGINE='postgres',SQLITE_JOURNAL_MODE='WAL');"
"print(config.DATABASE_ENGINE)"
),
],
cwd=tmp_path,
env=env,
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stderr
assert result.stdout.strip() == "postgres"
def test_server_shows_usage_info(initialized_archive):
"""Test that server command shows usage or starts."""

View File

@ -717,13 +717,13 @@ With the default [`DATABASE_ENGINE`](#database_engine)`=sqlite`, this is the pat
---
#### `SQLITE_JOURNAL_MODE`
**Possible Values:** [`WAL`]/`DELETE`/`TRUNCATE`/`PERSIST`/`MEMORY`/`OFF`
**Possible Values:** [`WAL` on native installs]/[`DELETE` in Docker]/`TRUNCATE`/`PERSIST`/`MEMORY`/`OFF`
SQLite [journal mode](https://www.sqlite.org/pragma.html#pragma_journal_mode), applied via `PRAGMA journal_mode = ...` on every new connection. Settable as `ARCHIVEBOX_SQLITE_JOURNAL_MODE`.
The default `WAL` (Write-Ahead Logging) lets readers and a single writer operate concurrently without blocking each other — readers see a stable snapshot while a write is in progress, instead of being serialized behind it. This is a substantial win for ArchiveBox, where the web UI, admin, and CLI workers frequently read the index while an extractor is writing.
The default is `WAL` (Write-Ahead Logging) on native installs and `DELETE` in Docker. WAL lets readers and a single writer operate concurrently without blocking each other, but its shared-memory locking is unsafe when a Docker bind mount exposes the same live database to host-side SQLite processes. Docker therefore rejects an explicit WAL override for the SQLite backend; use PostgreSQL for safe cross-runtime concurrency.
> [!WARNING]
> Do not change this unless you have a specific reason. `DELETE` and `TRUNCATE` serialize all readers against any writer (much worse concurrency). `MEMORY` and `OFF` disable durable journaling and can corrupt the database on crash or power loss. `WAL` requires the database to live on a real local filesystem — it does not work correctly over network filesystems like NFS or SMB.
> Do not change this unless you have a specific reason. `DELETE` and `TRUNCATE` serialize readers against writers. `MEMORY` and `OFF` disable durable journaling and can corrupt the database on crash or power loss. WAL requires one local filesystem and one locking domain; it is unsafe across network filesystems and Docker host bind mounts.
---
#### `SQLITE_MMAP_SIZE`