fix(state): guest durability barriers also apply configured database.synchronous

apply_durability_barriers() is the guest-connection entry point for
secondary state.db users (async delegation ledger) that must not run
journal-mode setup. database.synchronous (#90892) rides on the
journal-mode/pragma paths guests now skip, so apply it here directly —
otherwise guest connections silently run at the compile-time default.

Follow-up to the #93012 salvage.
This commit is contained in:
Teknium
2026-08-27 13:10:16 -07:00
parent 7e6eda7bbb
commit 4882184e95
2 changed files with 85 additions and 2 deletions

View File

@@ -2882,9 +2882,24 @@ def apply_durability_barriers(conn: sqlite3.Connection) -> bool:
This is the public entry point for secondary users of ``state.db`` that
must inherit its owner's journal mode while retaining per-connection
durability settings.
durability settings. Also applies the configured ``database.synchronous``
level (a per-connection pragma that would otherwise only ride on the
journal-mode setup path guest connections must not run).
"""
return _reapply_durability_barriers(conn)
ok = _reapply_durability_barriers(conn)
try:
# Local import avoids a circular import with hermes_cli.config.
from hermes_cli.config import cfg_get, load_config_readonly
cfg = load_config_readonly()
raw_synchronous = cfg_get(cfg, "database", "synchronous", default=None)
if raw_synchronous is not None:
_apply_synchronous_pragma(
conn, raw_synchronous, db_label="state.db (guest)"
)
except Exception:
pass
return ok
@contextmanager

View File

@@ -0,0 +1,68 @@
"""Guest ledger connections must inherit the configured database.synchronous.
apply_durability_barriers() is the guest-connection entry point (secondary
state.db users that must NOT touch journal mode). The configured
``database.synchronous`` level normally rides on apply_database_pragmas()
during the owner's journal-mode setup — a path guests deliberately skip — so
the guest entry point applies it directly.
"""
import sqlite3
import pytest
import hermes_state
from hermes_state import apply_durability_barriers
def _config(monkeypatch, database_section):
import hermes_cli.config as config_mod
cfg = {"database": database_section}
monkeypatch.setattr(config_mod, "load_config_readonly", lambda *a, **k: cfg)
return cfg
def test_guest_barriers_apply_configured_synchronous(monkeypatch, tmp_path):
_config(monkeypatch, {"synchronous": "FULL"})
conn = sqlite3.connect(tmp_path / "state.db")
try:
conn.execute("PRAGMA journal_mode=DELETE")
conn.execute("PRAGMA synchronous=1")
apply_durability_barriers(conn)
assert conn.execute("PRAGMA synchronous").fetchone()[0] == 2
# And the journal mode was NOT touched — that is the whole contract.
assert (
str(conn.execute("PRAGMA journal_mode").fetchone()[0]).lower()
== "delete"
)
finally:
conn.close()
def test_guest_barriers_leave_synchronous_alone_when_unset(monkeypatch, tmp_path):
_config(monkeypatch, {})
conn = sqlite3.connect(tmp_path / "state.db")
try:
conn.execute("PRAGMA journal_mode=DELETE")
conn.execute("PRAGMA synchronous=1")
apply_durability_barriers(conn)
assert conn.execute("PRAGMA synchronous").fetchone()[0] == 1
finally:
conn.close()
def test_guest_barriers_survive_config_failure(monkeypatch, tmp_path):
import hermes_cli.config as config_mod
def _boom(*a, **k):
raise RuntimeError("config unavailable")
monkeypatch.setattr(config_mod, "load_config_readonly", _boom)
conn = sqlite3.connect(tmp_path / "state.db")
try:
conn.execute("PRAGMA journal_mode=DELETE")
# Must not raise; best-effort like every other pragma path.
apply_durability_barriers(conn)
finally:
conn.close()