HydraCache
HydraCache is a Rust-native local async cache that is designed to grow toward database result caching and distributed synchronization later.
Status
HydraCache is in early development. The current implementation provides the
local async cache runtime plus the first database result-cache adapters:
hydracache-db and hydracache-sqlx.
Why HydraCache?
HydraCache is not trying to replace low-level cache engines, databases, or query processors. It is an application-facing cache layer for Rust services.
Compared with using Moka directly, HydraCache adds a smaller product-shaped API: loader helpers, TTLs, tag invalidation, local single-flight, codec-backed storage, and lightweight stats in one place.
Compared with ORM-level caches, HydraCache keeps freshness explicit. Keys, tags, and invalidation are application-controlled instead of hidden behind a large persistence framework.
Compared with Redis-style caches, HydraCache is embedded and local-first. The first version needs no server, proxy, daemon, or network hop.
Compared with ReadySet or Noria-style query engines, HydraCache deliberately does not try to incrementally maintain SQL result graphs. It is a lightweight cache library first, with database-result caching planned as an adapter layer.
The long-term direction is:
simple local cache -> database result-cache adapter -> optional distributed synchronization
v0 Scope
The first version includes:
- local async cache runtime
HydraCache::local()buildergetputget_or_loadget_or_insert_withtry_get_or_insert_withTypedCache<T>namespaced typed viewCacheKeyBuilderfor escaped segmented keysTagSetfor reusable invalidation tag groups- local single-flight miss deduplication
contains_key- per-entry TTL and default TTL
- tag-aware invalidation
- key invalidation
removeas a local-cache alias for key invalidationflushpostcardcodec overBytes- lightweight stats
- single-flight join stats
- tag-generation invalidation safety
- Moka-backed local storage
- database-neutral query result-cache descriptors
- SQLx helper methods:
fetch_one,fetch_optional, andfetch_all
Out of scope for v0:
- proc macros
- distributed invalidation
- cluster roles
- public generation-counter APIs
- persistence
Example
use Duration;
use ;
use ;
async
# async
API Notes
get returns Ok(None) when the key is missing or expired.
get_or_load runs the loader on a miss and stores the loaded value with the provided CacheOptions.
get_or_insert_with is the short local-cache spelling for infallible async loaders.
try_get_or_insert_with is the fallible-loader spelling. It behaves the same as get_or_load.
typed::<T>("namespace") creates a typed, namespaced view over the same cache. It
keeps the shared storage, stats, single-flight, tags, and invalidation safety,
but removes repeated type annotations at call sites and prefixes keys as
namespace:key.
CacheKeyBuilder builds escaped :-separated keys from segments. TagSet
collects reusable invalidation tags and can be attached with
CacheOptions::tag_set.
Concurrent get_or_load calls for the same missing key share one loader execution. Cache hits bypass single-flight entirely.
If a tag is invalidated while a tagged loader is still running, HydraCache skips storing that stale loader result. Callers after the invalidation start or join a fresh in-flight load instead of joining the stale one.
contains_key checks whether a key currently maps to a usable value. Expired entries are removed and reported as absent.
remove and invalidate_key both remove one key. remove is the shorter local-cache spelling; invalidate_key is kept for consistency with tag invalidation.
invalidate_tag removes all entries currently associated with the tag.
Use CacheOptions::tag("users") for one tag and CacheOptions::tags(["users", "user:42"]) for multiple tags.
stats returns lightweight counters for hits, misses, loads, single-flight joins, stale load discards, invalidations, and evictions. v0 does not wire backend eviction listeners yet, so evictions remains zero.
SQLx Adapter
hydracache-db provides the database-neutral result-cache adapter API. It keeps
your database client responsible for pools, transactions, queries, and row
mapping, while HydraCache owns the explicit cache boundary: key, tags, TTL,
single-flight, and storage.
hydracache-sqlx re-exports the same API for SQLx users and keeps SQLx as an
adapter dependency instead of making the generic database cache API depend on
SQLx.
use HydraCache;
use ;
# async
SqlxQueryExt adds fetch_one, fetch_optional, and fetch_all for common
pool-backed reads. fetch_optional caches None, and fetch_all caches empty
vectors, so repeated misses do not keep hitting the database. Use fetch_with
when you need sqlx::query!, sqlx::query_as!, transactions, or repository
methods at the call site. Use named::<T>("load-user") when you want a
diagnostic label; otherwise cached::<T>() derives diagnostics from the
namespace/key context.
hydracache-sqlx includes a Postgres integration test backed by
testcontainers. When Docker is available, it verifies cache hits, tag
invalidation, and reloads against a real database. When Docker is unavailable,
the test logs a skip message and exits successfully instead of failing the
build.
Testing and coverage commands are documented in docs/TESTING.md.
Quality Gate
The main local verification commands are:
cargo fmt --all -- --check
cargo check --workspace --all-targets --locked
cargo test --workspace --all-targets --locked
cargo clippy --workspace --all-targets --all-features --locked -- -D warnings
cargo test --doc --workspace --locked
cargo llvm-cov --workspace --all-targets --locked --summary-only
Coverage is tracked with cargo-llvm-cov. The current target is 100%
function coverage and 99%+ total line coverage, with visible uncovered source
lines investigated before release.
Which Crate Should I Use?
hydracache- use this for the local async cache, typed cache, TTLs, tags, single-flight, and stats.hydracache-db- use this when wrapping database or repository calls with explicit query-result caching.hydracache-sqlx- use this if you want the SQLx-facing crate, SQLx re-export, andfetch_one/fetch_optional/fetch_allhelpers.hydracache-core- use this only if you need core shared types without the runtime.
Release Plan
The v0 release plan is maintained here:
- docs/plans/V0_RELEASE_PLAN.md
- docs/plans/V0_3_LOCAL_ERGONOMICS_PLAN.md
- docs/plans/V0_7_SQLX_RUNTIME_ADAPTER_PLAN.md
- docs/plans/V0_8_SQLX_HELPERS_PLAN.md
Workspace
crates/hydracache-core- core public types: keys, tags, options, stats, codec, errorscrates/hydracache- user-facing local cache runtime, typed cache, single-flight, tag index, and statscrates/hydracache-db- database-neutral query result-cache adapter APIcrates/hydracache-macros- future macro ergonomicscrates/hydracache-sqlx- SQLx-facing integration crate and re-exports
Crate Layout
hydracache keeps public API re-exports in src/lib.rs and splits runtime code
into focused modules:
cache.rs-HydraCacheruntime APIbuilder.rs- local cache buildertyped.rs-TypedCache<T>namespaced viewentry.rs- encoded cache entries and TTL expirationinflight.rs- local single-flight in-flight load trackingtag_index.rs- tag index and generation freshness checksstats.rs- internal stats counters
hydracache-core keeps public API re-exports in src/lib.rs and splits shared
types into:
key.rs-CacheKeyandCacheKeyBuildertags.rs-TagSetoptions.rs-CacheOptionsstats.rs-CacheStatscodec.rs-CacheCodecandPostcardCodecerror.rs-CacheError