django-cfg 1.1.61__py3-none-any.whl → 1.1.63__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_cfg/__init__.py +1 -1
- django_cfg/management/commands/rundramatiq.py +174 -202
- django_cfg/modules/django_tasks.py +54 -428
- django_cfg/modules/dramatiq_setup.py +16 -0
- {django_cfg-1.1.61.dist-info → django_cfg-1.1.63.dist-info}/METADATA +145 -4
- {django_cfg-1.1.61.dist-info → django_cfg-1.1.63.dist-info}/RECORD +9 -27
- django_cfg/apps/accounts/tests/__init__.py +0 -1
- django_cfg/apps/accounts/tests/test_models.py +0 -412
- django_cfg/apps/accounts/tests/test_otp_views.py +0 -143
- django_cfg/apps/accounts/tests/test_serializers.py +0 -331
- django_cfg/apps/accounts/tests/test_services.py +0 -401
- django_cfg/apps/accounts/tests/test_signals.py +0 -110
- django_cfg/apps/accounts/tests/test_views.py +0 -255
- django_cfg/apps/newsletter/tests/__init__.py +0 -1
- django_cfg/apps/newsletter/tests/run_tests.py +0 -47
- django_cfg/apps/newsletter/tests/test_email_integration.py +0 -256
- django_cfg/apps/newsletter/tests/test_email_tracking.py +0 -332
- django_cfg/apps/newsletter/tests/test_newsletter_manager.py +0 -83
- django_cfg/apps/newsletter/tests/test_newsletter_models.py +0 -157
- django_cfg/apps/support/tests/__init__.py +0 -0
- django_cfg/apps/support/tests/test_models.py +0 -106
- django_cfg/apps/tasks/@docs/CONFIGURATION.md +0 -663
- django_cfg/apps/tasks/@docs/README.md +0 -195
- django_cfg/apps/tasks/@docs/TASKS_QUEUES.md +0 -423
- django_cfg/apps/tasks/@docs/TROUBLESHOOTING.md +0 -506
- {django_cfg-1.1.61.dist-info → django_cfg-1.1.63.dist-info}/WHEEL +0 -0
- {django_cfg-1.1.61.dist-info → django_cfg-1.1.63.dist-info}/entry_points.txt +0 -0
- {django_cfg-1.1.61.dist-info → django_cfg-1.1.63.dist-info}/licenses/LICENSE +0 -0
@@ -1,663 +0,0 @@
|
|
1
|
-
# ⚙️ Django-CFG Tasks Configuration Guide
|
2
|
-
|
3
|
-
## 🎯 Overview
|
4
|
-
|
5
|
-
Complete configuration reference for Django-CFG task system. Covers all settings, environment variables, and deployment configurations.
|
6
|
-
|
7
|
-
**TAGS**: `configuration, settings, deployment, dramatiq, redis`
|
8
|
-
|
9
|
-
---
|
10
|
-
|
11
|
-
## 🏗️ Configuration Models %%PRIORITY:HIGH%%
|
12
|
-
|
13
|
-
### TaskConfig (Main Configuration)
|
14
|
-
|
15
|
-
```python
|
16
|
-
from django_cfg.models.tasks import TaskConfig, DramatiqConfig
|
17
|
-
|
18
|
-
config = TaskConfig(
|
19
|
-
enabled=True, # Enable task system
|
20
|
-
backend="dramatiq", # Task backend (only dramatiq supported)
|
21
|
-
dramatiq=DramatiqConfig(...), # Dramatiq-specific settings
|
22
|
-
auto_discover_tasks=True, # Auto-discover task modules
|
23
|
-
task_modules=["tasks"] # Module names to search for tasks
|
24
|
-
)
|
25
|
-
```
|
26
|
-
|
27
|
-
**Field Details**:
|
28
|
-
- `enabled`: Master switch for entire task system
|
29
|
-
- `backend`: Currently only `TaskBackend.DRAMATIQ` supported
|
30
|
-
- `dramatiq`: Nested Dramatiq configuration (see below)
|
31
|
-
- `auto_discover_tasks`: Automatically find task modules in Django apps
|
32
|
-
- `task_modules`: List of module names to search (e.g., "tasks", "background")
|
33
|
-
|
34
|
-
---
|
35
|
-
|
36
|
-
### DramatiqConfig (Dramatiq Settings)
|
37
|
-
|
38
|
-
```python
|
39
|
-
from django_cfg.models.tasks import DramatiqConfig
|
40
|
-
|
41
|
-
dramatiq_config = DramatiqConfig(
|
42
|
-
# === Redis Configuration ===
|
43
|
-
redis_db=2, # Redis database number %%PRIORITY:HIGH%%
|
44
|
-
redis_key_prefix="dramatiq", # Key prefix for Redis keys
|
45
|
-
redis_socket_timeout=5, # Connection timeout (seconds)
|
46
|
-
redis_socket_connect_timeout=5, # Connect timeout (seconds)
|
47
|
-
redis_socket_keepalive=True, # Enable TCP keepalive
|
48
|
-
redis_socket_keepalive_options={},
|
49
|
-
redis_health_check_interval=30, # Health check interval (seconds)
|
50
|
-
|
51
|
-
# === Worker Configuration ===
|
52
|
-
processes=2, # Number of worker processes
|
53
|
-
threads=4, # Threads per process
|
54
|
-
|
55
|
-
# === Queue Configuration ===
|
56
|
-
queues=[ # Available queues
|
57
|
-
"default", "high", "low",
|
58
|
-
"vehicles", "tax", "parsing", "knowledge"
|
59
|
-
],
|
60
|
-
|
61
|
-
# === Middleware Stack ===
|
62
|
-
middleware=[ # Middleware in order %%PRIORITY:HIGH%%
|
63
|
-
"dramatiq.middleware.AgeLimit",
|
64
|
-
"dramatiq.middleware.TimeLimit",
|
65
|
-
"dramatiq.middleware.Callbacks",
|
66
|
-
"dramatiq.middleware.Retries",
|
67
|
-
"dramatiq.middleware.Shutdown",
|
68
|
-
],
|
69
|
-
|
70
|
-
# === Timeouts & Limits ===
|
71
|
-
max_age=3600000, # Max message age (ms)
|
72
|
-
max_retries=3, # Default retry count
|
73
|
-
min_backoff=15000, # Min retry backoff (ms)
|
74
|
-
max_backoff=300000, # Max retry backoff (ms)
|
75
|
-
|
76
|
-
# === Worker Settings ===
|
77
|
-
worker=WorkerConfig(
|
78
|
-
threads=4, # Threads per worker
|
79
|
-
shutdown_timeout=30, # Graceful shutdown timeout
|
80
|
-
log_level="INFO" # Worker log level
|
81
|
-
),
|
82
|
-
|
83
|
-
# === Monitoring ===
|
84
|
-
prometheus_enabled=False, # Disable Prometheus by default
|
85
|
-
prometheus_http_host="127.0.0.1",
|
86
|
-
prometheus_http_port=9191,
|
87
|
-
|
88
|
-
# === Admin Interface ===
|
89
|
-
admin_enabled=True # Enable Django admin integration
|
90
|
-
)
|
91
|
-
```
|
92
|
-
|
93
|
-
---
|
94
|
-
|
95
|
-
## 🔧 Environment-Specific Configurations
|
96
|
-
|
97
|
-
### Development Configuration
|
98
|
-
|
99
|
-
```python
|
100
|
-
# config/dev.yaml
|
101
|
-
tasks:
|
102
|
-
enabled: true
|
103
|
-
dramatiq:
|
104
|
-
redis_db: 2
|
105
|
-
processes: 1 # Single process for development
|
106
|
-
threads: 2 # Fewer threads
|
107
|
-
queues:
|
108
|
-
- default
|
109
|
-
- knowledge # Only essential queues
|
110
|
-
prometheus_enabled: false
|
111
|
-
```
|
112
|
-
|
113
|
-
### Production Configuration
|
114
|
-
|
115
|
-
```python
|
116
|
-
# config/prod.yaml
|
117
|
-
tasks:
|
118
|
-
enabled: true
|
119
|
-
dramatiq:
|
120
|
-
redis_db: 2
|
121
|
-
processes: 4 # More processes for production
|
122
|
-
threads: 8 # More threads
|
123
|
-
queues: # All queues
|
124
|
-
- default
|
125
|
-
- high
|
126
|
-
- low
|
127
|
-
- vehicles
|
128
|
-
- tax
|
129
|
-
- parsing
|
130
|
-
- knowledge
|
131
|
-
prometheus_enabled: true
|
132
|
-
prometheus_http_port: 9191
|
133
|
-
max_retries: 5 # More retries in production
|
134
|
-
```
|
135
|
-
|
136
|
-
### Testing Configuration
|
137
|
-
|
138
|
-
```python
|
139
|
-
# config/test.yaml
|
140
|
-
tasks:
|
141
|
-
enabled: false # Disable in tests
|
142
|
-
# OR for integration tests:
|
143
|
-
enabled: true
|
144
|
-
dramatiq:
|
145
|
-
redis_db: 15 # Use different DB for tests
|
146
|
-
processes: 1
|
147
|
-
threads: 1
|
148
|
-
```
|
149
|
-
|
150
|
-
---
|
151
|
-
|
152
|
-
## 🐳 Docker Configuration
|
153
|
-
|
154
|
-
### Docker Compose
|
155
|
-
|
156
|
-
```yaml
|
157
|
-
version: '3.8'
|
158
|
-
services:
|
159
|
-
redis:
|
160
|
-
image: redis:7-alpine
|
161
|
-
ports:
|
162
|
-
- "6379:6379"
|
163
|
-
volumes:
|
164
|
-
- redis_data:/data
|
165
|
-
command: redis-server --appendonly yes
|
166
|
-
|
167
|
-
django:
|
168
|
-
build: .
|
169
|
-
environment:
|
170
|
-
- DJANGO_SETTINGS_MODULE=api.settings
|
171
|
-
- REDIS_URL=redis://redis:6379/0
|
172
|
-
depends_on:
|
173
|
-
- redis
|
174
|
-
|
175
|
-
dramatiq-workers:
|
176
|
-
build: .
|
177
|
-
command: python manage.py rundramatiq --processes 2 --threads 4
|
178
|
-
environment:
|
179
|
-
- DJANGO_SETTINGS_MODULE=api.settings
|
180
|
-
- REDIS_URL=redis://redis:6379/0
|
181
|
-
depends_on:
|
182
|
-
- redis
|
183
|
-
- django
|
184
|
-
restart: unless-stopped
|
185
|
-
|
186
|
-
volumes:
|
187
|
-
redis_data:
|
188
|
-
```
|
189
|
-
|
190
|
-
### Dockerfile for Workers
|
191
|
-
|
192
|
-
```dockerfile
|
193
|
-
FROM python:3.11-slim
|
194
|
-
|
195
|
-
WORKDIR /app
|
196
|
-
COPY requirements.txt .
|
197
|
-
RUN pip install -r requirements.txt
|
198
|
-
|
199
|
-
COPY . .
|
200
|
-
|
201
|
-
# Health check for workers
|
202
|
-
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
203
|
-
CMD python -c "
|
204
|
-
import redis
|
205
|
-
r = redis.Redis(host='redis', port=6379, db=2)
|
206
|
-
r.ping()
|
207
|
-
" || exit 1
|
208
|
-
|
209
|
-
CMD ["python", "manage.py", "rundramatiq", "--processes", "2", "--threads", "4"]
|
210
|
-
```
|
211
|
-
|
212
|
-
---
|
213
|
-
|
214
|
-
## 🔄 Process Management
|
215
|
-
|
216
|
-
### Systemd Service
|
217
|
-
|
218
|
-
```ini
|
219
|
-
# /etc/systemd/system/dramatiq-workers.service
|
220
|
-
[Unit]
|
221
|
-
Description=Dramatiq Background Workers
|
222
|
-
After=network.target redis.service postgresql.service
|
223
|
-
Requires=redis.service
|
224
|
-
|
225
|
-
[Service]
|
226
|
-
Type=simple
|
227
|
-
User=www-data
|
228
|
-
Group=www-data
|
229
|
-
WorkingDirectory=/opt/myproject
|
230
|
-
Environment=DJANGO_SETTINGS_MODULE=api.settings
|
231
|
-
Environment=PYTHONPATH=/opt/myproject
|
232
|
-
ExecStart=/opt/myproject/venv/bin/python manage.py rundramatiq --processes 4 --threads 8
|
233
|
-
ExecReload=/bin/kill -HUP $MAINPID
|
234
|
-
KillMode=mixed
|
235
|
-
TimeoutStopSec=30
|
236
|
-
Restart=always
|
237
|
-
RestartSec=10
|
238
|
-
|
239
|
-
# Resource limits
|
240
|
-
LimitNOFILE=65536
|
241
|
-
MemoryMax=2G
|
242
|
-
CPUQuota=200%
|
243
|
-
|
244
|
-
# Logging
|
245
|
-
StandardOutput=journal
|
246
|
-
StandardError=journal
|
247
|
-
SyslogIdentifier=dramatiq-workers
|
248
|
-
|
249
|
-
[Install]
|
250
|
-
WantedBy=multi-user.target
|
251
|
-
```
|
252
|
-
|
253
|
-
**Management Commands**:
|
254
|
-
```bash
|
255
|
-
# Enable and start
|
256
|
-
sudo systemctl enable dramatiq-workers
|
257
|
-
sudo systemctl start dramatiq-workers
|
258
|
-
|
259
|
-
# Status and logs
|
260
|
-
sudo systemctl status dramatiq-workers
|
261
|
-
sudo journalctl -u dramatiq-workers -f
|
262
|
-
|
263
|
-
# Restart workers
|
264
|
-
sudo systemctl restart dramatiq-workers
|
265
|
-
```
|
266
|
-
|
267
|
-
### Supervisor Configuration
|
268
|
-
|
269
|
-
```ini
|
270
|
-
# /etc/supervisor/conf.d/dramatiq-workers.conf
|
271
|
-
[program:dramatiq-workers]
|
272
|
-
command=/opt/myproject/venv/bin/python manage.py rundramatiq --processes 4 --threads 8
|
273
|
-
directory=/opt/myproject
|
274
|
-
user=www-data
|
275
|
-
autostart=true
|
276
|
-
autorestart=true
|
277
|
-
startsecs=10
|
278
|
-
startretries=3
|
279
|
-
redirect_stderr=true
|
280
|
-
stdout_logfile=/var/log/dramatiq/workers.log
|
281
|
-
stdout_logfile_maxbytes=50MB
|
282
|
-
stdout_logfile_backups=10
|
283
|
-
environment=DJANGO_SETTINGS_MODULE="api.settings",PYTHONPATH="/opt/myproject"
|
284
|
-
|
285
|
-
# Resource limits
|
286
|
-
process_name=%(program_name)s_%(process_num)02d
|
287
|
-
numprocs=1
|
288
|
-
```
|
289
|
-
|
290
|
-
**Management Commands**:
|
291
|
-
```bash
|
292
|
-
# Reload configuration
|
293
|
-
sudo supervisorctl reread
|
294
|
-
sudo supervisorctl update
|
295
|
-
|
296
|
-
# Control workers
|
297
|
-
sudo supervisorctl start dramatiq-workers
|
298
|
-
sudo supervisorctl stop dramatiq-workers
|
299
|
-
sudo supervisorctl restart dramatiq-workers
|
300
|
-
sudo supervisorctl status dramatiq-workers
|
301
|
-
```
|
302
|
-
|
303
|
-
---
|
304
|
-
|
305
|
-
## 🔐 Security Configuration
|
306
|
-
|
307
|
-
### Redis Security
|
308
|
-
|
309
|
-
```bash
|
310
|
-
# /etc/redis/redis.conf
|
311
|
-
bind 127.0.0.1 # Bind to localhost only
|
312
|
-
requirepass your_strong_password # Set password
|
313
|
-
protected-mode yes # Enable protected mode
|
314
|
-
port 6379 # Default port (consider changing)
|
315
|
-
|
316
|
-
# Disable dangerous commands
|
317
|
-
rename-command FLUSHDB ""
|
318
|
-
rename-command FLUSHALL ""
|
319
|
-
rename-command KEYS ""
|
320
|
-
rename-command CONFIG ""
|
321
|
-
```
|
322
|
-
|
323
|
-
### Django Settings
|
324
|
-
|
325
|
-
```python
|
326
|
-
# settings/production.py
|
327
|
-
DRAMATIQ_BROKER = {
|
328
|
-
"BROKER": "dramatiq.brokers.redis.RedisBroker",
|
329
|
-
"OPTIONS": {
|
330
|
-
"url": "redis://:password@localhost:6379/2", # With password
|
331
|
-
"connection_pool_kwargs": {
|
332
|
-
"max_connections": 20,
|
333
|
-
"retry_on_timeout": True,
|
334
|
-
},
|
335
|
-
},
|
336
|
-
"MIDDLEWARE": [
|
337
|
-
"dramatiq.middleware.AgeLimit",
|
338
|
-
"dramatiq.middleware.TimeLimit",
|
339
|
-
"dramatiq.middleware.Callbacks",
|
340
|
-
"dramatiq.middleware.Retries",
|
341
|
-
"dramatiq.middleware.Shutdown",
|
342
|
-
],
|
343
|
-
}
|
344
|
-
|
345
|
-
# Security headers for Prometheus endpoint
|
346
|
-
if DRAMATIQ_CONFIG.get('prometheus_enabled'):
|
347
|
-
ALLOWED_HOSTS.append('monitoring.internal')
|
348
|
-
```
|
349
|
-
|
350
|
-
---
|
351
|
-
|
352
|
-
## 📊 Monitoring Configuration
|
353
|
-
|
354
|
-
### Prometheus Integration
|
355
|
-
|
356
|
-
```python
|
357
|
-
# Enable Prometheus in configuration
|
358
|
-
dramatiq_config = DramatiqConfig(
|
359
|
-
prometheus_enabled=True,
|
360
|
-
prometheus_http_host="0.0.0.0", # Allow external access
|
361
|
-
prometheus_http_port=9191,
|
362
|
-
)
|
363
|
-
```
|
364
|
-
|
365
|
-
**Prometheus Scrape Config**:
|
366
|
-
```yaml
|
367
|
-
# prometheus.yml
|
368
|
-
scrape_configs:
|
369
|
-
- job_name: 'dramatiq-workers'
|
370
|
-
static_configs:
|
371
|
-
- targets: ['worker1:9191', 'worker2:9191']
|
372
|
-
scrape_interval: 15s
|
373
|
-
metrics_path: /metrics
|
374
|
-
```
|
375
|
-
|
376
|
-
### Health Check Endpoint
|
377
|
-
|
378
|
-
```python
|
379
|
-
# Add to Django URLs
|
380
|
-
from django_cfg.modules.django_tasks import get_task_health
|
381
|
-
|
382
|
-
def health_check(request):
|
383
|
-
health = get_task_health()
|
384
|
-
status = 200 if health['enabled'] and health['redis_connection'] else 503
|
385
|
-
return JsonResponse(health, status=status)
|
386
|
-
|
387
|
-
# urls.py
|
388
|
-
urlpatterns = [
|
389
|
-
path('health/tasks/', health_check, name='tasks_health'),
|
390
|
-
]
|
391
|
-
```
|
392
|
-
|
393
|
-
---
|
394
|
-
|
395
|
-
## 🔧 Advanced Configuration
|
396
|
-
|
397
|
-
### Custom Middleware
|
398
|
-
|
399
|
-
```python
|
400
|
-
# custom_middleware.py
|
401
|
-
import dramatiq
|
402
|
-
from dramatiq.middleware import Middleware
|
403
|
-
|
404
|
-
class CustomLoggingMiddleware(Middleware):
|
405
|
-
def before_process_message(self, broker, message):
|
406
|
-
logger.info(f"Processing task: {message.actor_name}")
|
407
|
-
|
408
|
-
def after_process_message(self, broker, message, *, result=None, exception=None):
|
409
|
-
if exception:
|
410
|
-
logger.error(f"Task failed: {message.actor_name} - {exception}")
|
411
|
-
else:
|
412
|
-
logger.info(f"Task completed: {message.actor_name}")
|
413
|
-
|
414
|
-
# Add to middleware list
|
415
|
-
middleware = [
|
416
|
-
"dramatiq.middleware.AgeLimit",
|
417
|
-
"myapp.middleware.CustomLoggingMiddleware", # Custom middleware
|
418
|
-
"dramatiq.middleware.TimeLimit",
|
419
|
-
"dramatiq.middleware.Callbacks",
|
420
|
-
"dramatiq.middleware.Retries",
|
421
|
-
"dramatiq.middleware.Shutdown",
|
422
|
-
]
|
423
|
-
```
|
424
|
-
|
425
|
-
### Queue Routing
|
426
|
-
|
427
|
-
```python
|
428
|
-
# Route different tasks to different queues
|
429
|
-
@dramatiq.actor(queue_name="high")
|
430
|
-
def urgent_task():
|
431
|
-
pass
|
432
|
-
|
433
|
-
@dramatiq.actor(queue_name="knowledge")
|
434
|
-
def process_document():
|
435
|
-
pass
|
436
|
-
|
437
|
-
@dramatiq.actor(queue_name="low")
|
438
|
-
def cleanup_task():
|
439
|
-
pass
|
440
|
-
```
|
441
|
-
|
442
|
-
### Rate Limiting
|
443
|
-
|
444
|
-
```python
|
445
|
-
# Add rate limiting middleware
|
446
|
-
from dramatiq.rate_limits import ConcurrentRateLimiter
|
447
|
-
from dramatiq.rate_limits.backends import RedisBackend
|
448
|
-
|
449
|
-
# Configure rate limiter
|
450
|
-
backend = RedisBackend(url="redis://localhost:6379/2")
|
451
|
-
rate_limiter = ConcurrentRateLimiter(backend, "api-calls", limit=10)
|
452
|
-
|
453
|
-
@dramatiq.actor
|
454
|
-
def api_call_task():
|
455
|
-
with rate_limiter.acquire():
|
456
|
-
# Make API call
|
457
|
-
pass
|
458
|
-
```
|
459
|
-
|
460
|
-
---
|
461
|
-
|
462
|
-
## 🧪 Testing Configuration
|
463
|
-
|
464
|
-
### Test Settings
|
465
|
-
|
466
|
-
```python
|
467
|
-
# settings/test.py
|
468
|
-
import tempfile
|
469
|
-
|
470
|
-
# Use in-memory Redis for tests
|
471
|
-
DRAMATIQ_BROKER = {
|
472
|
-
"BROKER": "dramatiq.brokers.stub.StubBroker",
|
473
|
-
"OPTIONS": {},
|
474
|
-
"MIDDLEWARE": [
|
475
|
-
"dramatiq.middleware.AgeLimit",
|
476
|
-
"dramatiq.middleware.TimeLimit",
|
477
|
-
"dramatiq.middleware.Callbacks",
|
478
|
-
"dramatiq.middleware.Retries",
|
479
|
-
],
|
480
|
-
}
|
481
|
-
|
482
|
-
# Or use separate Redis DB
|
483
|
-
DRAMATIQ_BROKER = {
|
484
|
-
"BROKER": "dramatiq.brokers.redis.RedisBroker",
|
485
|
-
"OPTIONS": {
|
486
|
-
"url": "redis://localhost:6379/15", # Test DB
|
487
|
-
},
|
488
|
-
}
|
489
|
-
```
|
490
|
-
|
491
|
-
### Test Utilities
|
492
|
-
|
493
|
-
```python
|
494
|
-
# test_utils.py
|
495
|
-
import dramatiq
|
496
|
-
from dramatiq.brokers.stub import StubBroker
|
497
|
-
|
498
|
-
def setup_test_broker():
|
499
|
-
"""Set up test broker for unit tests."""
|
500
|
-
broker = StubBroker()
|
501
|
-
dramatiq.set_broker(broker)
|
502
|
-
return broker
|
503
|
-
|
504
|
-
def clear_test_queues():
|
505
|
-
"""Clear all test queues."""
|
506
|
-
broker = dramatiq.get_broker()
|
507
|
-
if hasattr(broker, 'flush_all'):
|
508
|
-
broker.flush_all()
|
509
|
-
```
|
510
|
-
|
511
|
-
---
|
512
|
-
|
513
|
-
## 📝 Configuration Validation
|
514
|
-
|
515
|
-
### Validation Rules
|
516
|
-
|
517
|
-
```python
|
518
|
-
# Built-in validation in TaskConfig
|
519
|
-
@field_validator("enabled")
|
520
|
-
@classmethod
|
521
|
-
def validate_enabled_with_environment(cls, v: bool) -> bool:
|
522
|
-
"""Validate task system can be enabled in current environment."""
|
523
|
-
if v:
|
524
|
-
try:
|
525
|
-
import dramatiq
|
526
|
-
import redis
|
527
|
-
except ImportError as e:
|
528
|
-
raise ValueError(f"Missing required dependency: {e}")
|
529
|
-
return v
|
530
|
-
|
531
|
-
@field_validator("dramatiq")
|
532
|
-
@classmethod
|
533
|
-
def validate_dramatiq_config(cls, v: DramatiqConfig) -> DramatiqConfig:
|
534
|
-
"""Validate Dramatiq configuration."""
|
535
|
-
if v.processes < 1:
|
536
|
-
raise ValueError("processes must be >= 1")
|
537
|
-
if v.threads < 1:
|
538
|
-
raise ValueError("threads must be >= 1")
|
539
|
-
if v.redis_db < 0 or v.redis_db > 15:
|
540
|
-
raise ValueError("redis_db must be 0-15")
|
541
|
-
return v
|
542
|
-
```
|
543
|
-
|
544
|
-
### Configuration Check Command
|
545
|
-
|
546
|
-
```python
|
547
|
-
# management/commands/check_tasks.py
|
548
|
-
from django.core.management.base import BaseCommand
|
549
|
-
from django_cfg.modules.django_tasks import get_task_service
|
550
|
-
|
551
|
-
class Command(BaseCommand):
|
552
|
-
help = 'Check task system configuration'
|
553
|
-
|
554
|
-
def handle(self, *args, **options):
|
555
|
-
service = get_task_service()
|
556
|
-
|
557
|
-
if not service.is_enabled():
|
558
|
-
self.stdout.write(
|
559
|
-
self.style.WARNING('Task system is disabled')
|
560
|
-
)
|
561
|
-
return
|
562
|
-
|
563
|
-
# Check configuration
|
564
|
-
if service.validate_configuration():
|
565
|
-
self.stdout.write(
|
566
|
-
self.style.SUCCESS('✅ Task system configuration is valid')
|
567
|
-
)
|
568
|
-
else:
|
569
|
-
self.stdout.write(
|
570
|
-
self.style.ERROR('❌ Task system configuration is invalid')
|
571
|
-
)
|
572
|
-
|
573
|
-
# Check Redis connection
|
574
|
-
if service.check_redis_connection():
|
575
|
-
self.stdout.write(
|
576
|
-
self.style.SUCCESS('✅ Redis connection is working')
|
577
|
-
)
|
578
|
-
else:
|
579
|
-
self.stdout.write(
|
580
|
-
self.style.ERROR('❌ Redis connection failed')
|
581
|
-
)
|
582
|
-
|
583
|
-
# Show discovered tasks
|
584
|
-
discovered = service.discover_tasks()
|
585
|
-
self.stdout.write(f"Discovered task modules: {discovered}")
|
586
|
-
```
|
587
|
-
|
588
|
-
**Usage**: `python manage.py check_tasks`
|
589
|
-
|
590
|
-
---
|
591
|
-
|
592
|
-
## 🚨 Common Configuration Mistakes %%PRIORITY:HIGH%%
|
593
|
-
|
594
|
-
### ❌ Wrong Redis Database
|
595
|
-
|
596
|
-
```python
|
597
|
-
# DON'T: Use same DB as cache
|
598
|
-
CACHES = {'default': {'LOCATION': 'redis://localhost:6379/2'}}
|
599
|
-
DRAMATIQ_REDIS_DB = 2 # Conflict!
|
600
|
-
|
601
|
-
# DO: Use separate databases
|
602
|
-
CACHES = {'default': {'LOCATION': 'redis://localhost:6379/0'}}
|
603
|
-
DRAMATIQ_REDIS_DB = 2 # Separate DB
|
604
|
-
```
|
605
|
-
|
606
|
-
### ❌ Incorrect Middleware Order
|
607
|
-
|
608
|
-
```python
|
609
|
-
# DON'T: Wrong order
|
610
|
-
middleware = [
|
611
|
-
"dramatiq.middleware.Retries", # Should be later
|
612
|
-
"dramatiq.middleware.AgeLimit", # Should be first
|
613
|
-
]
|
614
|
-
|
615
|
-
# DO: Correct order
|
616
|
-
middleware = [
|
617
|
-
"dramatiq.middleware.AgeLimit", # Age limit first
|
618
|
-
"dramatiq.middleware.TimeLimit", # Time limit second
|
619
|
-
"dramatiq.middleware.Callbacks", # Callbacks third
|
620
|
-
"dramatiq.middleware.Retries", # Retries fourth
|
621
|
-
"dramatiq.middleware.Shutdown", # Shutdown last
|
622
|
-
]
|
623
|
-
```
|
624
|
-
|
625
|
-
### ❌ Missing Environment Variables
|
626
|
-
|
627
|
-
```python
|
628
|
-
# DON'T: Hardcode settings
|
629
|
-
DRAMATIQ_BROKER = {
|
630
|
-
"OPTIONS": {"url": "redis://localhost:6379/2"}
|
631
|
-
}
|
632
|
-
|
633
|
-
# DO: Use environment variables
|
634
|
-
import os
|
635
|
-
DRAMATIQ_BROKER = {
|
636
|
-
"OPTIONS": {
|
637
|
-
"url": os.getenv('REDIS_URL', 'redis://localhost:6379/2')
|
638
|
-
}
|
639
|
-
}
|
640
|
-
```
|
641
|
-
|
642
|
-
---
|
643
|
-
|
644
|
-
## 🧠 Best Practices
|
645
|
-
|
646
|
-
### Configuration Management
|
647
|
-
|
648
|
-
1. **Environment-Specific**: Use different configs for dev/staging/prod
|
649
|
-
2. **Validation**: Always validate configuration on startup
|
650
|
-
3. **Secrets**: Use environment variables for sensitive data
|
651
|
-
4. **Documentation**: Document all configuration options
|
652
|
-
5. **Defaults**: Provide sensible defaults for all settings
|
653
|
-
|
654
|
-
### Resource Planning
|
655
|
-
|
656
|
-
1. **Memory**: Plan for ~50MB per worker process
|
657
|
-
2. **CPU**: 1 process per CPU core is good starting point
|
658
|
-
3. **Redis**: Monitor memory usage, enable persistence
|
659
|
-
4. **Network**: Consider Redis connection limits
|
660
|
-
5. **Disk**: Plan for log storage and Redis persistence
|
661
|
-
|
662
|
-
**DEPENDS_ON**: Django-CFG, Dramatiq, Redis
|
663
|
-
**USED_BY**: Development, DevOps, System administrators
|