Skip to content

feat(backends): add aclose() and async with to every backend - #308

Merged
allen0099 merged 1 commit into
masterfrom
feat/backend-aclose
Sep 27, 2026
Merged

allen0099 merged 1 commit into
masterfrom
feat/backend-aclose

Conversation

@allen0099

@allen0099 allen0099 commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • BaseCacheBackend gets a no-op aclose() (not abstract, so third-party backends keep working) plus __aenter__ (returns Self) / __aexit__ (calls aclose()). Every backend can now be used with async with.
  • AsyncRedisCacheBackend.aclose() awaits client.aclose(). That closes the connection pool the client created. A pool the caller passes as connection_pool= stays open, which is redis-py's own ownership rule (auto_close_connection_pool is False for a supplied pool). The types-redis attr-defined ignore now lives inside the backend.
  • MemcachedBackend.aclose() runs HashClient.close() in a worker thread. For a pooled client that calls ObjectPool.clear(), which closes both free and in-use sockets on every server.
  • Both are idempotent. A backend used after aclose() reconnects (redis-py and pymemcache both reopen connections on demand), so it needs another aclose(). This is documented.
  • examples/redis_backend.py now calls await backend.aclose() and no longer needs the # type: ignore. mypy --strict examples/redis_backend.py is clean.
  • The autouse close_network_clients fixture now calls backend.aclose(). It still skips mocked clients.
  • Docs: new "Closing a backend" section in docs/BACKENDS.md and the zh-TW mirror. It covers the lifespan pattern for every backend, async with, the ownership and reuse rules, and the fact that BackendProxy.set(None) does not close anything. The MemoryBackend section links to it.

Not in scope: BackendProxy does not close a backend when it is replaced or unset.

Changelog

changelog.d/243.added.md

Tests

  • Unit tests: base no-op, async with return value, closing when the body raises, Redis/Memcached with mocked clients, a repeated aclose() on unconnected real clients, and the Redis caller-supplied pool left open. The pool test fails if close_connection_pool=True is forced.
  • Live Redis and Memcached tests: pooled connections and sockets are disconnected after aclose(), and a second call is safe.
  • Full suite with live servers: 1138 passed, 1 skipped, coverage 99.97%. Without live servers: 946 passed, 193 skipped.

Closes #243

BaseCacheBackend gains a no-op aclose() plus __aenter__/__aexit__.
AsyncRedisCacheBackend closes its redis-py client (a caller-supplied
connection_pool stays open, as in redis-py) and MemcachedBackend closes
every pooled socket in a worker thread. The redis example and the test
fixture that closes network clients now go through backend.aclose().
@allen0099 allen0099 added this to the 0.3.9 milestone Sep 27, 2026
@allen0099
allen0099 merged commit 0fbac8f into master Sep 27, 2026
12 checks passed
@allen0099
allen0099 deleted the feat/backend-aclose branch September 27, 2026 13:51
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 aclose() to BaseCacheBackend and the Redis and Memcached backends

1 participant