mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-06-11 01:14:45 +02:00
feat(v0.6-2): dispatcher state persistence -- cold-start, cooldowns, dedup LRU to SQLite
Closes Rule-20 dispatcher gap from audit doc v0.6-phase1-audit.md finding #1. Pre-this-commit the cold-start anchor, 4 drop counters, per-toggle cooldown map, and dedup OrderedDict all lived in Dispatcher instance memory and were lost on every container restart. v5.sql adds three tables: - dispatcher_state (singleton id=1): cold_start_anchor + 4 drop counters - dispatcher_cooldowns ((toggle,category,region) keyed): last_fired_at - dispatcher_dedup ((source,event_id) keyed): seen_at Dispatcher refactor: - __init__ calls _restore_from_db -- counters, cold-start anchor, cooldown map, and dedup LRU (most-recent 10k by seen_at) all rehydrated from the three new tables - write-through on every mutation: _persist_state for counter/anchor, _persist_cooldown for cooldown UPSERT + 2*cooldown_s prune, _persist_dedup for dedup INSERT OR REPLACE + 7-day cleanup - in-memory caches stay authoritative on the fast read path - cumulative-since-install counters (NOT since-boot); LLM will be able to answer "we have dropped 47 stale events this week" after commit #5 (env_reporter) lands - graceful degrade: missing v5 tables / persistence outage falls back to fresh in-memory state without crashing the constructor Tests: - tests/test_dispatcher_persistence.py (17 tests): state restore on init, counter+cooldown+dedup survival across simulated restart, cooldown rearm within 2x window, dedup LRU rebuild caps at 10k, 7-day cleanup on insert, INSERT OR REPLACE on duplicate source+event_id, v5 migration idempotent, synthetic storm (50 events) -> restart -> replay (5 incl 1 duplicate) with the duplicate dedup-rejected and counters NOT resetting - tests/conftest.py (new): autouse MESHAI_DB_PATH redirection to per-test tmp file, so the dispatcher_* tables on production /data dont get polluted by tests that construct Dispatcher() without an explicit fixture - tests/test_notification_toggles.py: _dispatch helper wipes dedup/cooldown/ state tables between calls (per-call independence preserved; pre-v0.6-2 in-memory-only Dispatcher reset naturally per instance) Test count: 680 -> 697 (+17 new, 0 regressions). Refs audit doc v0.6-phase1-audit.md finding #1.
This commit is contained in:
parent
b2c4d53b14
commit
c333a97344
6 changed files with 815 additions and 7 deletions
|
|
@ -24,6 +24,23 @@ class RecChannel:
|
|||
|
||||
def _dispatch(cfg, event):
|
||||
rec = []
|
||||
# v0.6-2: wipe dedup state between calls so each _dispatch is an
|
||||
# independent "what happens if this event arrives now?" check.
|
||||
# The pre-v0.6-2 in-memory dedup naturally reset per Dispatcher
|
||||
# instance; the new persisted dedup carries across instances unless
|
||||
# we clear it here.
|
||||
try:
|
||||
from meshai.persistence import get_db
|
||||
conn = get_db()
|
||||
conn.execute("DELETE FROM dispatcher_dedup")
|
||||
conn.execute("DELETE FROM dispatcher_cooldowns")
|
||||
conn.execute(
|
||||
"UPDATE dispatcher_state SET cold_start_anchor=NULL, "
|
||||
"stale_dropped=0, cooldown_dropped=0, dedup_dropped=0, "
|
||||
"cold_start_dropped=0 WHERE id=1"
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
d = Dispatcher(cfg, lambda rule, conn: RecChannel(rec), connector=None)
|
||||
asyncio.run(d.dispatch(event))
|
||||
return rec
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue