diff --git a/authentik/core/migrations/0018_auto_20210330_1345_squashed_0028_alter_token_intent.py b/authentik/core/migrations/0018_auto_20210330_1345_squashed_0028_alter_token_intent.py index 4f2b47817..2d934c9bd 100644 --- a/authentik/core/migrations/0018_auto_20210330_1345_squashed_0028_alter_token_intent.py +++ b/authentik/core/migrations/0018_auto_20210330_1345_squashed_0028_alter_token_intent.py @@ -19,7 +19,8 @@ def migrate_sessions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): from django.contrib.sessions.backends.cache import KEY_PREFIX from django.core.cache import cache - cache.delete_pattern(KEY_PREFIX + "*") + session_keys = cache.keys(KEY_PREFIX + "*") + cache.delete_many(session_keys) def fix_duplicates(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): diff --git a/authentik/core/migrations/0022_authenticatedsession.py b/authentik/core/migrations/0022_authenticatedsession.py index bd4ba5ee8..df859a1a2 100644 --- a/authentik/core/migrations/0022_authenticatedsession.py +++ b/authentik/core/migrations/0022_authenticatedsession.py @@ -16,7 +16,8 @@ def migrate_sessions(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): from django.contrib.sessions.backends.cache import KEY_PREFIX from django.core.cache import cache - cache.delete_pattern(KEY_PREFIX + "*") + session_keys = cache.keys(KEY_PREFIX + "*") + cache.delete_many(session_keys) class Migration(migrations.Migration): diff --git a/authentik/core/signals.py b/authentik/core/signals.py index d5a97e95c..c114ffa65 100644 --- a/authentik/core/signals.py +++ b/authentik/core/signals.py @@ -37,7 +37,8 @@ def post_save_application(sender: type[Model], instance, created: bool, **_): if not created: # pragma: no cover return # Also delete user application cache - cache.delete_pattern(user_app_cache_key("*")) + keys = cache.keys(user_app_cache_key("*")) + cache.delete_many(keys) @receiver(user_logged_in) diff --git a/authentik/flows/api/flows.py b/authentik/flows/api/flows.py index 5de95c7a9..3f24679b7 100644 --- a/authentik/flows/api/flows.py +++ b/authentik/flows/api/flows.py @@ -130,8 +130,9 @@ class FlowViewSet(UsedByMixin, ModelViewSet): @action(detail=False, methods=["POST"]) def cache_clear(self, request: Request) -> Response: """Clear flow cache""" - count = cache.delete_pattern("flow_*") - LOGGER.debug("Cleared flow cache", keys=count) + keys = cache.keys("flow_*") + cache.delete_many(keys) + LOGGER.debug("Cleared flow cache", keys=len(keys)) return Response(status=204) @permission_required( diff --git a/authentik/flows/signals.py b/authentik/flows/signals.py index c1f0e217e..a285859a5 100644 --- a/authentik/flows/signals.py +++ b/authentik/flows/signals.py @@ -7,6 +7,13 @@ from structlog.stdlib import get_logger LOGGER = get_logger() +def delete_cache_prefix(prefix: str) -> int: + """Delete keys prefixed with `prefix` and return count of deleted keys.""" + keys = cache.keys(prefix) + cache.delete_many(keys) + return len(keys) + + @receiver(post_save) @receiver(pre_delete) # pylint: disable=unused-argument @@ -16,14 +23,14 @@ def invalidate_flow_cache(sender, instance, **_): from authentik.flows.planner import cache_key if isinstance(instance, Flow): - total = cache.delete_pattern(f"{cache_key(instance)}*") + total = delete_cache_prefix(f"{cache_key(instance)}*") LOGGER.debug("Invalidating Flow cache", flow=instance, len=total) if isinstance(instance, FlowStageBinding): - total = cache.delete_pattern(f"{cache_key(instance.target)}*") + total = delete_cache_prefix(f"{cache_key(instance.target)}*") LOGGER.debug("Invalidating Flow cache from FlowStageBinding", binding=instance, len=total) if isinstance(instance, Stage): total = 0 for binding in FlowStageBinding.objects.filter(stage=instance): prefix = cache_key(binding.target) - total += cache.delete_pattern(f"{prefix}*") + total += delete_cache_prefix(f"{prefix}*") LOGGER.debug("Invalidating Flow cache from Stage", stage=instance, len=total) diff --git a/authentik/flows/views/executor.py b/authentik/flows/views/executor.py index 9cb67d895..229c6a72f 100644 --- a/authentik/flows/views/executor.py +++ b/authentik/flows/views/executor.py @@ -205,7 +205,8 @@ class FlowExecutorView(APIView): self._logger.warning( "f(exec): found incompatible flow plan, invalidating run", exc=exc ) - cache.delete_pattern("flow_*") + keys = cache.keys("flow_*") + cache.delete_many(keys) return self.stage_invalid() if not next_binding: self._logger.debug("f(exec): no more stages, flow is done.") @@ -331,7 +332,8 @@ class FlowExecutorView(APIView): # from the cache. If there are errors, just delete all cached flows _ = plan.has_stages except Exception: # pylint: disable=broad-except - cache.delete_pattern("flow_*") + keys = cache.keys("flow_*") + cache.delete_many(keys) return self._initiate_plan() return plan diff --git a/authentik/policies/api/policies.py b/authentik/policies/api/policies.py index ae793be7b..6b6920a2f 100644 --- a/authentik/policies/api/policies.py +++ b/authentik/policies/api/policies.py @@ -128,10 +128,12 @@ class PolicyViewSet( @action(detail=False, methods=["POST"]) def cache_clear(self, request: Request) -> Response: """Clear policy cache""" - count = cache.delete_pattern("policy_*") - LOGGER.debug("Cleared Policy cache", keys=count) + keys = cache.keys("policy_*") + cache.delete_many(keys) + LOGGER.debug("Cleared Policy cache", keys=len(keys)) # Also delete user application cache - cache.delete_pattern(user_app_cache_key("*")) + keys = cache.keys(user_app_cache_key("*")) + cache.delete_many(keys) return Response(status=204) @permission_required("authentik_policies.view_policy") diff --git a/authentik/policies/signals.py b/authentik/policies/signals.py index 2acd98e1f..f38a508ce 100644 --- a/authentik/policies/signals.py +++ b/authentik/policies/signals.py @@ -19,7 +19,10 @@ def invalidate_policy_cache(sender, instance, **_): total = 0 for binding in PolicyBinding.objects.filter(policy=instance): prefix = f"policy_{binding.policy_binding_uuid.hex}_{binding.policy.pk.hex}*" - total += cache.delete_pattern(prefix) + keys = cache.keys(prefix) + total += len(keys) + cache.delete_many(keys) LOGGER.debug("Invalidating policy cache", policy=instance, keys=total) # Also delete user application cache - cache.delete_pattern(user_app_cache_key("*")) + keys = cache.keys(user_app_cache_key("*")) or [] + cache.delete_many(keys)