Skip to content

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-server deployments
  • Share state between multiple server instances
  • Use a query engine (DuckDB) for SQL-style services like Athena

Services with pluggable backends

ServiceBackend traitRedis crateNotes
SQSSqsBackendwinterbaume-sqs-redisMessage persistence and visibility timeouts
SNSSnsBackendIn-memory only currently
DynamoDBDynamoDbBackendwinterbaume-dynamodb-redisFull item storage
AthenaQueryExecutionBackendDuckDB crate for real SQL execution
Redshift DataQueryExecutionBackendSame DuckDB crate

Injecting a Redis backend

toml
[dependencies]
winterbaume-sqs-redis = { path = "..." }
rust
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

toml
[dependencies]
winterbaume-sqlengine-duckdb = { path = "..." }
rust
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.

Released under the Apache-2.0 License.