Storage¶
Connexions uses a layered storage architecture that provides service isolation while sharing a single backend connection.
Design Principles¶
Shared Backend, Isolated Views¶
All services share a single storage backend (memory or Redis), but each service gets an isolated "view" into that storage through key prefixing. This means:
- Single connection - One Redis client or memory store for the entire application
- No cross-service access - Service A cannot read or modify Service B's data
- Automatic namespacing - Keys are prefixed with the service name transparently
Lazy Resource Creation¶
Tables and history stores are created on first access, not upfront. This keeps memory usage low when services don't use all features.
TTL Support¶
Records can have individual expiration times:
- Per-record TTL - Each
Set()call can specify its own TTL - Zero means forever - A TTL of
0means the record never expires - Lazy expiration (memory) - Expired records are deleted on access, not via background cleanup
- Native TTL (Redis) - Uses Redis's built-in key expiration
Architecture¶
┌─────────────────────────────────────────────────────────┐
│ Application │
├─────────────────────────────────────────────────────────┤
│ Service A Service B Service C │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ DB │ │ DB │ │ DB │ │
│ ├─────────┤ ├─────────┤ ├─────────┤ │
│ │ History │ │ History │ │ History │ │
│ │ Table() │ │ Table() │ │ Table() │ │
│ └────┬────┘ └────┬────┘ └────┬────┘ │
│ │ │ │ │
├───────┴───────────────────┴───────────────────┴─────────┤
│ Storage │
│ (Memory or Redis backend) │
│ │
│ Keys: "serviceA:history:GET:/users" │
│ "serviceB:cache:token123" │
│ "serviceC:history:POST:/orders" │
└─────────────────────────────────────────────────────────┘
Components¶
Storage¶
The shared backend that manages the actual data. Supports:
- Memory - In-process storage, data lost on restart
- Redis - Distributed storage, persists across restarts
DB¶
A service-scoped wrapper that provides:
- History() - Typed access to request/response history
- Table(name) - Generic key-value storage with TTL support
- CircuitBreakerStore() - Shared circuit breaker state (see below)
Table¶
Generic key-value store with per-record TTL:
Get(ctx, key)- Retrieve a value (returns false if expired or missing)Set(ctx, key, value, ttl)- Store with optional expirationDelete(ctx, key)- Remove a keyData(ctx)- Get all non-expired entriesClear(ctx)- Remove all entries
HistoryTable¶
Typed wrapper for request/response tracking:
- Stores request body, response data, status codes
- Used by caching and upstream middleware
- Cleared periodically based on
historyDurationconfig
Circuit Breaker Store¶
Each service has its own circuit breaker, keyed by its upstream URL. The store provides:
- Memory - Circuit breaker state is local to the process
- Redis - Circuit breaker state is shared across all application instances
When running multiple instances of connexions behind a load balancer, Redis storage ensures that circuit breaker state (failure counts, open/closed state) is synchronized across all instances. This prevents one instance from continuing to hit a failing upstream while another has already tripped the breaker.
Choosing a Backend¶
| Feature | Memory | Redis |
|---|---|---|
| Setup | None | Requires Redis server |
| Persistence | No (lost on restart) | Yes |
| Multi-instance | No (each instance isolated) | Yes (shared state) |
| Circuit breaker sharing | Within process only | Across all instances |
| Performance | Fastest | Network overhead |
Use Memory when: - Running a single instance - Data loss on restart is acceptable - Simplicity is preferred
Use Redis when: - Running multiple instances behind a load balancer - Circuit breaker state must be shared - Request history should persist across restarts
Configuration¶
See App Configuration for setup options.
# Memory (default)
storage:
type: memory
# Redis
storage:
type: redis
redis:
address: localhost:6379
password: ""
db: 0