Skip to content

Add cache_clear operation#200

Open
9999years wants to merge 1 commit into
jaemk:masterfrom
9999years:cache-clear
Open

Add cache_clear operation#200
9999years wants to merge 1 commit into
jaemk:masterfrom
9999years:cache-clear

Conversation

@9999years

Copy link
Copy Markdown
Contributor

Closes #197.

Comment thread src/stores/redis.rs Outdated
Comment on lines +347 to +348
// https://redis.io/commands/flushdb/
let _: () = redis::cmd("FLUSHDB").query(&mut *conn)?;

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this delete someone's entire redis db? I think a scan + delete would need to be done instead, using the self.namespace + self.prefix in the scan

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I just assumed the Redis DB was already unique per cache. I can fix this.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ty!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I took a shot at it but needing two connections seems weird. scan_match needs &mut self, though, so I can't do anything with the deserialized values without opening another connection or collecting them all into memory first.

https://docs.rs/redis/latest/redis/trait.Commands.html#method.scan_match

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any plans to merge this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took a while, but I reimplemented this with the scan + delete strategy. Should be ready now?

@LucaCappelletti94

Copy link
Copy Markdown

Hi all, is there any development regarding this feature? Was it possibly added in some other PR? I am looking for a way to clear the redis cache.

@9999years

Copy link
Copy Markdown
Contributor Author

@LucaCappelletti94 I just fixed up the merge conflicts and fixed up the async Redis cache_clear implementation, so it should be ready now, but in the meantime you can just write your own functions (or even an extension trait) with the same implementations.

jaemk added a commit that referenced this pull request Jun 9, 2026
…ext major)

