Redis caching layer for django-rest-knox - dramatically reduce database load on token authentication
Project description
django-rest-knox-redis
Redis caching layer for django-rest-knox that dramatically reduces database load on token authentication.
The Problem
Every API request with token authentication hits your database. With django-rest-knox, each request requires:
- Query database by
token_keyindex - Fetch token record with user data
- Validate token hash
- Check user status
At scale, this becomes a bottleneck:
┌─────────────────────────────────────────────────────────────────┐
│ Database Load Analysis │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Requests/sec DB Queries/sec DB Connection Pool Usage │
│ ──────────── ────────────── ──────────────────────── │
│ 100 100 10% │
│ 500 500 50% │
│ 1,000 1,000 100% ← Saturation │
│ 2,000 2,000 200% ← Connection │
│ waiting │
└─────────────────────────────────────────────────────────────────┘
Real-world impact:
- 🔴 1,000 req/sec = 1,000 database queries just for authentication
- 🔴 Database connections exhausted under load
- 🔴 Latency spikes when DB is under pressure
- 🔴 Cascading failures affecting all services sharing the DB
The Solution
django-rest-knox-redis adds a Redis caching layer that eliminates most database queries:
┌─────────────────────────────────────────────────────────────────┐
│ With Redis Caching │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Request Flow: │
│ │
│ ┌──────────┐ ┌───────────┐ ┌──────────────┐ │
│ │ Client │────▶│ Redis │────▶│ Database │ │
│ └──────────┘ └───────────┘ └──────────────┘ │
│ │ │ │
│ 95% HIT ✓ 5% MISS │
│ (< 1ms) (then cache) │
│ │
└─────────────────────────────────────────────────────────────────┘
Performance Comparison
┌─────────────────────────────────────────────────────────────────┐
│ Authentication Latency (p99) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ knox (DB only) ████████████████████████████████ 45ms │
│ knox-redis (hit) ██ 2ms │
│ knox-redis (miss) ████████████████████████████████ 47ms │
│ │
│ * Redis cache hit: 22x faster │
│ │
├─────────────────────────────────────────────────────────────────┤
│ Database Queries per 10,000 Requests │
├─────────────────────────────────────────────────────────────────┤
│ │
│ knox (DB only) ████████████████████████████ 10,000 │
│ knox-redis (95%) █ 500 │
│ │
│ * 95% cache hit rate = 95% reduction in DB queries │
│ │
├─────────────────────────────────────────────────────────────────┤
│ Throughput (requests/sec on same hardware) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ knox (DB only) ████████████ 1,200 │
│ knox-redis ████████████████████████████ 8,500 │
│ │
│ * 7x higher throughput with Redis caching │
│ │
└─────────────────────────────────────────────────────────────────┘
How It Works
┌─────────────────────────────────────────────────────────────────┐
│ Authentication Flow │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 1. Request with "Authorization: Token xxx..." │
│ │ │
│ ▼ │
│ 2. ┌─────────────────────────────────────┐ │
│ │ Check Redis cache by token_key │ │
│ │ Key: knox:token:{first_15_chars} │ │
│ └─────────────────────────────────────┘ │
│ │ │
│ ┌───────────┴───────────┐ │
│ │ │ │
│ Cache HIT Cache MISS │
│ │ │ │
│ ▼ ▼ │
│ 3. Validate hash 4. Query database │
│ Get user from DB Validate token │
│ (by PK - fast) Cache in Redis │
│ │ │ │
│ └───────────┬───────────┘ │
│ │ │
│ ▼ │
│ 5. Return (user, token) to DRF │
│ │
└─────────────────────────────────────────────────────────────────┘
Key design decisions:
- ✅ User always fetched from DB - ensures
is_activechanges apply immediately - ✅ Token data cached indefinitely - no TTL, explicit invalidation only
- ✅ Graceful degradation - falls back to DB if Redis unavailable
- ✅ Atomic cache invalidation - on logout, logoutall, token deletion
Installation
pip install django-rest-knox-redis
Or with uv:
uv add django-rest-knox-redis
Quick Start
1. Configure Django Settings
# settings.py
INSTALLED_APPS = [
# ...
'rest_framework',
'knox',
'knox_redis',
]
# Configure Redis cache with django-redis
CACHES = {
'default': {
'BACKEND': 'django_redis.cache.RedisCache',
'LOCATION': 'redis://127.0.0.1:6379/1',
'OPTIONS': {
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
}
}
}
# Knox Redis settings
REST_KNOX_REDIS = {
'CACHE_ALIAS': 'default', # Which Django cache to use
'REDIS_KEY_PREFIX': 'knox', # Prefix for Redis keys
'CACHE_ENABLED': True, # Toggle caching on/off
}
# Standard Knox settings (optional)
REST_KNOX = {
'TOKEN_TTL': None, # Token lifetime
'AUTO_REFRESH': True, # Auto-refresh on activity
}
# IMPORTANT: Replace knox.auth.TokenAuthentication with knox_redis.auth.TokenAuthentication
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'knox_redis.auth.TokenAuthentication', # <-- Use knox_redis instead of knox
# ... other authentication classes
],
}
2. Update URLs
# urls.py
from knox_redis.views import LoginView, LogoutView, LogoutAllView
urlpatterns = [
path('api/auth/login/', LoginView.as_view(), name='knox_login'),
path('api/auth/logout/', LogoutView.as_view(), name='knox_logout'),
path('api/auth/logoutall/', LogoutAllView.as_view(), name='knox_logoutall'),
]
3. Use in Views (optional per-view override)
If you set DEFAULT_AUTHENTICATION_CLASSES globally, you don't need to specify it per view.
But you can still override authentication per view if needed:
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from knox_redis.auth import TokenAuthentication
class ProtectedView(APIView):
# Optional: override if not set globally in REST_FRAMEWORK settings
authentication_classes = [TokenAuthentication]
def get(self, request):
return Response({'user': request.user.username})
That's it! Your token authentication is now cached in Redis.
Migration from knox
If you're already using knox.auth.TokenAuthentication, migration is simple:
Before (knox only)
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'knox.auth.TokenAuthentication',
],
}
After (with Redis caching)
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'knox_redis.auth.TokenAuthentication', # Just change the import path
],
}
That's all! The knox_redis.auth.TokenAuthentication class is a drop-in replacement.
It inherits from knox.auth.TokenAuthentication and adds the Redis caching layer transparently.
Configuration Reference
REST_KNOX_REDIS Settings
| Setting | Type | Default | Description |
|---|---|---|---|
CACHE_ALIAS |
str |
'default' |
Django cache alias (must use django-redis) |
REDIS_KEY_PREFIX |
str |
'knox' |
Prefix for all Redis keys |
CACHE_ENABLED |
bool |
True |
Enable/disable caching globally |
Redis Key Schema
knox:token:{token_key} → JSON {digest, user_id, created, expiry}
knox:user:{user_id}:tokens → Set of token_keys (for bulk invalidation)
Cache Invalidation
Cache is automatically invalidated when:
| Event | Action |
|---|---|
LogoutView.post() |
Deletes single token from Redis |
LogoutAllView.post() |
Deletes all user tokens from Redis |
| Token deleted via ORM | Signal handler invalidates cache |
| Token expired | Removed on next auth attempt |
| User deleted | Invalidated on next auth attempt |
Error Handling
Redis unavailable? No problem:
# Cache operations are wrapped in try/except
# On Redis failure:
# 1. Warning logged
# 2. Falls back to database authentication
# 3. Application continues working
Monitoring
Check Cache Hit Rate
from django.core.cache import caches
from django_redis import get_redis_connection
redis_conn = get_redis_connection("default")
info = redis_conn.info()
print(f"Cache hits: {info['keyspace_hits']}")
print(f"Cache misses: {info['keyspace_misses']}")
print(f"Hit rate: {info['keyspace_hits'] / (info['keyspace_hits'] + info['keyspace_misses']) * 100:.1f}%")
View Cached Tokens
redis-cli KEYS "knox:token:*" | wc -l # Count cached tokens
redis-cli KEYS "knox:user:*" # List user token indexes
Development
# Clone repository
git clone https://github.com/yourusername/django-rest-knox-redis.git
cd django-rest-knox-redis
# Install with uv
uv sync
# Run tests
uv run pytest
# Run tests with coverage
uv run pytest --cov=knox_redis --cov-report=html
# Lint
uv run ruff check .
uv run ruff format .
Requirements
- Python 3.10+
- Django 4.2+
- django-rest-framework 3.14+
- django-rest-knox 4.2+
- django-redis 5.4+
- Redis Server 6.0+
License
MIT License - see LICENSE file.
Contributing
Contributions are welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Submit a pull request
Credits
- django-rest-knox - The excellent token authentication library this package extends
- django-redis - Redis cache backend for Django
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_rest_knox_redis-0.1.3.tar.gz.
File metadata
- Download URL: django_rest_knox_redis-0.1.3.tar.gz
- Upload date:
- Size: 16.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f0cd3bda389b94c058327efa4f220ec810305648649a3762e0f70193e618c79
|
|
| MD5 |
78873c566d1c762b4ee64d955b4616f8
|
|
| BLAKE2b-256 |
0cd87d6a88a0c3b6c0a59a1f8dd484593344c27a9cedc6bb984284f4152b72cf
|
Provenance
The following attestation bundles were made for django_rest_knox_redis-0.1.3.tar.gz:
Publisher:
publish.yml on safonin/django-rest-knox-redis
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_rest_knox_redis-0.1.3.tar.gz -
Subject digest:
3f0cd3bda389b94c058327efa4f220ec810305648649a3762e0f70193e618c79 - Sigstore transparency entry: 936711758
- Sigstore integration time:
-
Permalink:
safonin/django-rest-knox-redis@91e58fb2d18314b191a3851ad4081c796180687a -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/safonin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@91e58fb2d18314b191a3851ad4081c796180687a -
Trigger Event:
release
-
Statement type:
File details
Details for the file django_rest_knox_redis-0.1.3-py3-none-any.whl.
File metadata
- Download URL: django_rest_knox_redis-0.1.3-py3-none-any.whl
- Upload date:
- Size: 14.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0e07891d0c35b1fb1aafbc5a037a1b9fd7750d77a46e4fddf719b4828fdeac19
|
|
| MD5 |
558adb0523c547339a8aeba3bf2e9b26
|
|
| BLAKE2b-256 |
6cd79f347bc04055ac8a5361802bbfcbe55e5b28abedf0b55d3ad371df9bf28d
|
Provenance
The following attestation bundles were made for django_rest_knox_redis-0.1.3-py3-none-any.whl:
Publisher:
publish.yml on safonin/django-rest-knox-redis
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
django_rest_knox_redis-0.1.3-py3-none-any.whl -
Subject digest:
0e07891d0c35b1fb1aafbc5a037a1b9fd7750d77a46e4fddf719b4828fdeac19 - Sigstore transparency entry: 936711765
- Sigstore integration time:
-
Permalink:
safonin/django-rest-knox-redis@91e58fb2d18314b191a3851ad4081c796180687a -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/safonin
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@91e58fb2d18314b191a3851ad4081c796180687a -
Trigger Event:
release
-
Statement type: