feat: add RedisStore, SQLiteStore, and wire store into Mpp.create()#98
feat: add RedisStore, SQLiteStore, and wire store into Mpp.create()#98brendanjryan wants to merge 5 commits intomainfrom
Conversation
- RedisStore: production-ready store using redis-py (SET NX EX for atomic put_if_absent, configurable TTL and key prefix) - SQLiteStore: zero-infra production store using aiosqlite (INSERT OR IGNORE for atomic put_if_absent, lazy TTL expiry) - Mpp.create(store=...) and Mpp(store=...) automatically inject the store into intents that have a _store attribute (e.g. ChargeIntent) - New optional deps: pympp[redis], pympp[sqlite] - 27 new tests covering all store backends and wiring behavior
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b9cf62d2fd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
src/mpp/stores/sqlite.py
Outdated
| """ | ||
| now = time.time() | ||
| await self._db.execute( | ||
| "DELETE FROM kv WHERE key = ? AND expires_at <= ?", (key, now) |
There was a problem hiding this comment.
Purge expired SQLite keys globally before inserts
put_if_absent only removes expired rows for the current key, but charge replay keys are transaction hashes and are almost always unique, so old rows are never revisited and never deleted. In production this makes TTL ineffective for storage reclamation, causing unbounded kv growth over time and eventual write/latency issues on long-lived servers. Expiry cleanup needs to target all expired rows (or run an equivalent global/batched vacuum path), not just the incoming key.
Useful? React with 👍 / 👎.
- Add redis service to docker-compose.yml - 10 integration tests: CRUD, TTL verification, atomicity of put_if_absent under concurrent access, key prefix isolation - Skipped automatically when REDIS_URL is not set - Run with: docker compose up -d redis && REDIS_URL=redis://localhost:6379 uv run pytest -m redis -v
Summary
Adds two production-ready store backends and wires the
store=parameter intoMpp.create()Changes
New store backends (
mpp.stores):RedisStore— for multi-instance production deployments (Redis/Valkey). UsesSET NX EXfor atomicput_if_absent. Install withpip install pympp[redis].SQLiteStore— zero-infra production store using Python's built-insqlite3viaaiosqlite. UsesINSERT OR IGNOREfor atomicput_if_absentwith lazy TTL expiry. Install withpip install pympp[sqlite].Store wiring:
Mpp.create(store=...)andMpp(store=...)automatically inject the store into intents that accept one (e.g.,ChargeIntent). If an intent already has a store configured, it is not overwritten.Optional dependencies in
pyproject.toml:pympp[redis]→redis>=5.0pympp[sqlite]→aiosqlite>=0.20Usage