5f695ebe2a
Eine readonly-Verbindung (web-Container) kann eine WAL-Datenbank ohne aktiven Writer/-shm nicht öffnen -> "attempt to write a readonly database", die Seite blieb leer. Da collector (Writer) und web (Reader) getrennte Prozesse sind und der collector keine Dauerverbindung hält, existiert im Ruhezustand kein -shm. - collector/db.py + schema.sql: WAL entfernt (Rollback-Journal als Default); readonly-Reads sind damit jederzeit möglich. - web/lib/db.ts: busy_timeout=5s, falls gerade geschrieben wird. Bestehende DB einmalig umstellen: PRAGMA journal_mode=DELETE; Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
54 lines
1.7 KiB
Python
54 lines
1.7 KiB
Python
"""SQLite-Helfer für den Collector (Schema-Init + Schreibzugriffe)."""
|
|
|
|
import os
|
|
import sqlite3
|
|
from datetime import datetime
|
|
from pathlib import Path
|
|
|
|
SCHEMA_PATH = Path(__file__).with_name("schema.sql")
|
|
|
|
|
|
def get_db_path() -> str:
|
|
return os.environ.get("DB_PATH", "/data/charges.db")
|
|
|
|
|
|
def connect() -> sqlite3.Connection:
|
|
path = get_db_path()
|
|
Path(path).parent.mkdir(parents=True, exist_ok=True)
|
|
conn = sqlite3.connect(path)
|
|
conn.row_factory = sqlite3.Row
|
|
# Bewusst KEIN WAL: Der web-Container liest die DB read-only aus einem
|
|
# anderen Prozess. Eine readonly-Verbindung kann eine WAL-Datenbank ohne
|
|
# aktiven Writer/-shm nicht öffnen ("attempt to write a readonly database").
|
|
# Rollback-Journal (Default) lässt readonly-Reads jederzeit zu.
|
|
return conn
|
|
|
|
|
|
def init_db() -> None:
|
|
"""Legt Schema idempotent an und setzt install_date beim ersten Start."""
|
|
schema = SCHEMA_PATH.read_text(encoding="utf-8")
|
|
conn = connect()
|
|
try:
|
|
conn.executescript(schema)
|
|
conn.execute(
|
|
"INSERT OR IGNORE INTO meta (key, value) VALUES ('install_date', ?)",
|
|
(datetime.now().isoformat(timespec="seconds"),),
|
|
)
|
|
conn.commit()
|
|
finally:
|
|
conn.close()
|
|
|
|
|
|
def insert_charge(start_time: str, end_time: str, duration_s: int, energy_wh: float) -> None:
|
|
conn = connect()
|
|
try:
|
|
conn.execute(
|
|
"""INSERT INTO charges (start_time, end_time, duration_s, energy_wh, created_at)
|
|
VALUES (?, ?, ?, ?, ?)""",
|
|
(start_time, end_time, duration_s, energy_wh,
|
|
datetime.now().isoformat(timespec="seconds")),
|
|
)
|
|
conn.commit()
|
|
finally:
|
|
conn.close()
|