Add scheduled refresh for tables that change at a fixed time of day

This commit is contained in:
2026-07-30 11:28:20 +02:00
parent 4a86b2282f
commit 0026a4df22
12 changed files with 652 additions and 66 deletions
+2 -1
View File
@@ -225,7 +225,8 @@ SQLMEM_DEBUG=true # DEBUG level — podrobný výpis každého dotazu, cache o
- [x] **`vacuum(incremental=True)` varuje bez `auto_vacuum=INCREMENTAL`**: dřív tichý no-op; teď zaloguje warning (a jak to opravit) a vrátí se.
- [x] **`Stats.db_size_bytes`**: velikost cache souboru na disku (0 v memory módu) ve `stats` pro monitoring.
- [x] **Ochrana proti cache stampede**: `load_table` dělá double-checked locking — po získání `_load_lock` znovu ověří, zda tabulku mezitím nenahrál souběžný loader (cached + sloupce + ne-stale), a redundantní reload přeskočí. Bez toho druhý dotaz během studeného loadu velké tabulky spustil druhý plný reload (212M řádků = +2 h).
- [x] **Deklarativní inicializace (`tables=[TableSpec(...)]`)**: předem se deklaruje každá tabulka (`TableSpec(name, columns, indexes, refresh=Delta(...)|TTL(...), datetime_columns, preload)`). `preload=True` se na pozadí (nebo blokujícně) přednahraje při startu; co je v persistentní cache čerstvé, se přeskočí (warm restart = instant). Nedeklarovaná tabulka/sloupec (i `SELECT *` na sloupcově omezené tabulce) → `UndeclaredError` (fail-fast) místo tichého líného loadu; `columns=None` = celá tabulka + libovolný sloupec. Plně zpětně kompatibilní: bez `tables=` fungují staré `delta=/ttl=/indexes=/datetime_columns=` jako dřív (kombinace obojího → `ValueError`).
- [x] **Deklarativní inicializace (`tables=[TableSpec(...)]`)**: předem se deklaruje každá tabulka (`TableSpec(name, columns, indexes, refresh=Delta(...)|TTL(...)|Schedule(...), datetime_columns, preload)`). `preload=True` se na pozadí (nebo blokujícně) přednahraje při startu; co je v persistentní cache čerstvé, se přeskočí (warm restart = instant). Nedeklarovaná tabulka/sloupec (i `SELECT *` na sloupcově omezené tabulce) → `UndeclaredError` (fail-fast) místo tichého líného loadu; `columns=None` = celá tabulka + libovolný sloupec. Plně zpětně kompatibilní: bez `tables=` fungují staré `delta=/ttl=/schedule=/indexes=/datetime_columns=` jako dřív (kombinace obojího → `ValueError`).
- [x] **Plánovaný refresh v konkrétní čas (`schedule=`)**: `schedule={"VW_X": "03:00"}` (nebo seznam časů / `datetime.time`) — full reload v zadané časy dne v lokální timezone, pro tabulky měněné dávkou v pevnou hodinu (noční batch, ranní import). Validace (neplatný/tz-aware čas → `ValueError`) proběhne hned při konstrukci. Tabulka je „due", pokud se od posledního plánovaného slotu nenačetla (`is_due` porovnává proti `last_refresh_at`), takže zmeškaný slot se dohoní po startu a jeden slot nikdy nespustí reload dvakrát; read-time garance jako u TTL. Background thread si zkrátí čekání (`_next_tick`), aby slot spustil do ~sekundy. Dostupné i jako `TableSpec.refresh=Schedule(...)` v deklarativním módu. Tabulka má vždy jen jednu metodu obnovy — `delta` / `ttl` / `schedule`, jinak `ValueError` (`_check_one_method_per_table`).
## TODO — budoucí funkce