Releases: javaquasar/hydracache
v0.15.0
HydraCache 0.15.0 Release Notes
HydraCache 0.15.0 improves the explicit local-cache macro API introduced in
0.14.0.
What's New
- Added
tags = ...tocacheable!(...). - Kept repeated
tag = ...support and allowed it to combine withtags = .... - Added
cacheable_infallible!(...)for loaders that return a value directly
instead ofResult<T, E>. - Re-exported
cacheable_infallible!from the basehydracachecrate. - Expanded README and cargo-doc examples for
tags = ...,TagSet, and
cacheable_infallible!(...). - Added
crates/hydracache/examples/cacheable_function.rsas a live example of
cache hit, invalidation, and infallible function caching.
Examples
use hydracache::{cacheable, cacheable_infallible, HydraCache};
# async fn example() -> hydracache::CacheResult<()> {
let cache = HydraCache::local().build();
let value = cacheable!(
cache = cache,
key = "expensive:42",
tags = ["expensive", "expensive:42"],
ttl_secs = 60,
load = || async { Ok::<_, std::io::Error>(42_u64) },
)
.await?;
let total = cacheable_infallible!(
cache = cache,
key = "expensive-total",
tags = ["expensive"],
ttl_secs = 60,
load = || async { 1_u64 },
)
.await?;
assert_eq!(value, 42);
assert_eq!(total, 1);
# Ok(())
# }Why It Matters
This release keeps the macro explicit and local-first while making common call
sites shorter:
- use
tags = [...]when several invalidation tags belong to one value; - use
cacheable_infallible!when the loader cannot fail andOk::<_, E>(...)
would be only ceremony; - keep
cacheable!for fallible async loaders, HTTP calls, filesystem reads,
repository calls, or other application work.
Compatibility
This release is additive. Existing cacheable!, HydraCache, TypedCache,
DbCache, QueryCachePolicy, HydraCacheEntity, SQLx helper, and explicit
runtime APIs remain available.
Validation
Release validation passed:
cargo fmt --all -- --checkcargo check --workspace --all-targets --lockedcargo test --workspace --all-targets --lockedcargo clippy --workspace --all-targets --all-features --locked -- -D warningscargo test --doc --workspace --lockedcargo doc --workspace --no-deps --lockedcargo run -p hydracache --example cacheable_function --lockedcargo llvm-cov --workspace --all-targets --locked --summary-onlycargo package --workspace --allow-dirty --locked
Coverage summary in this local run:
- functions:
97.65% - visible source lines:
99.45% - regions:
97.60%
The 0.15.0 macro/runtime code is covered by parser unit tests, async runtime
tests, and trybuild compile-pass coverage. The lower workspace-level coverage
summary came from the Docker-backed SQLx integration path being skipped in this
local environment.
v0.14.0
HydraCache 0.14.0 Release Notes
HydraCache 0.14.0 adds cacheable!(...), the first local-cache macro for
ordinary expensive async work. This release keeps function caching separate from
database result caching: no SQLx, entity, collection, or repository concepts are
required.
What's New
- Added
cacheable!(...)inhydracache-macros. - Re-exported
cacheable!fromhydracache. - Required explicit
cache,key, andloadoptions. - Supported repeated
tagoptions. - Supported one TTL option:
ttl = Durationorttl_secs = u64. - Kept the generated path on top of
HydraCache::get_or_load, so existing
single-flight, TTL, tag invalidation, codec, and stats behavior still applies.
Example
use hydracache::{cacheable, HydraCache};
# async fn example() -> hydracache::CacheResult<()> {
let cache = HydraCache::local().build();
let value = cacheable!(
cache = cache,
key = "expensive:42",
tag = "expensive",
ttl_secs = 60,
load = || async { Ok::<_, std::io::Error>(42_u64) },
)
.await?;
assert_eq!(value, 42);
# Ok(())
# }Why It Matters
cacheable! gives application code a shorter spelling for ordinary async
function/result caching while preserving the main HydraCache design rule: cache
keys, tags, TTLs, and loaders remain explicit and visible at the call site.
This is intentionally different from the database adapter API:
- Use
cacheable!for expensive calculations, HTTP calls, filesystem reads, or
non-database service calls. - Use
hydracache-dborhydracache-sqlxfor database/repository result
caching with entity and collection invalidation semantics.
Known Boundaries
cacheable!is explicit-only: callers provide the cache, key, tags, TTL, and
loader.- It is not an attribute macro yet and does not rewrite function definitions.
- It does not derive keys or tags from function arguments.
- It does not replace
HydraCache::get_or_load; it is a compact spelling on
top of the same runtime path and bounds.
Compatibility
This release is additive. Existing HydraCache, TypedCache, DbCache,
QueryCachePolicy, HydraCacheEntity, SQLx helper, and explicit
get_or_load(...) APIs remain available.
Validation
Release validation passed:
cargo fmt --all -- --checkcargo check --workspace --all-targets --lockedcargo test --workspace --all-targets --lockedcargo clippy --workspace --all-targets --all-features --locked -- -D warningscargo test --doc --workspace --lockedcargo doc --workspace --no-deps --lockedcargo llvm-cov --workspace --all-targets --locked --summary-onlycargo package --workspace --allow-dirty --locked
Coverage summary:
- functions:
100.00% - visible source lines:
100.00% - regions:
98.53%
v0.13.0
HydraCache 0.13.0 Release Notes
HydraCache 0.13.0 adds the first ergonomic macro on top of the
database-neutral QueryCachePolicy API.
Highlights
- Added
query_cache_policy!(...). - Re-exported
query_cache_policy!fromhydracache-db. - Re-exported
query_cache_policy!fromhydracache-sqlx. - Added compile-pass and compile-fail tests for policy macro usage.
- Kept loaders explicit through
load(...),fetch_with(...), and SQLx
helper methods.
Example
use hydracache_db::query_cache_policy;
let user_id = 42_i64;
let policy = query_cache_policy!(
name = "load-user",
entity = User,
id = user_id,
tag = "tenant:7",
ttl_secs = 60,
);
let user = queries
.cached_with::<User>(policy)
.load(|| repo.find_user(user_id))
.await?;Manual policies remain available:
let policy = QueryCachePolicy::named("load-user")
.for_cache_entity::<User>(user_id)
.tag("tenant:7")
.ttl(Duration::from_secs(60));Macro Options
entity = Type, id = expr: useCacheEntitymetadata for key and tags.key = expr: set an explicit logical key.collection = expr: set a collection key and tag.name = expr: set a diagnostic operation name.tag = expr: add an invalidation tag; may be repeated.collection_tag = expr: add an escaped collection tag; may be repeated.ttl = expr: set an explicitDuration.ttl_secs = expr: set TTL viaDuration::from_secs(expr).
Exactly one key source is required: entity + id, key, or collection.
Compatibility
This release is additive. Existing QueryCachePolicy, DbQuery, and SQLx
helper APIs continue to work.
v0.12.0
HydraCache 0.12.0 Release Notes
HydraCache 0.12.0 adds reusable database query cache policies and a shorter
repository-style load API.
Highlights
- Added
QueryCachePolicytohydracache-db. - Added
DbCache::cached_with::<T>(policy). - Added
DbQuery::with_policy(policy)andDbQuery::cache_policy(). - Added
DbQuery::collection(name). - Added
DbQuery::load(...)as an alias forfetch_with(...). - Re-exported
QueryCachePolicyfromhydracache-sqlx. - Kept existing
.cached().key().tag().ttl().fetch_with(...),
for_entity(...), and SQLx helper APIs available.
Example
use std::time::Duration;
use hydracache_db::QueryCachePolicy;
let policy = QueryCachePolicy::named("load-user")
.for_cache_entity::<User>(42)
.ttl(Duration::from_secs(60));
let user = queries
.cached_with::<User>(policy)
.load(|| repo.find_user(42))
.await?;Why This Matters
QueryCachePolicy makes the reusable part of a cached database operation
explicit: diagnostic name, key, tags, and TTL. The loader remains caller-owned,
so applications can use SQLx, Diesel, SeaORM, raw clients, or repository
methods without forcing HydraCache to become a database abstraction layer.
Compatibility
This release is additive for public APIs. Existing query descriptors and SQLx
helper calls continue to work.
with_policy(...) replaces the current descriptor policy. It does not merge
old and new tags or TTLs.
Publish Order
Publish in dependency order:
hydracache-corehydracachehydracache-macroshydracache-dbhydracache-sqlx
Wait for crates.io index propagation between dependent crates.
v0.11.1
HydraCache 0.11.1 Release Notes
HydraCache 0.11.1 is a patch release for public documentation and crate
metadata. It does not change runtime behavior or public API semantics.
Highlights
- Clarified that
CacheEntityandHydraCacheEntitybelong to the
database-neutralhydracache-dblayer. - Kept the
hydracache-sqlxre-exports supported as adapter convenience for
SQLx users. - Updated README examples to show the canonical import boundary.
- Documented that publishing smoke tests may intentionally import through
hydracache-sqlxwhen verifying adapter re-exports.
Canonical Entity Import
Use hydracache-db for database-neutral entity metadata:
use hydracache_db::{CacheEntity, HydraCacheEntity};
use hydracache_sqlx::DbCache;
#[derive(serde::Serialize, serde::Deserialize, HydraCacheEntity)]
#[hydracache(entity = "user", collection = "users", id = i64)]
struct User {
id: i64,
name: String,
}
# async fn example(queries: DbCache) -> hydracache_sqlx::Result<()> {
let user = queries
.for_entity::<User>(42)
.fetch_with(|| async {
Ok::<_, std::io::Error>(User {
id: 42,
name: "Ada".to_owned(),
})
})
.await?;
assert_eq!(user.id, 42);
assert_eq!(User::cache_key_for(&42), "user:42");
assert_eq!(User::collection_tag(), Some("users".to_owned()));
# Ok(())
# }hydracache-sqlx::{CacheEntity, HydraCacheEntity} remains valid for users who
want to depend only on the SQLx adapter crate in application code.
Publish Order
Publish in dependency order:
hydracache-corehydracachehydracache-macroshydracache-dbhydracache-sqlx
Wait for crates.io index propagation between dependent crates.
v0.11.0
HydraCache 0.11.0 Release Notes
HydraCache 0.11.0 adds a derive macro for domain entity cache metadata.
Highlights
- Added the publishable
hydracache-macroscrate. - Added
#[derive(HydraCacheEntity)]. - Re-exported
HydraCacheEntityfromhydracache-db. - Re-exported
HydraCacheEntityfromhydracache-sqlx. - Kept manual
CacheEntityimplementations fully supported. - Added compile-pass and compile-fail macro tests with
trybuild. - Updated live documentation examples to use the derive path.
Example
use hydracache_sqlx::{CacheEntity, HydraCacheEntity};
#[derive(HydraCacheEntity)]
#[hydracache(entity = "user", collection = "users", id = i64)]
struct User;
assert_eq!(User::cache_key_for(&42), "user:42");
assert_eq!(User::entity_tag_for(&42), "user:42");
assert_eq!(User::collection_tag(), Some("users".to_owned()));Semantics
entityis required and becomesCacheEntity::ENTITY.idis required and becomesCacheEntity::Id.collectionis optional and becomesCacheEntity::COLLECTION.- Generated keys and tags still use the existing
CacheKeyBuilderescaping
rules through theCacheEntitydefault methods. - The macro does not change
DbCache,DbQuery, SQLx execution, loaders,
single-flight, TTLs, or invalidation semantics.
Backward Compatibility
Manual metadata remains valid:
impl CacheEntity for User {
type Id = i64;
const ENTITY: &'static str = "user";
const COLLECTION: Option<&'static str> = Some("users");
}The full-control descriptor API also remains available:
queries.cached::<User>().key("user:42").tag("user:42")Publish Order
hydracache-macros is now a published crate. Publish in dependency order:
hydracache-corehydracachehydracache-macroshydracache-dbhydracache-sqlx
Wait for crates.io index propagation between dependent crates.
Validation
cargo fmt --all -- --checkcargo check --workspace --all-targets --lockedcargo test --workspace --all-targets --lockedcargo clippy --workspace --all-targets --all-features --locked -- -D warningscargo test --doc --workspace --lockedcargo doc --workspace --no-deps --lockedcargo llvm-cov --workspace --all-targets --locked --summary-only
The SQLx Postgres integration test uses testcontainers. It runs against a real
database when Docker is available and skips successfully when Docker is not
available.
Stable cargo-llvm-cov does not count the exported proc-macro entrypoint as a
normal covered function, even though trybuild executes the macro through
rustc. The macro parser, expansion logic, crate-path resolution, successful
compile cases, and compile-fail diagnostics are covered by unit tests and
trybuild; the remaining function-summary gap is the thin
proc_macro::TokenStream wrapper.
v0.10.0
HydraCache 0.10.0 Release Notes
HydraCache 0.10.0 adds domain entity cache metadata for database result
caching.
Highlights
- Added
CacheEntityinhydracache-db. - Re-exported
CacheEntityfromhydracache-sqlx. - Added
DbCache::for_entity<T: CacheEntity>(id). - Added
DbQuery::for_cache_entity(id)for existing descriptor chains. - Automatically adds the entity tag and optional collection tag from metadata.
- Kept
entity(kind, id),collection(name), and the explicit
.cached().key().tag()API unchanged. - Extended unit tests and real Postgres SQLx integration tests for the new
metadata-driven path.
Example
use hydracache::HydraCache;
use hydracache_sqlx::{CacheEntity, DbCache};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
id: i64,
name: String,
}
impl CacheEntity for User {
type Id = i64;
const ENTITY: &'static str = "user";
const COLLECTION: Option<&'static str> = Some("users");
}
# async fn example() -> hydracache_sqlx::Result<()> {
let queries = DbCache::new(HydraCache::local().build(), "db");
let user = queries
.for_entity::<User>(42)
.fetch_with(|| async {
Ok::<_, std::io::Error>(User {
id: 42,
name: "Ada".to_owned(),
})
})
.await?;
assert_eq!(user.id, 42);
# Ok(())
# }Semantics
CacheEntity::ENTITYprovides the entity key/tag prefix.CacheEntity::COLLECTIONoptionally provides a broader invalidation tag.DbCache::for_entity::<User>(42)creates keyuser:42, taguser:42, and
tagusersfor the example above.DbQuery::for_cache_entity(42)applies the same metadata to an existing
descriptor and preserves existing tags.- All generated segments use
CacheKeyBuilderescaping.
Backward Compatibility
The 0.9.0 helpers remain available:
queries.entity::<User>("user", 42).collection_tag("users")The full-control API also remains available:
queries.cached::<User>().key("user:42").tag("user:42")CacheEntity is an additive metadata layer and the intended future expansion
target for a derive macro.
Validation
cargo fmt --all -- --checkcargo check --workspace --all-targets --lockedcargo test --workspace --all-targets --lockedcargo clippy --workspace --all-targets --all-features --locked -- -D warningscargo test --doc --workspace --lockedcargo doc --workspace --no-deps --lockedcargo llvm-cov --workspace --all-targets --locked --summary-onlycargo package -p hydracache-core --locked --allow-dirty
The SQLx Postgres integration test uses testcontainers. It runs against a real
database when Docker is available and skips successfully when Docker is not
available.
Dependent package verification must be run in publish order after
hydracache-core 0.10.0 and then hydracache 0.10.0 are available on
crates.io.
v0.9.0
HydraCache 0.9.0 Release Notes
HydraCache 0.9.0 adds database query ergonomics on top of the explicit
hydracache-db and hydracache-sqlx APIs.
Highlights
- Added
DbCache::entityfor entity-shaped query results. - Added
DbCache::collectionfor collection-shaped query results. - Added
DbQuery::for_entityto set an entity key and entity tag on an existing
query descriptor. - Added
DbQuery::collection_tagto associate entity results with broader
invalidation groups. - Kept
cached().key().tag().ttl().fetch_with(...)as the full-control API. - Extended the real Postgres testcontainers integration test to verify the new
helpers withSqlxQueryExt.
Example
use hydracache::HydraCache;
use hydracache_sqlx::{DbCache, SqlxQueryExt};
# async fn example(pool: sqlx::PgPool) -> hydracache_sqlx::Result<()> {
let cache = HydraCache::local().build();
let queries = DbCache::new(cache, "db");
let (id, name): (i64, String) = queries
.entity::<(i64, String)>("user", 42)
.collection_tag("users")
.fetch_one(
pool.clone(),
sqlx::query_as("select id, name from users where id = $1").bind(42_i64),
)
.await?;
assert_eq!(id, 42);
assert!(!name.is_empty());
# Ok(())
# }Semantics
entity::<T>("user", 42)creates logical keyuser:42and taguser:42.collection::<T>("users")creates logical keyusersand tagusers.collection_tag("users")adds a tag without changing the current key.- Generated entity and collection segments use
CacheKeyBuilderescaping, so
:and%inside one segment cannot accidentally create extra segments. - Explicit
.key(...)can still override a generated key when needed.
Backward Compatibility
The existing .cached::<T>().key(...).tag(...).ttl(...).fetch_with(...) API is
unchanged and remains the full-control layer. The new helpers are ergonomic
shortcuts over the same descriptor model, not a replacement.
Validation
cargo fmt --all -- --checkcargo check --workspace --all-targets --lockedcargo test --workspace --all-targets --lockedcargo clippy --workspace --all-targets --all-features --locked -- -D warningscargo test --doc --workspace --lockedcargo doc --workspace --no-deps --lockedcargo package -p hydracache-core --locked --allow-dirty
The SQLx Postgres integration test uses testcontainers. It runs against a real
database when Docker is available and skips successfully when Docker is not
available.
Dependent package verification must be run in publish order after
hydracache-core 0.9.0 and then hydracache 0.9.0 are available on crates.io.
v0.8.0
HydraCache 0.8.0 Release Notes
HydraCache 0.8.0 makes the SQLx adapter more ergonomic while keeping the
database-neutral cache boundary intact.
Highlights
- Added
SqlxQueryExtinhydracache-sqlx. - Added
fetch_one,fetch_optional, andfetch_allhelpers for common
pool-backed SQLx reads. - Kept
fetch_withas the escape hatch for SQLx macros, transactions,
repository methods, and custom database flows. - Added
DbQuery::fetch_value_withfor adapter crates that need to cache
shapes such asOption<T>andVec<T>. - Extended the real Postgres testcontainers integration test to cover helper
cache hits, optional results, list results, tag invalidation, and failed SQL
not being cached.
Example
use hydracache::HydraCache;
use hydracache_sqlx::{DbCache, SqlxQueryExt};
# async fn example(pool: sqlx::PgPool) -> hydracache_sqlx::Result<()> {
let cache = HydraCache::local().build();
let queries = DbCache::new(cache, "db");
let (id, name): (i64, String) = queries
.cached::<(i64, String)>()
.key("user:42")
.tag("user:42")
.fetch_one(
pool.clone(),
sqlx::query_as("select id, name from users where id = $1").bind(42_i64),
)
.await?;
assert_eq!(id, 42);
assert!(!name.is_empty());
# Ok(())
# }Semantics
- Cache hits do not execute SQLx.
- Cache misses execute SQLx once per key under local single-flight.
fetch_optionalcachesNone.fetch_allcaches empty vectors.- SQLx failures are returned and are not stored.
Validation
cargo fmt --allcargo check --workspace --all-targetscargo test --workspace --all-targetscargo clippy --workspace --all-targets --locked -- -D warningscargo test --doc --workspace --lockedcargo doc --workspace --no-deps --lockedcargo package -p hydracache-core --locked --allow-dirty
Dependent package verification must be run in publish order after
hydracache-core 0.8.0 is available on crates.io.
v0.7.0
HydraCache 0.7.0
Summary
0.7.0 introduces hydracache-db, the database-neutral result-cache adapter
crate, and hydracache-sqlx, the first SQLx-facing integration crate.
The release starts the SQLx direction carefully: SQLx remains responsible for
database execution, compile-time macros, pools, transactions, and row mapping.
HydraCache owns the cache boundary around that execution: explicit keys, tags,
TTL, local single-flight, serialization, and invalidation.
Added
hydracache-dbworkspace crate.hydracache-sqlxworkspace crate overhydracache-db.DbCachenamespaced adapter over an existingHydraCache.DbQuery<T>query result-cache descriptor.DbCache::cached::<T>()for cache descriptors without duplicate SQL text.DbCache::named::<T>("name")for optional diagnostic labels.SqlxCacheandSqlxQuerycompatibility aliases fromhydracache-sqlx.- Explicit query keys via
keyandkey_builder. - Query tags via
tag,tags, andtag_set. - Per-query TTL via
ttl. fetch_withfor cache lookup plus caller-owned SQLx loading.DbCacheError::MissingKeyfor descriptors without explicit keys.- Post-publish verification coverage for the SQLx adapter crate.
- Postgres testcontainers integration coverage for
hydracache-sqlx.
Example
use hydracache::HydraCache;
use hydracache_db::DbCache;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
id: i64,
name: String,
}
# async fn example() -> hydracache_sqlx::Result<()> {
let local = HydraCache::local().build();
let queries = DbCache::new(local, "db");
let user = queries
.cached::<User>()
.key("user:42")
.tag("user:42")
.fetch_with(|| async {
Ok::<_, std::io::Error>(User {
id: 42,
name: "Ada".to_owned(),
})
})
.await?;
# Ok(())
# }Why fetch_with First
The adapter does not hide SQLx behind a second query API yet. That is deliberate:
the first version should be easy to reason about, easy to test, and hard to
misuse.
Applications can keep using sqlx::query!, sqlx::query_as!, transactions,
pools, and their own connection lifetimes. HydraCache only wraps the result with
cache behavior.
cached::<T>() is the preferred entry point because it avoids duplicating SQL
text in the cache descriptor and in the SQLx loader. named::<T>("load-user")
can be used when diagnostics need a human-readable operation name. query_as
is kept as a compatibility/diagnostic alias when the SQL text itself is a useful
label.
Verification
cargo fmt --all -- --checkcargo check --workspace --all-targets --lockedcargo test --workspace --lockedcargo test -p hydracache-sqlx --test postgres_testcontainers --locked -- --nocapturecargo clippy --workspace --all-targets --all-features --locked -- -D warningsRUSTDOCFLAGS="-D warnings" cargo doc --workspace --no-deps --lockedcargo package -p hydracache-core --locked --allow-dirty
The Postgres integration test uses testcontainers. It runs against a real
Postgres container when Docker is available and skips cleanly when Docker is not
available in the environment.
hydracache, hydracache-db, and hydracache-sqlx package verification
requires hydracache-core 0.7.0 and then hydracache 0.7.0 to be visible in
the crates.io index first.
MSRV Dependency Note
hydracache-sqlx adds SQLx and testcontainers integration coverage, which
pulls modern async/database ecosystem dependencies. HydraCache now declares Rust
1.88 as MSRV for 0.7.0, allowing the lockfile to use current ICU/IDNA and
testcontainers dependency graphs instead of pinning older transitive versions.
The earlier Rust 1.85 pinning decision is tracked historically in
TD-0001.