Browse Source

Fix Redis configmap inline comment, clean cache key format, add blocked-IP scan to plan

- redis-configmap: move inline comment to its own line (Redis fatal parse error)
- settings: add CACHE_MIDDLEWARE_KEY_PREFIX='p' to remove double-dot in cache_page keys
- settings: monkey-patch _i18n_cache_key_suffix to strip pt-br/timezone suffix from keys
- ratelimit.py, settings: update example namespace from patobranco-pr to sapl31demo-df
- robots.txt: add AwarioSmartBot block
- plan: add rl:ip:*:blocked scan commands with TTL/value output

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
rate-limiter-2026
Edward Ribeiro 2 weeks ago
parent
commit
e6dff2bc00
  1. 3
      docker/k8s/redis/redis-configmap.yaml
  2. 14
      plan/RATE-LIMITER-PLAN.md
  3. 2
      plan/rate-limiter-v2.md
  4. 2
      sapl/middleware/ratelimit.py
  5. 16
      sapl/settings.py
  6. 4
      sapl/static/robots.txt

3
docker/k8s/redis/redis-configmap.yaml

@ -28,7 +28,8 @@ data:
bind 0.0.0.0 bind 0.0.0.0
protected-mode no protected-mode no
databases 4 # DB0: cache, DB1: rate limiter, DB2: channels (future) # DB0: cache DB1: rate limiter DB2: channels (future)
databases 2
activedefrag yes activedefrag yes
active-defrag-ignore-bytes 100mb active-defrag-ignore-bytes 100mb

14
plan/RATE-LIMITER-PLAN.md

@ -324,8 +324,22 @@ rancher kubectl exec -n sapl-redis deploy/sapl-redis -- redis-cli --latency-hist
rancher kubectl exec -n sapl-redis deploy/sapl-redis -- \ rancher kubectl exec -n sapl-redis deploy/sapl-redis -- \
redis-cli -n 1 dbsize redis-cli -n 1 dbsize
# All rate-limiter keys for an IP prefix
rancher kubectl exec -n sapl-redis deploy/sapl-redis -- \ rancher kubectl exec -n sapl-redis deploy/sapl-redis -- \
redis-cli -n 1 --scan --pattern 'rl:ip:*' | head -20 redis-cli -n 1 --scan --pattern 'rl:ip:*' | head -20
# All currently blocked IPs
rancher kubectl exec -n sapl-redis deploy/sapl-redis -- \
redis-cli -n 1 --scan --pattern 'rl:ip:*:blocked'
```
Via port-forward (local machine — run `kubectl port-forward svc/redis -n sapl-redis 6379:6379` first):
```bash
# All blocked IPs with value and remaining TTL
redis-cli -n 1 --scan --pattern 'rl:ip:*:blocked' | while read key; do
echo "$key → $(redis-cli -n 1 GET $key) (TTL: $(redis-cli -n 1 TTL $key)s)"
done
``` ```
--- ---

2
plan/rate-limiter-v2.md

@ -662,7 +662,7 @@ CACHES = {
else 'django.core.cache.backends.filebased.FileBasedCache' else 'django.core.cache.backends.filebased.FileBasedCache'
), ),
'LOCATION': REDIS_URL + '/0' if _redis_ready else '/var/tmp/django_cache', 'LOCATION': REDIS_URL + '/0' if _redis_ready else '/var/tmp/django_cache',
'KEY_PREFIX': f'cache:{POD_NAMESPACE}', # e.g. "cache:sapl:" or "cache:patobranco-pr:" 'KEY_PREFIX': f'cache:{POD_NAMESPACE}', # e.g. "cache:sapl:" or "cache:sapl31demo-df:"
**( **(
{ {
'OPTIONS': { 'OPTIONS': {

2
sapl/middleware/ratelimit.py

@ -40,7 +40,7 @@ logger = logging.getLogger('sapl.ratelimit')
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Tenant namespace — resolved once at startup from settings.POD_NAMESPACE. # Tenant namespace — resolved once at startup from settings.POD_NAMESPACE.
# On K8s: the k8s namespace (e.g. "patobranco-pr"), set by start.sh. # On K8s: the k8s namespace (e.g. "sapl31demo-df"), set by start.sh.
# On bare-metal / VM / docker-compose: the machine hostname (default). # On bare-metal / VM / docker-compose: the machine hostname (default).
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------

16
sapl/settings.py

@ -209,7 +209,7 @@ SPECTACULAR_SETTINGS = {
# Defaults to the machine hostname so self-hosted (bare-metal / VM / # Defaults to the machine hostname so self-hosted (bare-metal / VM /
# docker-compose) deployments work without any extra config. # docker-compose) deployments work without any extra config.
# On Kubernetes, POD_NAMESPACE is set by start.sh via the Downward API or # On Kubernetes, POD_NAMESPACE is set by start.sh via the Downward API or
# the service-account namespace file (e.g. "patobranco-pr"). # the service-account namespace file (e.g. "sapl31demo-df").
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
POD_NAMESPACE = config('POD_NAMESPACE', default=host) POD_NAMESPACE = config('POD_NAMESPACE', default=host)
@ -221,6 +221,11 @@ POD_NAMESPACE = config('POD_NAMESPACE', default=host)
REDIS_URL = config('REDIS_URL', default='') REDIS_URL = config('REDIS_URL', default='')
CACHE_BACKEND = config('CACHE_BACKEND', default='file') CACHE_BACKEND = config('CACHE_BACKEND', default='file')
# Avoid the double-dot in cache_page keys that occurs when this is '' (default).
# Namespace isolation is already handled by CACHES['default']['KEY_PREFIX'];
# this 'p' just fills the empty slot in Django's key format string.
CACHE_MIDDLEWARE_KEY_PREFIX = 'p'
def _build_cache_layer(pod_namespace, cache_backend, redis_url): def _build_cache_layer(pod_namespace, cache_backend, redis_url):
""" """
@ -488,6 +493,15 @@ def _compat_utc_tzinfo_factory(offset):
pg_utils.utc_tzinfo_factory = _compat_utc_tzinfo_factory pg_utils.utc_tzinfo_factory = _compat_utc_tzinfo_factory
##
## Strip the language/timezone suffix from Django page-cache keys.
## Django's _i18n_cache_key_suffix appends ".pt-br.America/Sao_Paulo" when
## USE_I18N and USE_TZ are True. SAPL is monolingual (always pt-br /
## America/Sao_Paulo), so the suffix is constant noise that bloats key names.
##
import django.utils.cache as _dj_cache
_dj_cache._i18n_cache_key_suffix = lambda request, cache_key: cache_key
# DATE_FORMAT = 'N j, Y' # DATE_FORMAT = 'N j, Y'
DATE_FORMAT = 'd/m/Y' DATE_FORMAT = 'd/m/Y'
SHORT_DATE_FORMAT = 'd/m/Y' SHORT_DATE_FORMAT = 'd/m/Y'

4
sapl/static/robots.txt

@ -26,6 +26,10 @@ User-agent: anthropic-python
Disallow: / Disallow: /
Crawl-delay: 10 Crawl-delay: 10
User-agent: AwarioSmartBot
Disallow: /
Crawl-delay: 10
User-agent: * User-agent: *
Disallow: /relatorios/ Disallow: /relatorios/
Crawl-delay: 10 Crawl-delay: 10

Loading…
Cancel
Save