Pluggable Backends
Several high-traffic services decouple their protocol handlers from persistence behind an object-safe async backend trait. The default backend is always in-memory; alternative implementations (Redis, DuckDB) live in separate crates and are injected at construction time.
Why pluggable backends?
The default in-memory backends work well for unit and integration tests where state does not need to survive process restarts. Pluggable backends let you:
- Persist state across server restarts in long-running
winterbaume-serverdeployments - Share state between multiple server instances
- Use a query engine (DuckDB) for SQL-style services like Athena
Services with pluggable backends
| Service | Backend trait | Redis crate | Notes |
|---|---|---|---|
| SQS | SqsBackend | winterbaume-sqs-redis | Message persistence and visibility timeouts |
| SNS | SnsBackend | — | In-memory only currently |
| DynamoDB | DynamoDbBackend | winterbaume-dynamodb-redis | Full item storage |
| Athena | QueryExecutionBackend | — | DuckDB crate for real SQL execution |
| Redshift Data | QueryExecutionBackend | — | Same DuckDB crate |
Injecting a Redis backend
[dependencies]
winterbaume-sqs-redis = { path = "..." }use winterbaume_sqs::SqsService;
use winterbaume_sqs_redis::RedisSqsBackend;
let backend = RedisSqsBackend::new("redis://127.0.0.1/").await.unwrap();
let sqs = SqsService::with_backend(backend);
let mock = MockAws::builder()
.with_service(sqs)
.build();Injecting a DuckDB query backend
[dependencies]
winterbaume-sqlengine-duckdb = { path = "..." }use winterbaume_athena::AthenaService;
use winterbaume_sqlengine_duckdb::DuckDbQueryBackend;
let query_backend = DuckDbQueryBackend::new().unwrap();
let athena = AthenaService::with_query_backend(query_backend);
let mock = MockAws::builder()
.with_service(athena)
.build();Snapshot, restore, and merge with external backends
For services that use pluggable backends, StatefulService::snapshot(), restore(), and merge() delegate through the backend rather than through an in-memory shadow. This means:
- Snapshotting a Redis-backed SQS service reads live queue state from Redis.
- Restoring a view onto a Redis-backed service writes that state back to Redis.
- If the backend is not running, these operations will fail.
Adding a new backend
Implement the service's backend trait (defined in crates/winterbaume-{service}/src/backend.rs) and construct the service with ServiceName::with_backend(your_backend). The trait is object-safe and all methods are async.