ADR-0002: Use 5 specialized databases instead of one general-purpose store
- Status: Accepted
- Date: 2026-06-21
- Decider: @cryptorugmunch
Context
RMI stores data across multiple categories:
| Category | Volume | Access pattern | Query shape |
|---|---|---|---|
| User data, wallets, alerts (relational) | 252K rows | CRUD + joins | SQL |
| Cache, rate limits, labels (key-value) | 82K keys | O(1) get/set | key lookup |
| Analytics, cost tracking, legacy logs (columnar) | 83K rows (will grow) | Aggregate scans | SQL OLAP |
| Wallets, tokens, transfers (graph) | 20K nodes | Multi-hop traversal | Cypher |
| Embeddings, semantic search (vector) | 75 vectors | k-NN similarity | HNSW |
A single Postgres would not serve all five patterns efficiently:
- Graph queries in Postgres require recursive CTEs that scale O(nΒ²)
- Vector search in Postgres uses pgvector but is 10Γ slower than Qdrant
- OLAP scans in Postgres lock transactional tables
- Cache in Postgres requires manual eviction policies
Decision
Use five specialized databases:
- Postgres 16 β source of truth (relational data)
- Redis 7.2 β cache, rate limits, queues
- ClickHouse β OLAP, cost tracking, deprecation logs
- Neo4j 5 β graph queries (cross-chain wallet flows)
- Qdrant β vector search (RAG semantic retrieval)
Plus DuckDB for local analytics on the laptop (single-file, no server).
Alternatives Considered
- All Postgres (with extensions): pgvector + recursive CTEs + pg_partman. Rejected β graph perf unacceptable, OLAP competes with transactional load.
- Single MongoDB: Rejected β weak analytics, no vector search, no graph.
- DynamoDB + S3 + Neptune: Rejected β vendor lock-in, high cost, no vector.
- Drop Neo4j, use ClickHouse: Possible. Rejected for now β graph queries on 84K nodes are O(seconds), not O(minutes).
Consequences
- Positive: Each DB does one job well. Optimized for its access pattern. Can scale independently.
- Negative:
- 5 things to back up, monitor, patch, version
- New engineer onboarding: must learn 5 DBs
- Cross-DB queries require application-level joins
- 5 different query languages (SQL, Redis commands, SQL/CH, Cypher, REST)
- Mitigations:
- DataBus facade unifies access (
app/databus/core.py) - Container memory limits set on all 5 DB containers
- Each DB has a runbook in
docs/runbooks/
- DataBus facade unifies access (
Re-evaluation triggers
- Neo4j license change (currently community edition, free)
- Postgres + pgvector catching up to Qdrant perf (current gap: ~10Γ)
- ClickHouse memory pressure requiring Memgraph substitution