Skip to content

feat(cache-manager): add add() for atomic store-if-absent of application values - #93

Merged
allen0099 merged 1 commit into
masterfrom
feat/cache-manager-add
Sep 25, 2026
Merged

allen0099 merged 1 commit into
masterfrom
feat/cache-manager-add

Conversation

@allen0099

Copy link
Copy Markdown
Owner

Closes #65.

Change

CacheManager.add(key, value, ttl=None) -> bool stores a JSON value only when the key is free and returns whether it did.

  • It uses the same key prefix, JSON encoding and default_ttl handling as set(). A non-JSON-serializable value raises TypeError.
  • It calls backend.set_if_absent (Add an atomic set-if-absent (SET NX EX) to BaseCacheBackend for lock / slot acquisition #62), so the check and the write are one atomic operation on Memory, Redis and Memcached. Of several concurrent callers, exactly one gets True.
  • An expired key counts as free. A key that holds an undecodable value counts as present, so add() returns False for it, even though get() and get_or_set() treat it as a miss. The docstring and the guide both say so.
  • set() and add() now share a private _encode() helper that builds the JSON CacheEntry.

Docs:

  • docs/APP_CACHE.md has an example and a behavior bullet, and the Memcached note now lists add().
  • The README features line mentions add().
  • CLAUDE.md is updated.
  • [Unreleased] in CHANGELOG.md has an Added entry.

Tests

The new tests in tests/test_cache_manager.py run against Memory, and against Redis when a test server is configured:

  • The value is stored when the key is free.
  • An existing value is never overwritten.
  • 20 concurrent add() calls: exactly one succeeds, and the stored value is the winner's.
  • An explicit ttl applies, and the key can be added again after it lapses.
  • default_ttl is used when no ttl is given.
  • A corrupted entry counts as present.
  • TypeError is raised and the key is left free.

Local results:

  • Full suite against the throwaway Redis (6380) and Memcached (11212) test servers: 689 passed, 1 skipped. Coverage is 100%, including manager.py.
  • ruff, mypy --strict and zensical build --strict are clean.

…ion values

CacheManager.add(key, value, ttl=None) -> bool stores a JSON value only when
the key is free and reports whether it did. It runs on the backend's
set_if_absent, so of several concurrent callers exactly one wins, which
makes once-per-key work (webhook or email deduplication) race-free.

set() and add() now share one _encode() helper for the JSON entry.

Closes #65
@allen0099
allen0099 merged commit b08881e into master Sep 25, 2026
10 checks passed
@allen0099
allen0099 deleted the feat/cache-manager-add branch September 25, 2026 08:52
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.

Add CacheManager.add() for atomic store-if-absent of application values

1 participant