Second tranche of the next major (after the redb DiskCache rewrite). Bundles a
set of breaking and additive changes.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149)
- `force_refresh` attribute to bypass the cache per call (#146)
- `in_impl` attribute to cache methods inside `impl` blocks / `self` receivers (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; the redis/disk macro paths use the borrowed setter
  to avoid an extra clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear` (#200)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache (#180)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236)
- release workflow tags and creates a GitHub release on publish (#245)
- fix a doctest under `--no-default-features` (#260)

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 9, 2026
…ext major)

Second tranche of the next major (after the redb DiskCache rewrite). Bundles a
set of breaking and additive changes.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149)
- `force_refresh` attribute to bypass the cache per call (#146)
- `in_impl` attribute to cache methods inside `impl` blocks / `self` receivers (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; the redis/disk macro paths use the borrowed setter
  to avoid an extra clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear` (#200)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache (#180)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236)
- release workflow tags and creates a GitHub release on publish (#245)
- fix a doctest under `--no-default-features` (#260)

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 9, 2026
…ext major)

Second tranche of the next major (after the redb DiskCache rewrite). Bundles a
set of breaking and additive changes.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149)
- `force_refresh` attribute to bypass the cache per call (#146)
- `in_impl` attribute to cache methods inside `impl` blocks / `self` receivers (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; the redis/disk macro paths use the borrowed setter
  to avoid an extra clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear` (#200)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache (#180)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236)
- release workflow tags and creates a GitHub release on publish (#245)
- fix a doctest under `--no-default-features` (#260)

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 9, 2026
…ext major)

Second tranche of the next major (after the redb DiskCache rewrite). Bundles a
set of breaking and additive changes.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149)
- `force_refresh` attribute to bypass the cache per call (#146)
- `in_impl` attribute to cache methods inside `impl` blocks / `self` receivers (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; the redis/disk macro paths use the borrowed setter
  to avoid an extra clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear` (#200)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache (#180)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236)
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245)
- fix a doctest under `--no-default-features` (#260)

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 10, 2026
…ext major)

Second tranche of the next major (after the redb DiskCache rewrite). Bundles a
set of breaking and additive changes.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149)
- `force_refresh` attribute to bypass the cache per call (#146)
- `in_impl` attribute to cache methods inside `impl` blocks / `self` receivers (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; the redis/disk macro paths use the borrowed setter
  to avoid an extra clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear` (#200)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache (#180)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236)
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245)
- fix a doctest under `--no-default-features` (#260)

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 10, 2026
…ext major)

Second tranche of the next major (after the redb DiskCache rewrite). Bundles a
set of breaking and additive changes.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149)
- `force_refresh` attribute to bypass the cache per call (#146)
- `in_impl` attribute to cache methods inside `impl` blocks / `self` receivers (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear` (#200)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache (#180)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236)
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245)
- fix a doctest under `--no-default-features` (#260)

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 10, 2026
…ext major)

Next-major batch (after the redb DiskCache rewrite). Bundles a set of
breaking and additive changes plus the doc/test follow-ups.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149)
- `force_refresh` attribute to bypass the cache per call, on all three macros
  (`#[cached]` / `#[concurrent_cached]` / `#[once]`); on `#[once]` it overwrites
  the single shared value (no per-call key, so no key-exclusion caveat) (#146)
- `in_impl` attribute to cache methods inside `impl` blocks / `self` receivers (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)
- the shared `force_refresh` guard builder is factored into one helper

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear` (#200)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache (#180)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236)
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245); the CI tag-release git identity is scoped to `--local`
- fix a doctest under `--no-default-features` (#260)
- exclude internal dev tooling (.agents, .claude, .github, bin, docs/dev, AGENTS.md,
  CLAUDE.md) from the published crate via Cargo `exclude`
- documentation accuracy pass: `ttl_millis` is valid with `result_fallback` and (on the
  in-memory path) needs `time_stores`; `in_impl` emits a same-visibility `{fn}_no_cache`
  cache-bypass sibling; wasm is incompatible with `redis_connection_manager` /
  `redis_async_cache`; Redis whole-second `ttl_millis` rounding; the `in_impl` shared-cache
  footgun; the async `V: Sync` clone-elision asymmetry; `CachedAsync` shared-ref default
  Send bounds; migration-guide `&mut V` -> `&V` coercion caveat
- added coverage: async clone-elision clone-count assertions; `#[concurrent_cached]`
  `result_fallback` + `force_refresh`; `#[once]` `force_refresh`; generic `in_impl`
  rejection golden; `TtlSortedCache` shared-ref get-or-set delegation

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 10, 2026
…ext major)

Next-major batch (after the redb DiskCache rewrite). Bundles a set of
breaking and additive changes plus the doc/test follow-ups.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149);
  rejected alongside a `create` block like the other store-builder attributes
- `force_refresh` attribute to bypass the cache per call, on all three macros
  (`#[cached]` / `#[concurrent_cached]` / `#[once]`); on `#[once]` it overwrites
  the single shared value (no per-call key, so no key-exclusion caveat) (#146)
- `in_impl` attribute to cache methods inside `impl` blocks; `self`-receiver
  methods require `in_impl` (a `convert` block alone cannot rescue them, since the
  cache static cannot live at `impl` scope) and emit a same-visibility
  `{fn}_no_cache` cache-bypass sibling (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)
- the shared `force_refresh` guard builder is factored into one helper

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear` (#200)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache (#180)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236)
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245); the CI tag-release git identity is scoped to `--local`
- fix a doctest under `--no-default-features` (#260)
- exclude internal dev tooling (.agents, .claude, .github, bin, docs/dev, AGENTS.md,
  CLAUDE.md) from the published crate via Cargo `exclude`
- documentation accuracy pass: `ttl_millis` is valid with `result_fallback` and (on the
  in-memory path) needs `time_stores`; on `#[cached]` the "honored exactly" claim is
  scoped to the default in-memory store; Redis rounds `ttl_millis` up to the next whole
  second (500ms -> 1s, 1500ms -> 2s); `in_impl` emits a same-visibility `{fn}_no_cache`
  sibling (documented on all three macros); `#[concurrent_cached]` `force_refresh`
  documents the `result_fallback` interaction; wasm is incompatible with
  `redis_connection_manager` / `redis_async_cache`; the `in_impl` shared-cache footgun;
  the async `V: Sync` clone-elision asymmetry, including how a `Send + !Sync + !Clone`
  value surfaces at the generated set site; `CachedAsync` shared-ref default Send bounds;
  `Cached::cache_get_or_set_with` / `cache_try_get_or_set_with` flagged as provided
  defaults; migration-guide `&mut V` -> `&V` coercion caveat
- added coverage: async clone-elision clone-count assertions; `#[concurrent_cached]`
  `result_fallback` + `force_refresh`; `#[once]` `force_refresh`; `force_refresh` +
  `in_impl`; generic `in_impl` rejection golden; compile-fail goldens for
  `force_refresh`-unparseable on `#[once]` / `#[concurrent_cached]`, generic
  `#[concurrent_cached]` without `convert`, and `ttl_millis` + `create` conflict;
  `TtlSortedCache` shared-ref get-or-set delegation
- macro-output polish: no dead `#[doc]` on the function-local `in_impl` cache static;
  documented rationale for `#[allow(dead_code)]` on the generated prime companion and
  for the `crate_path()` `::cached` fallback

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 11, 2026
…ext major)

Next-major batch (after the redb DiskCache rewrite). Bundles a set of
breaking and additive changes plus the doc/test follow-ups.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149);
  rejected alongside a `create` block like the other store-builder attributes
- `force_refresh` attribute to bypass the cache per call, on all three macros
  (`#[cached]` / `#[concurrent_cached]` / `#[once]`); on `#[once]` it overwrites
  the single shared value (no per-call key, so no key-exclusion caveat). Combined
  with `result_fallback`, a force-refreshed `Err` still serves the previously
  cached `Ok`, and capturing that fallback leaves no read side effects on the
  bypassed entry (no TTL renewal, recency update, or hit-counter change) on both
  `#[cached]` and `#[concurrent_cached]` (#146)
- `in_impl` attribute to cache methods inside `impl` blocks; `self`-receiver
  methods require `in_impl` (a `convert` block alone cannot rescue them, since the
  cache static cannot live at `impl` scope) and emit a `{fn}_no_cache`
  cache-bypass sibling, hidden from rustdoc via `#[doc(hidden)]` (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)
- the shared `force_refresh` guard builder is factored into one helper

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear`, with
  `cache_reset` delegating to `cache_clear`; `RedisCacheBuilder` /
  `AsyncRedisCacheBuilder` `build()` reject an empty namespace+prefix scope
  (`EmptyScope`) so `cache_clear` cannot `SCAN MATCH *` the whole database (#200)
- `RedbCacheBuilder::build()` validates `cache_name` as a filename component,
  returning `InvalidCacheName` for a path separator (`/` or `\`) or a
  path-traversal component (`.` / `..`)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache, with
  matching methods on `LruTtlCache` and `ExpiringLruCache` (#180)
- `ConcurrentCloneCached` gains a non-renewing `cache_peek_with_expiry_status`
  (used to capture a `result_fallback` stale value without read side effects)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236), the latter demonstrating `in_impl`
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245); the CI tag-release git identity is scoped to `--local`
- fix a doctest under `--no-default-features` (#260)
- exclude internal dev tooling (.agents, .claude, .github, bin, docs/dev, AGENTS.md,
  CLAUDE.md) from the published crate via Cargo `exclude`
- dev tooling: pr-review shards reviewers across model-sized, randomized chunks
  (multiple of each type); pr-cycle fans fix application across disjoint sub-agents;
  AGENTS.md states README is generated from src/lib.rs via cargo-readme
- documentation accuracy pass: `ttl_millis` is valid with `result_fallback` and (on the
  in-memory path) needs `time_stores`; on `#[cached]` the "honored exactly" claim is
  scoped to the default in-memory store; Redis rounds `ttl_millis` up to the next whole
  second (500ms -> 1s, 1500ms -> 2s); `in_impl` emits a `#[doc(hidden)]` `{fn}_no_cache`
  sibling (documented on all three macros); `#[concurrent_cached]` `force_refresh`
  documents the `result_fallback` interaction; the `#[concurrent_cached]` attribute table
  reaches parity with `#[cached]` (ttl_millis / force_refresh / in_impl rows); wasm is
  incompatible with `redis_connection_manager` / `redis_async_cache`; the `in_impl`
  shared-cache footgun; the async `V: Sync` clone-elision asymmetry, including how a
  `Send + !Sync + !Clone` value surfaces at the generated set site; `CachedAsync`
  shared-ref default Send bounds; `Cached::cache_get_or_set_with` /
  `cache_try_get_or_set_with` flagged as provided defaults; migration-guide
  `&mut V` -> `&V` coercion caveat
- added coverage: async clone-elision clone-count assertions; `#[concurrent_cached]`
  `result_fallback` + `force_refresh` (including the no-read-side-effects bypass); `#[once]`
  `force_refresh`; `force_refresh` + `in_impl`; generic `in_impl` rejection goldens for
  `#[cached]` (free fn) and `#[concurrent_cached]` (in_impl method); compile-fail goldens
  for `force_refresh`-unparseable on `#[once]` / `#[concurrent_cached]`, generic
  `#[concurrent_cached]` without `convert`, and `ttl_millis` + `create` conflict;
  `_mut` trait coverage on `ExpiringCache` / `TtlSortedCache`; redis `cache_clear` /
  `cache_set_ref` and redb `cache_set_ref` round-trips; `TtlSortedCache` shared-ref
  get-or-set delegation
- macro-output polish: ASCII-only macro diagnostics; no dead `#[doc]` on the
  function-local `in_impl` cache static; documented rationale for `#[allow(dead_code)]`
  on the generated prime companion and for the `crate_path()` `::cached` fallback

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 11, 2026
…ext major)

Next-major batch (after the redb DiskCache rewrite). Bundles a set of
breaking and additive changes plus the doc/test follow-ups.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149);
  rejected alongside a `create` block like the other store-builder attributes
- `force_refresh` attribute to bypass the cache per call, on all three macros
  (`#[cached]` / `#[concurrent_cached]` / `#[once]`); on `#[once]` it overwrites
  the single shared value (no per-call key, so no key-exclusion caveat). Combined
  with `result_fallback`, a force-refreshed `Err` still serves the previously
  cached `Ok`, and capturing that fallback leaves no read side effects on the
  bypassed entry (no TTL renewal, recency update, or hit-counter change) on both
  `#[cached]` and `#[concurrent_cached]` (#146)
- `in_impl` attribute to cache methods inside `impl` blocks; `self`-receiver
  methods require `in_impl` (a `convert` block alone cannot rescue them, since the
  cache static cannot live at `impl` scope) and emit a `{fn}_no_cache`
  cache-bypass sibling, hidden from rustdoc via `#[doc(hidden)]` (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)
- the shared `force_refresh` guard builder is factored into one helper

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear`, with
  `cache_reset` delegating to `cache_clear`; `RedisCacheBuilder` /
  `AsyncRedisCacheBuilder` `build()` reject an empty namespace+prefix scope
  (`EmptyScope`) so `cache_clear` cannot `SCAN MATCH *` the whole database (#200)
- `RedbCacheBuilder::build()` validates `cache_name` as a filename component,
  returning `InvalidCacheName` for a path separator (`/` or `\`) or a
  path-traversal component (`.` / `..`)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache, with
  matching methods on `LruTtlCache` and `ExpiringLruCache` (#180)
- `ConcurrentCloneCached` gains a non-renewing `cache_peek_with_expiry_status`
  (used to capture a `result_fallback` stale value without read side effects)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236), the latter demonstrating `in_impl`
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245); the CI tag-release git identity is scoped to `--local`
- fix a doctest under `--no-default-features` (#260)
- exclude internal dev tooling (.agents, .claude, .github, bin, docs/dev, AGENTS.md,
  CLAUDE.md) from the published crate via Cargo `exclude`
- dev tooling: pr-review shards reviewers across model-sized, randomized chunks
  (multiple of each type); pr-cycle fans fix application across disjoint sub-agents;
  AGENTS.md states README is generated from src/lib.rs via cargo-readme
- documentation accuracy pass: `ttl_millis` is valid with `result_fallback` and (on the
  in-memory path) needs `time_stores`; on `#[cached]` the "honored exactly" claim is
  scoped to the default in-memory store; Redis rounds `ttl_millis` up to the next whole
  second (500ms -> 1s, 1500ms -> 2s); `in_impl` emits a `#[doc(hidden)]` `{fn}_no_cache`
  sibling (documented on all three macros); `#[concurrent_cached]` `force_refresh`
  documents the `result_fallback` interaction; the `#[concurrent_cached]` attribute table
  reaches parity with `#[cached]` (ttl_millis / force_refresh / in_impl rows); wasm is
  incompatible with `redis_connection_manager` / `redis_async_cache`; the `in_impl`
  shared-cache footgun; the async `V: Sync` clone-elision asymmetry, including how a
  `Send + !Sync + !Clone` value surfaces at the generated set site; `CachedAsync`
  shared-ref default Send bounds; `Cached::cache_get_or_set_with` /
  `cache_try_get_or_set_with` flagged as provided defaults; migration-guide
  `&mut V` -> `&V` coercion caveat
- added coverage: async clone-elision clone-count assertions; `#[concurrent_cached]`
  `result_fallback` + `force_refresh` (including the no-read-side-effects bypass); `#[once]`
  `force_refresh`; `force_refresh` + `in_impl`; generic `in_impl` rejection goldens for
  `#[cached]` (free fn) and `#[concurrent_cached]` (in_impl method); compile-fail goldens
  for `force_refresh`-unparseable on `#[once]` / `#[concurrent_cached]`, generic
  `#[concurrent_cached]` without `convert`, and `ttl_millis` + `create` conflict;
  `_mut` trait coverage on `ExpiringCache` / `TtlSortedCache`; redis `cache_clear` /
  `cache_set_ref` and redb `cache_set_ref` round-trips; `TtlSortedCache` shared-ref
  get-or-set delegation
- macro-output polish: ASCII-only macro diagnostics; no dead `#[doc]` on the
  function-local `in_impl` cache static; documented rationale for `#[allow(dead_code)]`
  on the generated prime companion and for the `crate_path()` `::cached` fallback

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 11, 2026
…ext major)

Next-major batch (after the redb DiskCache rewrite). Bundles a set of
breaking and additive changes plus the doc/test follow-ups.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149);
  rejected alongside a `create` block like the other store-builder attributes
- `force_refresh` attribute to bypass the cache per call, on all three macros
  (`#[cached]` / `#[concurrent_cached]` / `#[once]`); on `#[once]` it overwrites
  the single shared value (no per-call key, so no key-exclusion caveat). Combined
  with `result_fallback`, a force-refreshed `Err` still serves the previously
  cached `Ok`, and capturing that fallback leaves no read side effects on the
  bypassed entry (no TTL renewal, recency update, or hit-counter change) on both
  `#[cached]` and `#[concurrent_cached]` (#146)
- `in_impl` attribute to cache methods inside `impl` blocks; `self`-receiver
  methods require `in_impl` (a `convert` block alone cannot rescue them, since the
  cache static cannot live at `impl` scope) and emit a `{fn}_no_cache`
  cache-bypass sibling, hidden from rustdoc via `#[doc(hidden)]` (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)
- the shared `force_refresh` guard builder is factored into one helper

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear`, with
  `cache_reset` delegating to `cache_clear`; `RedisCacheBuilder` /
  `AsyncRedisCacheBuilder` `build()` reject an empty namespace+prefix scope
  (`EmptyScope`) so `cache_clear` cannot `SCAN MATCH *` the whole database (#200)
- `RedbCacheBuilder::build()` validates `cache_name` as a filename component,
  returning `InvalidCacheName` for a path separator (`/` or `\`) or a
  path-traversal component (`.` / `..`)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache, with
  matching methods on `LruTtlCache` and `ExpiringLruCache` (#180)
- `ConcurrentCloneCached` gains a non-renewing `cache_peek_with_expiry_status`
  (used to capture a `result_fallback` stale value without read side effects)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236), the latter demonstrating `in_impl`
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245); the CI tag-release git identity is scoped to `--local`
- fix a doctest under `--no-default-features` (#260)
- exclude internal dev tooling (.agents, .claude, .github, bin, docs/dev, AGENTS.md,
  CLAUDE.md) from the published crate via Cargo `exclude`
- dev tooling: pr-review shards reviewers across model-sized, randomized chunks
  (multiple of each type); pr-cycle fans fix application across disjoint sub-agents;
  AGENTS.md states README is generated from src/lib.rs via cargo-readme
- documentation accuracy pass: `ttl_millis` is valid with `result_fallback` and (on the
  in-memory path) needs `time_stores`; on `#[cached]` the "honored exactly" claim is
  scoped to the default in-memory store; Redis rounds `ttl_millis` up to the next whole
  second (500ms -> 1s, 1500ms -> 2s); `in_impl` emits a `#[doc(hidden)]` `{fn}_no_cache`
  sibling (documented on all three macros); `#[concurrent_cached]` `force_refresh`
  documents the `result_fallback` interaction; the `#[concurrent_cached]` attribute table
  reaches parity with `#[cached]` (ttl_millis / force_refresh / in_impl rows); wasm is
  incompatible with `redis_connection_manager` / `redis_async_cache`; the `in_impl`
  shared-cache footgun; the async `V: Sync` clone-elision asymmetry, including how a
  `Send + !Sync + !Clone` value surfaces at the generated set site; `CachedAsync`
  shared-ref default Send bounds; `Cached::cache_get_or_set_with` /
  `cache_try_get_or_set_with` flagged as provided defaults; migration-guide
  `&mut V` -> `&V` coercion caveat
- added coverage: async clone-elision clone-count assertions; `#[concurrent_cached]`
  `result_fallback` + `force_refresh` (including the no-read-side-effects bypass); `#[once]`
  `force_refresh`; `force_refresh` + `in_impl`; generic `in_impl` rejection goldens for
  `#[cached]` (free fn) and `#[concurrent_cached]` (in_impl method); compile-fail goldens
  for `force_refresh`-unparseable on `#[once]` / `#[concurrent_cached]`, generic
  `#[concurrent_cached]` without `convert`, and `ttl_millis` + `create` conflict;
  `_mut` trait coverage on `ExpiringCache` / `TtlSortedCache`; redis `cache_clear` /
  `cache_set_ref` and redb `cache_set_ref` round-trips; `TtlSortedCache` shared-ref
  get-or-set delegation
- macro-output polish: ASCII-only macro diagnostics; no dead `#[doc]` on the
  function-local `in_impl` cache static; documented rationale for `#[allow(dead_code)]`
  on the generated prime companion and for the `crate_path()` `::cached` fallback

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 11, 2026
…ext major)

Next-major batch (after the redb DiskCache rewrite). Bundles a set of
breaking and additive changes plus the doc/test follow-ups.

Macros:
- `ttl_millis` attribute for sub-second TTLs, mutually exclusive with `ttl` (#149);
  rejected alongside a `create` block like the other store-builder attributes
- `force_refresh` attribute to bypass the cache per call, on all three macros
  (`#[cached]` / `#[concurrent_cached]` / `#[once]`); on `#[once]` it overwrites
  the single shared value (no per-call key, so no key-exclusion caveat). Combined
  with `result_fallback`, a force-refreshed `Err` still serves the previously
  cached `Ok`, and capturing that fallback leaves no read side effects on the
  bypassed entry (no TTL renewal, recency update, or hit-counter change) on both
  `#[cached]` and `#[concurrent_cached]` (#146)
- `in_impl` attribute to cache methods inside `impl` blocks; `self`-receiver
  methods require `in_impl` (a `convert` block alone cannot rescue them, since the
  cache static cannot live at `impl` scope) and emit a `{fn}_no_cache`
  cache-bypass sibling, hidden from rustdoc via `#[doc(hidden)]` (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)
- the shared `force_refresh` guard builder is factored into one helper

Traits and stores:
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear`, with
  `cache_reset` delegating to `cache_clear`; `RedisCacheBuilder` /
  `AsyncRedisCacheBuilder` `build()` reject an empty namespace+prefix scope
  (`EmptyScope`) so `cache_clear` cannot `SCAN MATCH *` the whole database (#200)
- `RedbCacheBuilder::build()` validates `cache_name` as a filename component,
  returning `InvalidCacheName` for a path separator (`/` or `\`) or a
  path-traversal component (`.` / `..`)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache, with
  matching methods on `LruTtlCache` and `ExpiringLruCache` (#180)
- `ConcurrentCloneCached` gains a non-renewing `cache_peek_with_expiry_status`
  (used to capture a `result_fallback` stale value without read side effects)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236), the latter demonstrating `in_impl`
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245); the CI tag-release git identity is scoped to `--local`
- fix a doctest under `--no-default-features` (#260)
- exclude internal dev tooling (.agents, .claude, .github, bin, docs/dev, AGENTS.md,
  CLAUDE.md) from the published crate via Cargo `exclude`
- dev tooling: pr-review shards reviewers across model-sized, randomized chunks
  (multiple of each type); pr-cycle fans fix application across disjoint sub-agents;
  AGENTS.md states README is generated from src/lib.rs via cargo-readme
- documentation accuracy pass: `ttl_millis` is valid with `result_fallback` and (on the
  in-memory path) needs `time_stores`; on `#[cached]` the "honored exactly" claim is
  scoped to the default in-memory store; Redis rounds `ttl_millis` up to the next whole
  second (500ms -> 1s, 1500ms -> 2s); `in_impl` emits a `#[doc(hidden)]` `{fn}_no_cache`
  sibling (documented on all three macros); `#[concurrent_cached]` `force_refresh`
  documents the `result_fallback` interaction; the `#[concurrent_cached]` attribute table
  reaches parity with `#[cached]` (ttl_millis / force_refresh / in_impl rows); wasm is
  incompatible with `redis_connection_manager` / `redis_async_cache`; the `in_impl`
  shared-cache footgun; the async `V: Sync` clone-elision asymmetry, including how a
  `Send + !Sync + !Clone` value surfaces at the generated set site; `CachedAsync`
  shared-ref default Send bounds; `Cached::cache_get_or_set_with` /
  `cache_try_get_or_set_with` flagged as provided defaults; migration-guide
  `&mut V` -> `&V` coercion caveat
- added coverage: async clone-elision clone-count assertions; `#[concurrent_cached]`
  `result_fallback` + `force_refresh` (including the no-read-side-effects bypass); `#[once]`
  `force_refresh`; `force_refresh` + `in_impl`; generic `in_impl` rejection goldens for
  `#[cached]` (free fn) and `#[concurrent_cached]` (in_impl method); compile-fail goldens
  for `force_refresh`-unparseable on `#[once]` / `#[concurrent_cached]`, generic
  `#[concurrent_cached]` without `convert`, and `ttl_millis` + `create` conflict;
  `_mut` trait coverage on `ExpiringCache` / `TtlSortedCache`; redis `cache_clear` /
  `cache_set_ref` and redb `cache_set_ref` round-trips; `TtlSortedCache` shared-ref
  get-or-set delegation
- macro-output polish: ASCII-only macro diagnostics; no dead `#[doc]` on the
  function-local `in_impl` cache static; documented rationale for `#[allow(dead_code)]`
  on the generated prime companion and for the `crate_path()` `::cached` fallback

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 13, 2026
…or + ttl consistency

Next-major batch (after the redb DiskCache rewrite). Bundles a set of breaking and
additive changes plus the doc/test follow-ups, including a consumer-experience pass
that made constructors and the ttl attribute consistent across the public surface.

Macros:
- `ttl` attribute now takes a `Duration` expression
  (`ttl = "core::time::Duration::from_secs(60)"`); the old whole-seconds integer
  form (`ttl = 60`) is removed and emits a migration error pointing at
  `ttl_secs`/`ttl_millis`
- `ttl_secs` attribute for whole-second TTLs and `ttl_millis` for sub-second TTLs;
  `ttl` / `ttl_secs` / `ttl_millis` are three-way mutually exclusive, and each is
  mutually exclusive with `expires` (#149)
- `force_refresh` attribute to bypass the cache per call, on all three macros
  (`#[cached]` / `#[concurrent_cached]` / `#[once]`); on `#[once]` it overwrites
  the single shared value (no per-call key, so no key-exclusion caveat). Combined
  with `result_fallback`, a force-refreshed `Err` still serves the previously
  cached `Ok`, and capturing that fallback leaves no read side effects on the
  bypassed entry (no TTL renewal, recency update, or hit-counter change) on both
  `#[cached]` and `#[concurrent_cached]` (#146)
- `in_impl` attribute to cache methods inside `impl` blocks; `self`-receiver
  methods require `in_impl` (a `convert` block alone cannot rescue them, since the
  cache static cannot live at `impl` scope) and emit a `{fn}_no_cache`
  cache-bypass sibling, hidden from rustdoc via `#[doc(hidden)]` (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)
- `refresh` is rejected alongside the other store-builder attributes when a
  `create` block is supplied, mirroring `#[concurrent_cached]`
- the shared `force_refresh` guard builder is factored into one helper

Traits and stores:
- every in-memory and sharded store gains a `new()` that returns a ready-to-use
  cache (zero-config stores take no args; required-field stores take them
  positionally - `LruCache::new(max_size)`, `TtlCache::new(ttl)`,
  `LruTtlCache::new(max_size, ttl)`, the `Sharded*` variants, etc.), all
  `#[must_use]`. `RedbCache` / `RedisCache` / `AsyncRedisCache` drop `new()` (it
  returned a builder, not a cache); use `builder(...)`. `new()` now consistently
  returns a usable cache everywhere it exists
- every TTL builder (non-sharded, sharded, Redis, Redb) gains `ttl_secs` /
  `ttl_millis` convenience setters alongside `ttl(Duration)`; last-writer-wins
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear`, with
  `cache_reset` delegating to `cache_clear`; `RedisCacheBuilder` /
  `AsyncRedisCacheBuilder` `build()` reject an empty namespace+prefix scope
  (`EmptyScope`) so `cache_clear` cannot `SCAN MATCH *` the whole database (#200)
- `RedbCacheBuilder::build()` validates `cache_name` as a filename component,
  returning `InvalidCacheName` for a path separator (`/` or `\`) or a
  path-traversal component (`.` / `..`)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache, with
  matching methods on `LruTtlCache` and `ExpiringLruCache` (#180)
- `ConcurrentCloneCached` gains a non-renewing `cache_peek_with_expiry_status`
  (used to capture a `result_fallback` stale value without read side effects)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- docs and examples now prefer the short alias method API
  (`get`/`set`/`remove`/`clear`/`len`/...) over the `cache_*`-prefixed forms, with
  a note (in the crate docs and on the `Cached` trait) on when to use the `cache_*`
  names - a collision with another in-scope trait's method. Both forms remain; no
  method was removed
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236), the latter demonstrating `in_impl` and
  noting the in_impl cache is not externally invalidatable and its shared-id
  staleness footgun
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245); it retries a missing release when the tag
  already exists on the remote; the CI tag-release git identity is scoped to `--local`
- fix a doctest under `--no-default-features` (#260)
- exclude internal dev tooling (.agents, .claude, .github, bin, docs/dev, AGENTS.md,
  CLAUDE.md) from the published crate via Cargo `exclude`
- README is generated from src/lib.rs via cargo-readme (regenerated here); dev
  tooling: pr-review shards reviewers across model-sized randomized chunks; pr-cycle
  fans fix application across disjoint sub-agents
- documentation accuracy pass: `ttl_millis` / `ttl_secs` are valid with
  `result_fallback` and (on the in-memory path) need `time_stores`; on `#[cached]`
  the "honored exactly" claim is scoped to the default in-memory store; Redis rounds
  `ttl_millis` up to the next whole second (500ms -> 1s, 1500ms -> 2s); `in_impl`
  emits a `#[doc(hidden)]` `{fn}_no_cache` sibling (all three macros);
  `#[concurrent_cached]` `force_refresh` documents the `result_fallback` interaction;
  the `#[concurrent_cached]` attribute table reaches parity with `#[cached]`; wasm is
  incompatible with `redis_connection_manager` / `redis_async_cache`; the async
  `V: Sync` clone-elision asymmetry and how a `Send + !Sync + !Clone` value surfaces
  at the generated set site; `CachedAsync` shared-ref default Send bounds;
  `Cached::cache_get_or_set_with` / `cache_try_get_or_set_with` flagged as provided
  defaults; migration-guide `&mut V` -> `&V` coercion caveat; redis `build()` lists
  `InvalidTtl` before `EmptyScope` to match validation order
- macro-output polish: ASCII-only macro diagnostics; no dead `#[doc]` on the
  function-local `in_impl` cache static; documented rationale for `#[allow(dead_code)]`
  on the generated prime companion and for the `crate_path()` `::cached` fallback

Tests:
- positive macro coverage for all three ttl spellings (`ttl` Duration expr /
  `ttl_secs` / `ttl_millis`) across `#[cached]` / `#[once]` / `#[concurrent_cached]`,
  and compile-fail UI goldens for the three-way exclusivity and the `ttl = <int>`
  migration error
- inline store coverage for every new `new()` constructor and the `ttl_secs` /
  `ttl_millis` builder setters (including override semantics)
- async clone-elision clone-count assertions; `#[concurrent_cached]`
  `result_fallback` + `force_refresh` (including the no-read-side-effects bypass);
  `#[once]` `force_refresh`; `force_refresh` + `in_impl`; generic-rejection goldens;
  `_mut` trait coverage on `ExpiringCache` / `TtlSortedCache`; redis `cache_clear` /
  `cache_set_ref` and redb `cache_set_ref` round-trips; deferred attribute/store
  combinations (max_size+ttl_millis, in_impl+ttl_millis, ttl_millis+force_refresh,
  result_fallback+ttl_millis, async once in_impl) and store(0) counter resets;
  sharded peek does not renew TTL under refresh_on_hit

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 14, 2026
…or + ttl consistency

Next-major batch (after the redb DiskCache rewrite). Bundles a set of breaking and
additive changes plus the doc/test follow-ups, including a consumer-experience pass
that made constructors and the ttl attribute consistent across the public surface.

Macros:
- `ttl` attribute now takes a `Duration` expression
  (`ttl = "core::time::Duration::from_secs(60)"`); the old whole-seconds integer
  form (`ttl = 60`) is removed and emits a migration error pointing at
  `ttl_secs`/`ttl_millis`
- `ttl_secs` attribute for whole-second TTLs and `ttl_millis` for sub-second TTLs;
  `ttl` / `ttl_secs` / `ttl_millis` are three-way mutually exclusive, and each is
  mutually exclusive with `expires` (#149)
- `force_refresh` attribute to bypass the cache per call, on all three macros
  (`#[cached]` / `#[concurrent_cached]` / `#[once]`); on `#[once]` it overwrites
  the single shared value (no per-call key, so no key-exclusion caveat). Combined
  with `result_fallback`, a force-refreshed `Err` still serves the previously
  cached `Ok`, and capturing that fallback leaves no read side effects on the
  bypassed entry (no TTL renewal, recency update, or hit-counter change) on both
  `#[cached]` and `#[concurrent_cached]` (#146)
- `in_impl` attribute to cache methods inside `impl` blocks; `self`-receiver
  methods require `in_impl` (a `convert` block alone cannot rescue them, since the
  cache static cannot live at `impl` scope) and emit a `{fn}_no_cache`
  cache-bypass sibling, hidden from rustdoc via `#[doc(hidden)]` (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)
- `refresh` is rejected alongside the other store-builder attributes when a
  `create` block is supplied, mirroring `#[concurrent_cached]`
- the shared `force_refresh` guard builder is factored into one helper

Traits and stores:
- every in-memory and sharded store gains a `new()` that returns a ready-to-use
  cache (zero-config stores take no args; required-field stores take them
  positionally - `LruCache::new(max_size)`, `TtlCache::new(ttl)`,
  `LruTtlCache::new(max_size, ttl)`, the `Sharded*` variants, etc.), all
  `#[must_use]`. `RedbCache` / `RedisCache` / `AsyncRedisCache` drop `new()` (it
  returned a builder, not a cache); use `builder(...)`. `new()` now consistently
  returns a usable cache everywhere it exists
- `new()` / `builder()` on each sharded `*Base` type are now defined only on the
  default-hasher specialization (`*Base<K, V, DefaultShardHasher>`, the named
  alias). They previously sat on the generic `*Base<K, V, H>` impl but always
  returned a `DefaultShardHasher` builder, so a `*Base::<_, _, CustomHasher>::
  builder()` / `::new()` turbofish compiled yet silently dropped the custom
  hasher; that turbofish now fails to compile (E0599). A custom hasher is
  specified via `ShardedX::builder().hasher(h)`, which switches the builder's
  hasher type; alias-based construction and the `.hasher()` path are unchanged
- every TTL builder (non-sharded, sharded, Redis, Redb) gains `ttl_secs` /
  `ttl_millis` convenience setters alongside `ttl(Duration)`; last-writer-wins
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear`, with
  `cache_reset` delegating to `cache_clear`; `RedisCacheBuilder` /
  `AsyncRedisCacheBuilder` `build()` reject an empty namespace+prefix scope
  (`EmptyScope`) so `cache_clear` cannot `SCAN MATCH *` the whole database (#200)
- `RedbCacheBuilder::build()` validates `cache_name` as a filename component,
  returning `InvalidCacheName` for a path separator (`/` or `\`) or a
  path-traversal component (`.` / `..`)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache, with
  matching methods on `LruTtlCache` and `ExpiringLruCache` (#180)
- `ConcurrentCloneCached` gains a non-renewing `cache_peek_with_expiry_status`
  (used to capture a `result_fallback` stale value without read side effects)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- docs and examples now prefer the short alias method API
  (`get`/`set`/`remove`/`clear`/`len`/...) over the `cache_*`-prefixed forms, with
  a note (in the crate docs and on the `Cached` trait) on when to use the `cache_*`
  names - a collision with another in-scope trait's method. Both forms remain; no
  method was removed
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236), the latter demonstrating `in_impl` and
  noting the in_impl cache is not externally invalidatable and its shared-id
  staleness footgun
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245); it retries a missing release when the tag
  already exists on the remote; the CI tag-release git identity is scoped to `--local`
- fix a doctest under `--no-default-features` (#260)
- exclude internal dev tooling (.agents, .claude, .github, bin, docs/dev, AGENTS.md,
  CLAUDE.md) from the published crate via Cargo `exclude`
- README is generated from src/lib.rs via cargo-readme (regenerated here); dev
  tooling: pr-review shards reviewers across model-sized randomized chunks; pr-cycle
  fans fix application across disjoint sub-agents
- documentation accuracy pass: `ttl_millis` / `ttl_secs` are valid with
  `result_fallback` and (on the in-memory path) need `time_stores`; on `#[cached]`
  the "honored exactly" claim is scoped to the default in-memory store; Redis rounds
  `ttl_millis` up to the next whole second (500ms -> 1s, 1500ms -> 2s); `in_impl`
  emits a `#[doc(hidden)]` `{fn}_no_cache` sibling (all three macros);
  `#[concurrent_cached]` `force_refresh` documents the `result_fallback` interaction;
  the `#[concurrent_cached]` attribute table reaches parity with `#[cached]`; wasm is
  incompatible with `redis_connection_manager` / `redis_async_cache`; the async
  `V: Sync` clone-elision asymmetry and how a `Send + !Sync + !Clone` value surfaces
  at the generated set site; `CachedAsync` shared-ref default Send bounds;
  `Cached::cache_get_or_set_with` / `cache_try_get_or_set_with` flagged as provided
  defaults; migration-guide `&mut V` -> `&V` coercion caveat; redis `build()` lists
  `InvalidTtl` before `EmptyScope` to match validation order
- macro-output polish: ASCII-only macro diagnostics; no dead `#[doc]` on the
  function-local `in_impl` cache static; documented rationale for `#[allow(dead_code)]`
  on the generated prime companion and for the `crate_path()` `::cached` fallback
- elevate all compiler warnings to errors workspace-wide via the Cargo `[lints]`
  table (`[workspace.lints.rust] warnings = "deny"`, opted into by the `cached`
  and `cached_proc_macro` packages), so `cargo build`/`test`/`clippy` deny
  warnings rather than only the Makefile clippy target; gate `validate_ttl` to
  `any(time_stores, disk_store, redis_store)` and an unused test `AtomicUsize`
  import to `proc_macro` to keep `--no-default-features` warning-clean

Tests:
- positive macro coverage for all three ttl spellings (`ttl` Duration expr /
  `ttl_secs` / `ttl_millis`) across `#[cached]` / `#[once]` / `#[concurrent_cached]`,
  and compile-fail UI goldens for the three-way exclusivity and the `ttl = <int>`
  migration error
- inline store coverage for every new `new()` constructor and the `ttl_secs` /
  `ttl_millis` builder setters (including override semantics)
- compile-fail UI golden asserting `*Base::<_, _, CustomHasher>::{new,builder}()`
  no longer compiles (the custom-hasher `.hasher()` path stays covered by the
  existing sharded `custom_hasher` unit test)
- async clone-elision clone-count assertions; `#[concurrent_cached]`
  `result_fallback` + `force_refresh` (including the no-read-side-effects bypass);
  `#[once]` `force_refresh`; `force_refresh` + `in_impl`; generic-rejection goldens;
  `_mut` trait coverage on `ExpiringCache` / `TtlSortedCache`; redis `cache_clear` /
  `cache_set_ref` and redb `cache_set_ref` round-trips; deferred attribute/store
  combinations (max_size+ttl_millis, in_impl+ttl_millis, ttl_millis+force_refresh,
  result_fallback+ttl_millis, async once in_impl) and store(0) counter resets;
  sharded peek does not renew TTL under refresh_on_hit

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 20, 2026
…or + ttl consistency

Next-major batch (after the redb DiskCache rewrite). Bundles a set of breaking and
additive changes plus the doc/test follow-ups, including a consumer-experience pass
that made constructors and the ttl attribute consistent across the public surface.

Macros:
- `ttl` attribute now takes a `Duration` expression
  (`ttl = "core::time::Duration::from_secs(60)"`); the old whole-seconds integer
  form (`ttl = 60`) is removed and emits a migration error pointing at
  `ttl_secs`/`ttl_millis`
- `ttl_secs` attribute for whole-second TTLs and `ttl_millis` for sub-second TTLs;
  `ttl` / `ttl_secs` / `ttl_millis` are three-way mutually exclusive, and each is
  mutually exclusive with `expires` (#149)
- `force_refresh` attribute to bypass the cache per call, on all three macros
  (`#[cached]` / `#[concurrent_cached]` / `#[once]`); on `#[once]` it overwrites
  the single shared value (no per-call key, so no key-exclusion caveat). Combined
  with `result_fallback`, a force-refreshed `Err` still serves the previously
  cached `Ok`, and capturing that fallback leaves no read side effects on the
  bypassed entry (no TTL renewal, recency update, or hit-counter change) on both
  `#[cached]` and `#[concurrent_cached]` (#146)
- `in_impl` attribute to cache methods inside `impl` blocks; `self`-receiver
  methods require `in_impl` (a `convert` block alone cannot rescue them, since the
  cache static cannot live at `impl` scope) and emit a `{fn}_no_cache`
  cache-bypass sibling, hidden from rustdoc via `#[doc(hidden)]` (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)
- `refresh` is rejected alongside the other store-builder attributes when a
  `create` block is supplied, mirroring `#[concurrent_cached]`
- the shared `force_refresh` guard builder is factored into one helper

Traits and stores:
- every in-memory and sharded store gains a `new()` that returns a ready-to-use
  cache (zero-config stores take no args; required-field stores take them
  positionally - `LruCache::new(max_size)`, `TtlCache::new(ttl)`,
  `LruTtlCache::new(max_size, ttl)`, the `Sharded*` variants, etc.), all
  `#[must_use]`. `RedbCache` / `RedisCache` / `AsyncRedisCache` drop `new()` (it
  returned a builder, not a cache); use `builder(...)`. `new()` now consistently
  returns a usable cache everywhere it exists
- `new()` / `builder()` on each sharded `*Base` type are now defined only on the
  default-hasher specialization (`*Base<K, V, DefaultShardHasher>`, the named
  alias). They previously sat on the generic `*Base<K, V, H>` impl but always
  returned a `DefaultShardHasher` builder, so a `*Base::<_, _, CustomHasher>::
  builder()` / `::new()` turbofish compiled yet silently dropped the custom
  hasher; that turbofish now fails to compile (E0599). A custom hasher is
  specified via `ShardedX::builder().hasher(h)`, which switches the builder's
  hasher type; alias-based construction and the `.hasher()` path are unchanged
- every TTL builder (non-sharded, sharded, Redis, Redb) gains `ttl_secs` /
  `ttl_millis` convenience setters alongside `ttl(Duration)`; last-writer-wins
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear`, with
  `cache_reset` delegating to `cache_clear`; `RedisCacheBuilder` /
  `AsyncRedisCacheBuilder` `build()` reject an empty namespace+prefix scope
  (`EmptyScope`) so `cache_clear` cannot `SCAN MATCH *` the whole database (#200)
- `RedbCacheBuilder::build()` validates `cache_name` as a filename component,
  returning `InvalidCacheName` for a path separator (`/` or `\`) or a
  path-traversal component (`.` / `..`)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache, with
  matching methods on `LruTtlCache` and `ExpiringLruCache` (#180)
- `ConcurrentCloneCached` gains a non-renewing `cache_peek_with_expiry_status`
  (used to capture a `result_fallback` stale value without read side effects)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- docs and examples now prefer the short alias method API
  (`get`/`set`/`remove`/`clear`/`len`/...) over the `cache_*`-prefixed forms, with
  a note (in the crate docs and on the `Cached` trait) on when to use the `cache_*`
  names - a collision with another in-scope trait's method. Both forms remain; no
  method was removed
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236), the latter demonstrating `in_impl` and
  noting the in_impl cache is not externally invalidatable and its shared-id
  staleness footgun
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245); it retries a missing release when the tag
  already exists on the remote; the CI tag-release git identity is scoped to `--local`
- fix a doctest under `--no-default-features` (#260)
- exclude internal dev tooling (.agents, .claude, .github, bin, docs/dev, AGENTS.md,
  CLAUDE.md) from the published crate via Cargo `exclude`
- README is generated from src/lib.rs via cargo-readme (regenerated here); dev
  tooling: pr-review shards reviewers across model-sized randomized chunks; pr-cycle
  fans fix application across disjoint sub-agents
- documentation accuracy pass: `ttl_millis` / `ttl_secs` are valid with
  `result_fallback` and (on the in-memory path) need `time_stores`; on `#[cached]`
  the "honored exactly" claim is scoped to the default in-memory store; Redis rounds
  `ttl_millis` up to the next whole second (500ms -> 1s, 1500ms -> 2s); `in_impl`
  emits a `#[doc(hidden)]` `{fn}_no_cache` sibling (all three macros);
  `#[concurrent_cached]` `force_refresh` documents the `result_fallback` interaction;
  the `#[concurrent_cached]` attribute table reaches parity with `#[cached]`; wasm is
  incompatible with `redis_connection_manager` / `redis_async_cache`; the async
  `V: Sync` clone-elision asymmetry and how a `Send + !Sync + !Clone` value surfaces
  at the generated set site; `CachedAsync` shared-ref default Send bounds;
  `Cached::cache_get_or_set_with` / `cache_try_get_or_set_with` flagged as provided
  defaults; migration-guide `&mut V` -> `&V` coercion caveat; redis `build()` lists
  `InvalidTtl` before `EmptyScope` to match validation order
- macro-output polish: ASCII-only macro diagnostics; no dead `#[doc]` on the
  function-local `in_impl` cache static; documented rationale for `#[allow(dead_code)]`
  on the generated prime companion and for the `crate_path()` `::cached` fallback
- elevate all compiler warnings to errors workspace-wide via the Cargo `[lints]`
  table (`[workspace.lints.rust] warnings = "deny"`, opted into by the `cached`
  and `cached_proc_macro` packages), so `cargo build`/`test`/`clippy` deny
  warnings rather than only the Makefile clippy target; gate `validate_ttl` to
  `any(time_stores, disk_store, redis_store)` and an unused test `AtomicUsize`
  import to `proc_macro` to keep `--no-default-features` warning-clean

Tests:
- positive macro coverage for all three ttl spellings (`ttl` Duration expr /
  `ttl_secs` / `ttl_millis`) across `#[cached]` / `#[once]` / `#[concurrent_cached]`,
  and compile-fail UI goldens for the three-way exclusivity and the `ttl = <int>`
  migration error
- inline store coverage for every new `new()` constructor and the `ttl_secs` /
  `ttl_millis` builder setters (including override semantics)
- compile-fail UI golden asserting `*Base::<_, _, CustomHasher>::{new,builder}()`
  no longer compiles (the custom-hasher `.hasher()` path stays covered by the
  existing sharded `custom_hasher` unit test)
- async clone-elision clone-count assertions; `#[concurrent_cached]`
  `result_fallback` + `force_refresh` (including the no-read-side-effects bypass);
  `#[once]` `force_refresh`; `force_refresh` + `in_impl`; generic-rejection goldens;
  `_mut` trait coverage on `ExpiringCache` / `TtlSortedCache`; redis `cache_clear` /
  `cache_set_ref` and redb `cache_set_ref` round-trips; deferred attribute/store
  combinations (max_size+ttl_millis, in_impl+ttl_millis, ttl_millis+force_refresh,
  result_fallback+ttl_millis, async once in_impl) and store(0) counter resets;
  sharded peek does not renew TTL under refresh_on_hit

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
jaemk added a commit that referenced this pull request Jun 20, 2026
…sistency

Make constructors and the ttl attribute consistent across the public surface, on
top of the redb DiskCache rewrite. Breaking and additive changes with doc and test
follow-ups:

Macros:
- `ttl` attribute now takes a `Duration` expression
  (`ttl = "core::time::Duration::from_secs(60)"`); the old whole-seconds integer
  form (`ttl = 60`) is removed and emits a migration error pointing at
  `ttl_secs`/`ttl_millis`
- `ttl_secs` attribute for whole-second TTLs and `ttl_millis` for sub-second TTLs;
  `ttl` / `ttl_secs` / `ttl_millis` are three-way mutually exclusive, and each is
  mutually exclusive with `expires` (#149)
- `force_refresh` attribute to bypass the cache per call, on all three macros
  (`#[cached]` / `#[concurrent_cached]` / `#[once]`); on `#[once]` it overwrites
  the single shared value (no per-call key, so no key-exclusion caveat). Combined
  with `result_fallback`, a force-refreshed `Err` still serves the previously
  cached `Ok`, and capturing that fallback leaves no read side effects on the
  bypassed entry (no TTL renewal, recency update, or hit-counter change) on both
  `#[cached]` and `#[concurrent_cached]` (#146)
- `in_impl` attribute to cache methods inside `impl` blocks; `self`-receiver
  methods require `in_impl` (a `convert` block alone cannot rescue them, since the
  cache static cannot live at `impl` scope) and emit a `{fn}_no_cache`
  cache-bypass sibling, hidden from rustdoc via `#[doc(hidden)]` (#16, #140)
- reference arguments (`&T`, `Option<&T>`) form the default key without `convert` (#202, #203)
- generated code resolves the crate path via `proc-macro-crate`, so a renamed or
  re-exported `cached` dependency works (#157)
- macro-introduced bindings are hygienically named, so arguments named `key` /
  `cache` / `result` no longer collide with generated code (#230, #114)
- clear compile error for generic functions used without `key` + `convert` (#80)
- `refresh` is rejected alongside the other store-builder attributes when a
  `create` block is supplied, mirroring `#[concurrent_cached]`
- the shared `force_refresh` guard builder is factored into one helper

Traits and stores:
- every in-memory and sharded store gains a `new()` that returns a ready-to-use
  cache (zero-config stores take no args; required-field stores take them
  positionally - `LruCache::new(max_size)`, `TtlCache::new(ttl)`,
  `LruTtlCache::new(max_size, ttl)`, the `Sharded*` variants, etc.), all
  `#[must_use]`. `RedbCache` / `RedisCache` / `AsyncRedisCache` drop `new()` (it
  returned a builder, not a cache); use `builder(...)`. `new()` now consistently
  returns a usable cache everywhere it exists
- `new()` / `builder()` on each sharded `*Base` type are now defined only on the
  default-hasher specialization (`*Base<K, V, DefaultShardHasher>`, the named
  alias). They previously sat on the generic `*Base<K, V, H>` impl but always
  returned a `DefaultShardHasher` builder, so a `*Base::<_, _, CustomHasher>::
  builder()` / `::new()` turbofish compiled yet silently dropped the custom
  hasher; that turbofish now fails to compile (E0599). A custom hasher is
  specified via `ShardedX::builder().hasher(h)`, which switches the builder's
  hasher type; alias-based construction and the `.hasher()` path are unchanged
- every TTL builder (non-sharded, sharded, Redis, Redb) gains `ttl_secs` /
  `ttl_millis` convenience setters alongside `ttl(Duration)`; last-writer-wins
- `get_or_set_with` / `try_get_or_set_with` (and the async variants) now return
  `&V` instead of `&mut V`, with new `*_mut` variants preserving the old behavior (#179)
- new additive `SerializeCached` / `SerializeCachedAsync` traits (`cache_set_ref`)
  for serialize-backed stores; `#[concurrent_cached]` routes its set through an
  autoref shim that uses the borrowed setter for any store implementing the trait
  (built-in redis/disk or a custom `ty`/`create` store), avoiding a value clone (#196, #195)
- `RedisCache` / `AsyncRedisCache` gain `cache_clear` / `async_cache_clear`, with
  `cache_reset` delegating to `cache_clear`; `RedisCacheBuilder` /
  `AsyncRedisCacheBuilder` `build()` reject an empty namespace+prefix scope
  (`EmptyScope`) so `cache_clear` cannot `SCAN MATCH *` the whole database (#200)
- `RedbCacheBuilder::build()` validates `cache_name` as a filename component,
  returning `InvalidCacheName` for a path separator (`/` or `\`) or a
  path-traversal component (`.` / `..`)
- `LruCache::set_max_size` / `try_set_max_size` for resizing a live cache, with
  matching methods on `LruTtlCache` and `ExpiringLruCache` (#180)
- `ConcurrentCloneCached` gains a non-renewing `cache_peek_with_expiry_status`
  (used to capture a `result_fallback` stale value without read side effects)

Redis:
- TLS features split so rustls is selectable; `redis_tokio` / `redis_smol` no
  longer imply native-tls (add `*_native_tls` or `*_rustls`) (#231)

Docs and process:
- docs and examples now prefer the short alias method API
  (`get`/`set`/`remove`/`clear`/`len`/...) over the `cache_*`-prefixed forms, with
  a note (in the crate docs and on the `Cached` trait) on when to use the `cache_*`
  names - a collision with another in-scope trait's method. Both forms remain; no
  method was removed
- document floats as the canonical `convert` case (#78); add cache-invalidation
  and struct-method examples (#21, #236), the latter demonstrating `in_impl` and
  noting the in_impl cache is not externally invalidatable and its shared-id
  staleness footgun
- release workflow tags and creates a GitHub release for each published workspace
  crate on publish, via `bin/tag-release.sh` (root `cached` -> `vX.Y.Z`, subcrates
  -> `<crate-name>-vX.Y.Z`) (#245); it retries a missing release when the tag
  already exists on the remote; the CI tag-release git identity is scoped to `--local`
- fix a doctest under `--no-default-features` (#260)
- exclude internal dev tooling (.agents, .claude, .github, bin, docs/dev, AGENTS.md,
  CLAUDE.md) from the published crate via Cargo `exclude`
- README is generated from src/lib.rs via cargo-readme (regenerated here); dev
  tooling: pr-review shards reviewers across model-sized randomized chunks; pr-cycle
  fans fix application across disjoint sub-agents
- documentation accuracy pass: `ttl_millis` / `ttl_secs` are valid with
  `result_fallback` and (on the in-memory path) need `time_stores`; on `#[cached]`
  the "honored exactly" claim is scoped to the default in-memory store; Redis rounds
  `ttl_millis` up to the next whole second (500ms -> 1s, 1500ms -> 2s); `in_impl`
  emits a `#[doc(hidden)]` `{fn}_no_cache` sibling (all three macros);
  `#[concurrent_cached]` `force_refresh` documents the `result_fallback` interaction;
  the `#[concurrent_cached]` attribute table reaches parity with `#[cached]`; wasm is
  incompatible with `redis_connection_manager` / `redis_async_cache`; the async
  `V: Sync` clone-elision asymmetry and how a `Send + !Sync + !Clone` value surfaces
  at the generated set site; `CachedAsync` shared-ref default Send bounds;
  `Cached::cache_get_or_set_with` / `cache_try_get_or_set_with` flagged as provided
  defaults; migration-guide `&mut V` -> `&V` coercion caveat; redis `build()` lists
  `InvalidTtl` before `EmptyScope` to match validation order
- macro-output polish: ASCII-only macro diagnostics; no dead `#[doc]` on the
  function-local `in_impl` cache static; documented rationale for `#[allow(dead_code)]`
  on the generated prime companion and for the `crate_path()` `::cached` fallback
- elevate all compiler warnings to errors workspace-wide via the Cargo `[lints]`
  table (`[workspace.lints.rust] warnings = "deny"`, opted into by the `cached`
  and `cached_proc_macro` packages), so `cargo build`/`test`/`clippy` deny
  warnings rather than only the Makefile clippy target; gate `validate_ttl` to
  `any(time_stores, disk_store, redis_store)` and an unused test `AtomicUsize`
  import to `proc_macro` to keep `--no-default-features` warning-clean

Tests:
- positive macro coverage for all three ttl spellings (`ttl` Duration expr /
  `ttl_secs` / `ttl_millis`) across `#[cached]` / `#[once]` / `#[concurrent_cached]`,
  and compile-fail UI goldens for the three-way exclusivity and the `ttl = <int>`
  migration error
- inline store coverage for every new `new()` constructor and the `ttl_secs` /
  `ttl_millis` builder setters (including override semantics)
- compile-fail UI golden asserting `*Base::<_, _, CustomHasher>::{new,builder}()`
  no longer compiles (the custom-hasher `.hasher()` path stays covered by the
  existing sharded `custom_hasher` unit test)
- async clone-elision clone-count assertions; `#[concurrent_cached]`
  `result_fallback` + `force_refresh` (including the no-read-side-effects bypass);
  `#[once]` `force_refresh`; `force_refresh` + `in_impl`; generic-rejection goldens;
  `_mut` trait coverage on `ExpiringCache` / `TtlSortedCache`; redis `cache_clear` /
  `cache_set_ref` and redb `cache_set_ref` round-trips; deferred attribute/store
  combinations (max_size+ttl_millis, in_impl+ttl_millis, ttl_millis+force_refresh,
  result_fallback+ttl_millis, async once in_impl) and store(0) counter resets;
  sharded peek does not renew TTL under refresh_on_hit

See docs/migrations/2.0-to-unreleased.md and the CHANGELOG for migration details.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cache clear operation

4 participants