Store backend support tiers¶
PyCharter's contract store runs on five backends behind one client interface: PostgreSQL, SQLite, MongoDB, Redis, and in-memory. The client API is identical across all five — every store method works everywhere. What differs is the upgrade story: only the SQL backends have a managed migration path when a PyCharter release reshapes its storage layout.
This page states that difference plainly so you can pick a backend with eyes open.
The tiers¶
| Backend | Tier | Managed migrations | Recommended for |
|---|---|---|---|
| PostgreSQL | Production | Alembic (pycharter db upgrade, auto-run on API startup) |
Production deployments |
| SQLite | Production | Alembic (same migration chain as Postgres) | Single-node production, CI, local work |
| MongoDB | Supported | None — manual handling across upgrades | Existing Mongo estates that accept manual upgrade steps |
| Redis | Dev / cache | None | Ephemeral or cache-style usage |
| In-memory | Dev / test | n/a (process-lifetime only) | Tests, prototyping |
What "no managed migrations" means¶
Alembic migrations live under src/pycharter/db/migrations/versions/
and reshape SQL tables only. When a release changes how contracts
are persisted:
- Postgres / SQLite — run
pycharter db upgrade(the REST API also runs it automatically at startup). Done. - MongoDB / Redis — there is no automated path. The changelog flags storage-affecting changes; apply them manually, or use the export/re-seed route below.
Document shape changes are a separate, backend-independent mechanism
— each persisted artifact carries a format_version header validated
at read time on every backend. See
Artifact Format Versioning. The gap
described here is only about storage layout (collections, key
patterns, indexes).
Upgrading a MongoDB or Redis store¶
The reliable route is a store-to-store copy through the client API — contracts are versioned documents, so reading them out of one backend and writing them into another is a complete, portable round trip:
from pycharter.contract_store import (
MongoDBContractStore,
PostgresContractStore,
)
source = MongoDBContractStore("mongodb://localhost:27017/pycharter")
target = PostgresContractStore("postgresql://localhost/pycharter")
source.connect()
target.connect()
for ref in source.list_contracts():
name, version = ref["name"], ref["version"]
contract = source.get_contract(name, version)
target.store_json_schema(name, version, contract["json_schema"])
if contract.get("coercion_rules"):
target.store_coercion_rules(name, version, contract["coercion_rules"])
if contract.get("validation_rules"):
target.store_validation_rules(name, version, contract["validation_rules"])
if contract.get("metadata"):
target.store_metadata(name, version, contract["metadata"])
Run the copy before upgrading (old store → SQL store), or after (SQL store → re-created non-SQL store) — the client API is the same on both sides.
Picking a backend¶
- Starting fresh in production? PostgreSQL.
- Single node, simple ops? SQLite — it shares the Postgres migration chain and upgrades the same way.
- Already standardised on MongoDB? Supported — budget for manual steps on storage-affecting releases (rare; always changelog-flagged).
- Redis and in-memory are deliberately positioned for cache/dev/test use, not as systems of record.