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
31
tests/conftest.py
Normal file
31
tests/conftest.py
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
"""Pytest fixture isolation for meshai persistence (v0.6-2).
|
||||
|
||||
Before v0.6-2 the dispatcher held all state in instance memory, so tests
|
||||
that constructed `Dispatcher(...)` were inert w.r.t. SQLite. v0.6-2 made
|
||||
`Dispatcher.__init__` read/restore from the persistence layer, which by
|
||||
default points at `/data/meshai.sqlite`. Without isolation every test
|
||||
would now read+write production state, polluting across tests and across
|
||||
pytest invocations.
|
||||
|
||||
This autouse fixture redirects `MESHAI_DB_PATH` to a per-test tmp file
|
||||
and clears the persistence-layer threading.local caches around each test.
|
||||
Existing tests that don't reference any fixture get isolation for free;
|
||||
tests that explicitly use a `db_path` (or similar) fixture can still
|
||||
override the env var inside their own fixture body -- last setenv wins.
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from meshai.persistence import close_thread_connection
|
||||
from meshai.persistence import db as _persistence_db
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _isolate_meshai_db(tmp_path, monkeypatch):
|
||||
"""Point MESHAI_DB_PATH at a tmp file per test."""
|
||||
p = str(tmp_path / "meshai-test-isolated.sqlite")
|
||||
monkeypatch.setenv("MESHAI_DB_PATH", p)
|
||||
_persistence_db._initialised.clear()
|
||||
close_thread_connection()
|
||||
yield p
|
||||
close_thread_connection()
|
||||
_persistence_db._initialised.discard(p)
|
||||
Loading…
Add table
Add a link
Reference in a new issue