fix(cache-manager): match key_prefix literally in clear_pattern - #316
Merged
Merged
Conversation
clear_pattern passed key_prefix + pattern to the backend as one glob, so glob characters in the manager's own prefix were live: cache[1]: missed its own keys and a?: also cleared ab:'s. A prefix free of glob metacharacters keeps the backend's native clear_pattern; any other prefix lists every key, matches the prefix literally and the remainder with fnmatchcase, and deletes via delete_many. The constructor warns about such a prefix.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
CacheManager.clear_pattern(pattern)passedkey_prefix + patterntobackend.clear_pattern()as a single glob, so glob characters in the manager's own prefix were live:CacheManager(key_prefix="cache[1]:").clear_pattern("*")missed its own keys.key_prefix="a?:"also cleared the keys of a manager with prefixab:.This PR implements the hybrid option the maintainer chose:
Fast path, unchanged. A
key_prefixwithout*,?,[,]or\still callsbackend.clear_pattern(key_prefix + pattern), e.g. RedisSCAN MATCH. The argument is the same as before.Slow path. Any other prefix:
get_all_keys()lists every key;key_prefixliterally and whose remainder matchespatternunderfnmatch.fnmatchcase;delete_many()deletes them;On this path
patternis fnmatch syntax, not Redis glob: case-sensitive, no backslash escapes,[!a]for negation. This is documented in the docstring and the docs.UserWarningat construction whenkey_prefixcontains a glob metacharacter. The message saysclear_pattern()will list and filter in Python, which is slower on Redis, and suggests a prefix without*?[]\.stacklevel=2points it at the caller's line. The only internal constructor isget_app_cache(), which uses the defaultcache:prefix, so the warning never fires from library code.Memcached. Both paths return 0 with exactly one
RuntimeWarning. On the slow path it comes fromget_all_keys(), anddelete_many()of nothing is silent.Scope. The metacharacter set mirrors
_GLOB_SPECIALinbackends/redis.pyas a private constant, so no backend API is added. Themanager.pychange is confined to__init__,clear_patternand a small private helper.Behaviour changes
cache[1]:) or fewer keys (a?:) than before.Tests
New file
tests/test_cache_manager_clear_pattern.py, run on memory and live Redis where applicable:backend.clear_pattern("cache:user:*")and never enumerates keys;cache[1]:clears its own keys but notcache1:a;a?:leaves theab:manager's keys alone;?,*and[!u]in the pattern part still work as a glob on the slow path, case-sensitively;RuntimeWarningon both paths.Full suite incl. live Redis/Memcached tests: 1204 passed, 1 skipped, coverage 99% (
manager.py100%). ruff, ruff format and mypy --strict are clean.Changelog
changelog.d/140.fixed.mdCloses #140