rmqtt-storage
Is a simple wrapper around some key-value storages.
Usage
Add this to your Cargo.toml:
[]
= "0.11"
Features
- KV operations: Insert, get, remove, contains_key, batch insert/remove
- Map data type: Named key-value maps with iter, prefix_iter, key_iter, clear, remove_with_prefix, remove_and_fetch, batch ops, and TTL support
- List data type: Ordered lists with push, pop, pushs, push_limit, get_index, all, iter, clear, and TTL support
- Counters: Atomic increment/decrement/set/get for isize values
- Key expiration (
ttlfeature): Per-key and per-collection TTL with background cleanup - Storage backends:
- sled: Embedded database (BTreeMap-like). Supports all optional features (
ttl,len,map_len). - redis: Single-node Redis backend. Supports all optional features.
- redis cluster: Distributed Redis cluster. Note: the
lenfeature is not supported yet. - redb: Embedded ACID transactional B-Tree. Supports all optional features (
ttl,len,map_len).
- sled: Embedded database (BTreeMap-like). Supports all optional features (
- Binary serialization via
postcard— fast, compact - Asynchronous API with command-channel architecture for thread-safe operations
- Circuit Breaker (optional): Protects all storage backends (sled, Redis, Redis Cluster, Redb) from cascading failures using
tower-resilience-circuitbreaker. Configure failure thresholds, sliding windows, half-open recovery, and per-call operation timeouts.
Feature Flags
| Feature | Description | Default |
|---|---|---|
sled |
Sled embedded database backend | no |
redb |
Redb embedded ACID transactional backend | no |
redis |
Single-node Redis backend | no |
redis-cluster |
Redis Cluster distributed backend | no |
ttl |
Key expiration / TTL support | no |
len |
Storage item count (len()) |
no |
map_len |
Map item count (map.len()) |
no |
circuit-breaker |
Circuit breaker protection (requires a storage backend) | no |
Circuit Breaker Usage
Enable the feature and wrap your storage with CircuitBrokenDB:
[]
= { = "0.11", = ["sled", "circuit-breaker"] }
use ;
use init_db;
let inner = init_db.await?;
let db = new;
// All existing APIs work transparently through the circuit breaker
let val: = db.get.await?;
let map = db.map.await?;
map.insert.await?;
CircuitBrokenDB implements the [StorageDB] trait, and CircuitBrokenMap/CircuitBrokenList implement the [Map]/[List] traits respectively. This means they can be used polymorphically alongside native storage backends in generic contexts.
All Map / List / DB operations funnel through a single shared circuit breaker — a failing backend trips the breaker globally, and failure metrics are unified across all operation types.
When the failure rate exceeds the configured threshold, the circuit opens and subsequent calls fail fast instead of waiting for timeouts. After a configurable recovery period, the circuit transitions to half-open to test if the backend has recovered.
Operation Timeout
Set operation_timeout to abort calls that take longer than the specified duration. Timed-out calls are automatically counted as failures, triggering the circuit breaker:
use Duration;
use CircuitBreakerConfig;
let config = CircuitBreakerConfig ;
Examples
The examples/session_storage_demo.rs simulates MQTT Broker session management, demonstrating all three core data structures:
- DB (KV): Client session info + counters
- Map: Topic subscription management
- List: Offline message queue
Run with the sled backend (default):
Run with the redb backend:
Changelog
0.9.0
- Serialization: Migrated from
bincodetopostcard— faster, smaller encoded output - Refactor: Removed sled transaction dependency, replaced with direct tree operations for better single-threaded throughput
- Testing: Added
serial_testto prevent sled I/O contention in parallel test runs
0.10.0
- Circuit Breaker: New
circuit-breakerfeature — wrapsDefaultStorageDB/StorageMap/StorageListwith per-instance fault tolerance viatower-resilience-circuitbreaker0.10- Sliding window failure rate detection (default: 50% over 20 calls)
- Half-Open automatic recovery (default: 30 s wait)
- Slow call detection (default: 2 s threshold)
- Operation timeout (
operation_timeout): abort hung calls and count as failures - State transition logging
- Independent breakers per DB / Map / List instance
- Trait implementation:
CircuitBrokenDBnow implementsStorageDBtrait,CircuitBrokenMapimplementsMaptrait,CircuitBrokenListimplementsListtrait — enabling polymorphic dispatch alongside native storage backends - Iterator methods (
map_iter,list_iter,scan,iter,key_iter,prefix_iter) bypass the circuit breaker for safe pass-through - Zero impact when feature is disabled
0.10.1
- Internal
_rawmethods: Added_insert_raw,_batch_insert_raw,remove_and_fetch_rawto Redis and Redis Cluster backends — bypasspostcardser/de forcircuit-breakerinternal useinsert_raw/batch_insert_rawrefactored to delegate to their_rawcounterpartsremove_and_fetch_raw— new raw-bytes variant ofremove_and_fetch
- CB optimization:
CBMapRequest::RemoveAndFetchnow uses a singleremove_and_fetch_rawcall instead ofget_raw+remove(fewer round trips, atomic) - Bug fix: Redis
_batch_insertnow guards against empty input to avoidMSETwith zero arguments - Tests: Added 7 new test cases —
push_limit,map_remove/list_remove(DB level), empty list pop/get_index, empty mapremove_and_fetch, large value (100KB), emptybatch_insert/batch_remove - Cleanup: Removed hanging CB integration tests (redundant with lib.rs
test_init_dbcoverage); fixedinit_dbreturn type and feature-gated imports inlib.rs
0.10.2
- Unified breaker: Map / List / DB now share a single
CircuitBreakerinstance instead of having independent breakers — one failure metric for all operation types, simpler concurrency model- Removed outer
Mutex— switched to clone-per-callArc<CircuitBreaker>for true concurrent access CbTimeoutWrapper<S>— Tower service layer that injects timeouts inside the breaker chain so timeouts are counted as failures byDefaultClassifier- Removed
RecordResulthack and three-stepraw_call— all three types now use a singlesvc.call(req) - Removed
CBMapRequest/CBListRequest— Map/List operations unified intoCBStorageRequestwith inlineStorageMap/StorageListhandles - Removed
build_map_cb/build_list_cb— Map/List share the DB-level breaker directly
- Removed outer
0.11.0 (current)
- Redb embedded backend (new feature
redb): Full ACID transactional storage backend built onredb4.1.0 — embedded B-Tree with MVCC, WAL, and fsync-guaranteed durability- Same command-channel +
spawn_blockingasync architecture as sled - 5 typed
TableDefinition<&[u8], &[u8]>tables (KV_TABLE,MAP_TABLE,LIST_TABLE,EXPIRE_KEYS_TABLE,KEY_EXPIRE_TABLE) - Every write operation wrapped in
begin_write()+commit()for ACID guarantees - Read operations use
begin_read()for snapshot isolation - Snapshot-collection iterators (eager
collect::<Vec>()in read transaction) - Full feature support:
ttl,len,map_len,circuit-breaker - Configurable cache size (default: 1GB) via
RedbConfig - Single-file storage — deployment-friendly, no external processes
- Same command-channel +
- Test coverage (P0/P1/P2): 6 new tests covering previously untested
StorageDB::info(), empty container iterators,remove_with_prefixedge cases, complex type ser/de through all 3 storage types, TTL on created Map/List, and scan with no match - 6-dimension test expansion: 23 additional tests from 6 angles — cross-feature interaction (TTL+batch, TTL+counter, map_iter+modify, list state chain), concurrency (counter atomicity, concurrent read/write, map_iter consistency), data integrity (overwrite, clear+insert, counter sequence including isize::MAX/MIN, batch duplicate keys, remove_and_fetch idempotence), edge cases (empty/Unicode/binary keys, 1MB values, special characters in names, limit=0 push_limit, map/list name conflict), error handling (get/remove non-existent, double remove), dispatch correctness (name roundtrip for Map/List)
- Total test count: 37 → 65 (redb) / 65+ (sled with circuit-breaker)
- Cross-backend compatibility: Fixed
test_list_push_limit_zerodivergence between sled and redb; fixedtest_map_iter_while_modifyingcleanup approach; fixedtest_name_with_special_charsname conflict - Bug fix:
exec_map_is_expiredandexec_list_is_expiredinstorage_redb.rs— fixed compilation error when using_dbparameter name withttlfeature (compilation failed witherror[E0425]: cannot find value db)