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
+20
View File
@@ -6,6 +6,26 @@ All notable changes to this project will be documented in this file.
---
## [1.17.0] - 2026-07-30
### Added
- **Scheduled refresh at a fixed time of day** — `CachingEngine(engine, schedule={"VW_X": "03:00", "VW_Y": ["06:30", "14:30"]})` fully reloads a table at the times of day you name (`"HH:MM"` / `"HH:MM:SS"` strings or `datetime.time`, one or a list), for tables that change on a clock (nightly batch, morning import) rather than continuously or via a change column. Times are interpreted in the local timezone; an invalid or timezone-aware time raises `ValueError` at construction.
- **Slot-based, not timer-based** — a table is due when it hasn't been reloaded since its most recent scheduled moment (judged against the persisted `last_refresh_at`), so a slot missed while the process was down is caught up on the first refresh check after start, and a slot never fires twice.
- The background thread **shortens its tick** to land within a second of each slot instead of waiting up to `SQLMEM_REFRESH_INTERVAL`.
- **Read-time guarantee**, same as TTL: a query touching a table whose slot has passed reloads it before answering.
- Also available as a **`TableSpec.refresh=Schedule(...)`** strategy in declarative mode, alongside `Delta`/`TTL`; `Schedule` validates its times eagerly at construction.
- `stats` reports `tracking="schedule"`, and `state="stale"` once a slot has passed.
- New public type `Schedule`; `schedule.py` module with the parsing/normalization (`parse_schedule`) and slot maths (`previous_occurrence`, `next_occurrence`, `seconds_until_next`, `is_due`) it and the engine share.
- `CacheManager.last_refresh_at(table)` — the load timestamp as a `datetime` (`seconds_since_refresh` now derives from it).
### Changed
- `pyproject.toml` — bumped version to `1.17.0`.
- **Refresh methods are mutually exclusive** — a table listed under any two of `delta`, `ttl`, `schedule` raises `ValueError` (was: only the `delta`/`ttl` pair). Per `TableSpec` this was already structurally impossible (one `refresh` field).
- `QueryExecutor`'s TTL staleness check (`_ttl_expired`) is generalized to `_stale_reason`, covering TTL and schedule expiry identically wherever staleness is checked — read-time reload, and the double-checked-locking `satisfied` predicates.
- `CachingEngine._refresh_ttl`/new `_refresh_scheduled` share a `_reload` helper.
---
## [1.16.0] - 2026-06-11
### Added