dj-queue 0.8.1__tar.gz → 0.9.1__tar.gz
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.
- {dj_queue-0.8.1 → dj_queue-0.9.1}/PKG-INFO +65 -64
- {dj_queue-0.8.1 → dj_queue-0.9.1}/README.md +64 -61
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/admin.py +2 -12
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/config.py +11 -9
- dj_queue-0.9.1/dj_queue/contrib/prometheus.py +26 -0
- dj_queue-0.9.1/dj_queue/cron.py +1393 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/dashboard.py +15 -59
- dj_queue-0.9.1/dj_queue/metrics.py +162 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/models/recurring.py +3 -2
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/observability.py +3 -18
- dj_queue-0.9.1/dj_queue/operations/_helpers.py +190 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/operations/concurrency.py +11 -9
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/operations/jobs.py +118 -31
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/operations/recurring.py +2 -4
- dj_queue-0.9.1/dj_queue/queue_state.py +118 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/base.py +34 -14
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/scheduler.py +2 -4
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/supervisor.py +52 -182
- dj_queue-0.9.1/dj_queue/runtime/topology.py +51 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/pyproject.toml +1 -3
- dj_queue-0.8.1/dj_queue/contrib/prometheus.py +0 -128
- dj_queue-0.8.1/dj_queue/operations/_helpers.py +0 -41
- {dj_queue-0.8.1 → dj_queue-0.9.1}/LICENSE +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/__init__.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/api.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/apps.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/backend.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/contrib/__init__.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/contrib/asgi.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/contrib/gunicorn.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/db.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/exceptions.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/hooks.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/log.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/management/__init__.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/management/commands/__init__.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/management/commands/dj_queue.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/management/commands/dj_queue_health.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/management/commands/dj_queue_prune.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/migrations/0001_initial.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/migrations/0002_pause_semaphore.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/migrations/0003_recurringtask_recurringexecution.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/migrations/0004_dashboard.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/migrations/0005_remove_recurringexecution_dj_queue_recurring_executions_task_key_run_at_unique_and_more.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/migrations/0006_blockedexecution_dj_queue_bl_concurr_2d8393_idx_and_more.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/migrations/0007_recurringtask_next_run_at.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/migrations/0008_remove_blockedexecution_dj_queue_bl_concurr_1ce730_idx_and_more.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/migrations/__init__.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/models/__init__.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/models/jobs.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/models/runtime.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/operations/__init__.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/operations/_insert.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/operations/cleanup.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/operations/queues.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/routers.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/__init__.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/connection_budget.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/dispatcher.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/errors.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/interruptible.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/notify.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/pidfile.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/pool.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/procline.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/runtime/worker.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/_dashboard_process_rows.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/_dashboard_recurring_rows.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/_dashboard_section_table.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/_dashboard_semaphore_rows.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/_paginator.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/_queue_controls.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/_sortable_header_cells.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/change_form.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/change_list.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/dashboard.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/includes/fieldset.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templates/admin/dj_queue/queue_jobs.html +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templatetags/__init__.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/templatetags/dj_queue_admin.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/urls.py +0 -0
- {dj_queue-0.8.1 → dj_queue-0.9.1}/dj_queue/views.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dj-queue
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: Database-backed task queue backend for Django’s Tasks framework.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -13,9 +13,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.13
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.14
|
|
15
15
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
16
|
-
Requires-Dist: croniter>=6.2.2
|
|
17
16
|
Requires-Dist: django>=6.0.0
|
|
18
|
-
Requires-Dist: pyyaml>=6.0.3
|
|
19
17
|
Requires-Dist: psycopg>=3.3.3 ; extra == 'postgres'
|
|
20
18
|
Requires-Dist: prometheus-client>=0.4.0 ; extra == 'prometheus'
|
|
21
19
|
Requires-Python: >=3.12
|
|
@@ -408,15 +406,18 @@ unschedule_recurring_task("tenant_42_report")
|
|
|
408
406
|
|
|
409
407
|
Dynamic recurring tasks require
|
|
410
408
|
`TASKS[backend_alias]["OPTIONS"]["scheduler"]["dynamic_tasks_enabled"] = True`
|
|
411
|
-
or the equivalent `scheduler.dynamic_tasks_enabled
|
|
409
|
+
or the equivalent `scheduler.dynamic_tasks_enabled = true` in the optional TOML
|
|
412
410
|
config.
|
|
413
411
|
|
|
414
|
-
The scheduler is part of the normal `dj_queue` runtime. You do not run a
|
|
415
|
-
separate recurring service.
|
|
412
|
+
The scheduler is part of the normal `dj_queue` runtime. You do not run a separate recurring service.
|
|
416
413
|
|
|
417
414
|
Notes:
|
|
418
415
|
|
|
419
|
-
- schedules are cron expressions
|
|
416
|
+
- schedules are [Fugit](https://github.com/floraison/fugit#fugitcron)-style cron or cronish natural-language expressions
|
|
417
|
+
- cron supports five fields or an optional leading seconds field, presets like `@daily`, optional timezone suffixes, wraparound ranges, `L`/`last` and negative monthdays, weekday `#` and `%` extensions, and normal day-of-month/day-of-week OR semantics
|
|
418
|
+
- add `&` to either day field to require day-of-month and day-of-week to both match
|
|
419
|
+
- natural-language schedules support `every`, `at`, `on`, and `from` forms such as `every day at noon`, `every weekday at five`, `every 5 minutes`, `every month on day 2 at 10:00`, `from monday to friday at 9`, and `at minute 5`
|
|
420
|
+
- natural-language schedules that expand to multiple cron expressions, such as `every day at 16:15 and 18:30`, are treated as the union of those schedules
|
|
420
421
|
- recurring task keys are scoped per backend alias
|
|
421
422
|
- only dynamic tasks can be unscheduled at runtime; unscheduling a static task returns `0`
|
|
422
423
|
- Django admin exposes the same unschedule operation on recurring-task list and detail views
|
|
@@ -810,83 +811,83 @@ Configuration precedence is explicit:
|
|
|
810
811
|
|
|
811
812
|
- CLI overrides
|
|
812
813
|
- environment variables
|
|
813
|
-
-
|
|
814
|
+
- TOML file pointed to by `DJ_QUEUE_CONFIG`
|
|
814
815
|
- Django `TASKS` settings
|
|
815
816
|
|
|
816
|
-
###
|
|
817
|
+
### TOML file config
|
|
817
818
|
|
|
818
819
|
```bash
|
|
819
820
|
# via cli
|
|
820
|
-
python manage.py dj_queue --config /etc/dj_queue.
|
|
821
|
+
python manage.py dj_queue --config /etc/dj_queue.toml
|
|
821
822
|
|
|
822
823
|
# or via environment variable
|
|
823
|
-
DJ_QUEUE_CONFIG=/etc/dj_queue.
|
|
824
|
+
DJ_QUEUE_CONFIG=/etc/dj_queue.toml python manage.py dj_queue
|
|
824
825
|
```
|
|
825
826
|
|
|
826
|
-
The
|
|
827
|
+
The TOML file is an overlay on `TASKS[backend_alias]["OPTIONS"]`. It supports
|
|
827
828
|
two shapes:
|
|
828
829
|
|
|
829
830
|
- a flat mapping of option values for the selected backend alias
|
|
830
831
|
- a `backends` mapping keyed by backend alias, where only the selected alias is applied
|
|
831
832
|
|
|
833
|
+
TOML has no `null` value. Omit optional settings to keep their existing defaults.
|
|
834
|
+
|
|
832
835
|
Flat mapping example:
|
|
833
836
|
|
|
834
|
-
```
|
|
835
|
-
mode
|
|
836
|
-
database_alias
|
|
837
|
-
preserve_finished_jobs
|
|
838
|
-
clear_finished_jobs_after
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
queue_name: maintenance
|
|
865
|
-
priority: -5
|
|
866
|
-
description: nightly cleanup
|
|
837
|
+
```toml
|
|
838
|
+
mode = "async"
|
|
839
|
+
database_alias = "queue"
|
|
840
|
+
preserve_finished_jobs = true
|
|
841
|
+
clear_finished_jobs_after = 86400
|
|
842
|
+
listen_notify = true
|
|
843
|
+
silence_polling = true
|
|
844
|
+
|
|
845
|
+
[[workers]]
|
|
846
|
+
queues = ["default", "email*"]
|
|
847
|
+
threads = 8
|
|
848
|
+
processes = 1
|
|
849
|
+
polling_interval = 0.1
|
|
850
|
+
|
|
851
|
+
[[dispatchers]]
|
|
852
|
+
batch_size = 500
|
|
853
|
+
polling_interval = 1
|
|
854
|
+
concurrency_maintenance = true
|
|
855
|
+
concurrency_maintenance_interval = 600
|
|
856
|
+
|
|
857
|
+
[scheduler]
|
|
858
|
+
dynamic_tasks_enabled = true
|
|
859
|
+
polling_interval = 5
|
|
860
|
+
|
|
861
|
+
[recurring.nightly_cleanup]
|
|
862
|
+
task_path = "myapp.tasks.cleanup"
|
|
863
|
+
schedule = "0 3 * * *"
|
|
864
|
+
queue_name = "maintenance"
|
|
865
|
+
priority = -5
|
|
866
|
+
description = "nightly cleanup"
|
|
867
867
|
```
|
|
868
868
|
|
|
869
869
|
Multi-backend overlay example:
|
|
870
870
|
|
|
871
|
-
```
|
|
872
|
-
backends
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
871
|
+
```toml
|
|
872
|
+
[backends.default]
|
|
873
|
+
mode = "async"
|
|
874
|
+
database_alias = "default"
|
|
875
|
+
|
|
876
|
+
[[backends.default.workers]]
|
|
877
|
+
queues = ["default", "email*"]
|
|
878
|
+
threads = 8
|
|
879
|
+
processes = 1
|
|
880
|
+
polling_interval = 0.1
|
|
881
|
+
|
|
882
|
+
[backends.critical]
|
|
883
|
+
mode = "fork"
|
|
884
|
+
database_alias = "queue"
|
|
885
|
+
|
|
886
|
+
[[backends.critical.workers]]
|
|
887
|
+
queues = ["alerts", "critical-review"]
|
|
888
|
+
threads = 2
|
|
889
|
+
processes = 1
|
|
890
|
+
polling_interval = 0.05
|
|
890
891
|
```
|
|
891
892
|
|
|
892
893
|
Environment overrides currently supported by `dj_queue` itself:
|
|
@@ -380,15 +380,18 @@ unschedule_recurring_task("tenant_42_report")
|
|
|
380
380
|
|
|
381
381
|
Dynamic recurring tasks require
|
|
382
382
|
`TASKS[backend_alias]["OPTIONS"]["scheduler"]["dynamic_tasks_enabled"] = True`
|
|
383
|
-
or the equivalent `scheduler.dynamic_tasks_enabled
|
|
383
|
+
or the equivalent `scheduler.dynamic_tasks_enabled = true` in the optional TOML
|
|
384
384
|
config.
|
|
385
385
|
|
|
386
|
-
The scheduler is part of the normal `dj_queue` runtime. You do not run a
|
|
387
|
-
separate recurring service.
|
|
386
|
+
The scheduler is part of the normal `dj_queue` runtime. You do not run a separate recurring service.
|
|
388
387
|
|
|
389
388
|
Notes:
|
|
390
389
|
|
|
391
|
-
- schedules are cron expressions
|
|
390
|
+
- schedules are [Fugit](https://github.com/floraison/fugit#fugitcron)-style cron or cronish natural-language expressions
|
|
391
|
+
- cron supports five fields or an optional leading seconds field, presets like `@daily`, optional timezone suffixes, wraparound ranges, `L`/`last` and negative monthdays, weekday `#` and `%` extensions, and normal day-of-month/day-of-week OR semantics
|
|
392
|
+
- add `&` to either day field to require day-of-month and day-of-week to both match
|
|
393
|
+
- natural-language schedules support `every`, `at`, `on`, and `from` forms such as `every day at noon`, `every weekday at five`, `every 5 minutes`, `every month on day 2 at 10:00`, `from monday to friday at 9`, and `at minute 5`
|
|
394
|
+
- natural-language schedules that expand to multiple cron expressions, such as `every day at 16:15 and 18:30`, are treated as the union of those schedules
|
|
392
395
|
- recurring task keys are scoped per backend alias
|
|
393
396
|
- only dynamic tasks can be unscheduled at runtime; unscheduling a static task returns `0`
|
|
394
397
|
- Django admin exposes the same unschedule operation on recurring-task list and detail views
|
|
@@ -782,83 +785,83 @@ Configuration precedence is explicit:
|
|
|
782
785
|
|
|
783
786
|
- CLI overrides
|
|
784
787
|
- environment variables
|
|
785
|
-
-
|
|
788
|
+
- TOML file pointed to by `DJ_QUEUE_CONFIG`
|
|
786
789
|
- Django `TASKS` settings
|
|
787
790
|
|
|
788
|
-
###
|
|
791
|
+
### TOML file config
|
|
789
792
|
|
|
790
793
|
```bash
|
|
791
794
|
# via cli
|
|
792
|
-
python manage.py dj_queue --config /etc/dj_queue.
|
|
795
|
+
python manage.py dj_queue --config /etc/dj_queue.toml
|
|
793
796
|
|
|
794
797
|
# or via environment variable
|
|
795
|
-
DJ_QUEUE_CONFIG=/etc/dj_queue.
|
|
798
|
+
DJ_QUEUE_CONFIG=/etc/dj_queue.toml python manage.py dj_queue
|
|
796
799
|
```
|
|
797
800
|
|
|
798
|
-
The
|
|
801
|
+
The TOML file is an overlay on `TASKS[backend_alias]["OPTIONS"]`. It supports
|
|
799
802
|
two shapes:
|
|
800
803
|
|
|
801
804
|
- a flat mapping of option values for the selected backend alias
|
|
802
805
|
- a `backends` mapping keyed by backend alias, where only the selected alias is applied
|
|
803
806
|
|
|
807
|
+
TOML has no `null` value. Omit optional settings to keep their existing defaults.
|
|
808
|
+
|
|
804
809
|
Flat mapping example:
|
|
805
810
|
|
|
806
|
-
```
|
|
807
|
-
mode
|
|
808
|
-
database_alias
|
|
809
|
-
preserve_finished_jobs
|
|
810
|
-
clear_finished_jobs_after
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
queue_name: maintenance
|
|
837
|
-
priority: -5
|
|
838
|
-
description: nightly cleanup
|
|
811
|
+
```toml
|
|
812
|
+
mode = "async"
|
|
813
|
+
database_alias = "queue"
|
|
814
|
+
preserve_finished_jobs = true
|
|
815
|
+
clear_finished_jobs_after = 86400
|
|
816
|
+
listen_notify = true
|
|
817
|
+
silence_polling = true
|
|
818
|
+
|
|
819
|
+
[[workers]]
|
|
820
|
+
queues = ["default", "email*"]
|
|
821
|
+
threads = 8
|
|
822
|
+
processes = 1
|
|
823
|
+
polling_interval = 0.1
|
|
824
|
+
|
|
825
|
+
[[dispatchers]]
|
|
826
|
+
batch_size = 500
|
|
827
|
+
polling_interval = 1
|
|
828
|
+
concurrency_maintenance = true
|
|
829
|
+
concurrency_maintenance_interval = 600
|
|
830
|
+
|
|
831
|
+
[scheduler]
|
|
832
|
+
dynamic_tasks_enabled = true
|
|
833
|
+
polling_interval = 5
|
|
834
|
+
|
|
835
|
+
[recurring.nightly_cleanup]
|
|
836
|
+
task_path = "myapp.tasks.cleanup"
|
|
837
|
+
schedule = "0 3 * * *"
|
|
838
|
+
queue_name = "maintenance"
|
|
839
|
+
priority = -5
|
|
840
|
+
description = "nightly cleanup"
|
|
839
841
|
```
|
|
840
842
|
|
|
841
843
|
Multi-backend overlay example:
|
|
842
844
|
|
|
843
|
-
```
|
|
844
|
-
backends
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
845
|
+
```toml
|
|
846
|
+
[backends.default]
|
|
847
|
+
mode = "async"
|
|
848
|
+
database_alias = "default"
|
|
849
|
+
|
|
850
|
+
[[backends.default.workers]]
|
|
851
|
+
queues = ["default", "email*"]
|
|
852
|
+
threads = 8
|
|
853
|
+
processes = 1
|
|
854
|
+
polling_interval = 0.1
|
|
855
|
+
|
|
856
|
+
[backends.critical]
|
|
857
|
+
mode = "fork"
|
|
858
|
+
database_alias = "queue"
|
|
859
|
+
|
|
860
|
+
[[backends.critical.workers]]
|
|
861
|
+
queues = ["alerts", "critical-review"]
|
|
862
|
+
threads = 2
|
|
863
|
+
processes = 1
|
|
864
|
+
polling_interval = 0.05
|
|
862
865
|
```
|
|
863
866
|
|
|
864
867
|
Environment overrides currently supported by `dj_queue` itself:
|
|
@@ -35,6 +35,7 @@ from dj_queue.operations.jobs import (
|
|
|
35
35
|
retry_failed_job,
|
|
36
36
|
retry_failed_jobs,
|
|
37
37
|
)
|
|
38
|
+
from dj_queue.queue_state import status_rank_expression
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class DjQueueFirstAdminSite(admin.AdminSite):
|
|
@@ -503,18 +504,7 @@ class JobAdmin(HiddenSidebarAdminMixin, admin.ModelAdmin):
|
|
|
503
504
|
|
|
504
505
|
def get_queryset(self, request):
|
|
505
506
|
queryset = super().get_queryset(request)
|
|
506
|
-
return queryset.annotate(
|
|
507
|
-
status_rank=Case(
|
|
508
|
-
When(ready_execution__isnull=False, then=Value(0)),
|
|
509
|
-
When(scheduled_execution__isnull=False, then=Value(1)),
|
|
510
|
-
When(claimed_execution__isnull=False, then=Value(2)),
|
|
511
|
-
When(blocked_execution__isnull=False, then=Value(3)),
|
|
512
|
-
When(failed_execution__isnull=False, then=Value(4)),
|
|
513
|
-
When(finished_at__isnull=False, then=Value(5)),
|
|
514
|
-
default=Value(99),
|
|
515
|
-
output_field=IntegerField(),
|
|
516
|
-
)
|
|
517
|
-
)
|
|
507
|
+
return queryset.annotate(status_rank=status_rank_expression())
|
|
518
508
|
|
|
519
509
|
@admin.display(description="status", ordering="status_rank")
|
|
520
510
|
def display_status(self, obj):
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import math
|
|
3
3
|
import os
|
|
4
|
+
import tomllib
|
|
4
5
|
import warnings
|
|
5
6
|
from collections.abc import Mapping, Sequence
|
|
6
7
|
from dataclasses import asdict, dataclass, field, replace
|
|
@@ -8,12 +9,12 @@ from functools import lru_cache
|
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
from typing import Any
|
|
10
11
|
|
|
11
|
-
import yaml
|
|
12
|
-
from croniter import croniter
|
|
13
12
|
from django.conf import settings
|
|
14
13
|
from django.core.exceptions import ImproperlyConfigured
|
|
15
14
|
from django.utils.module_loading import import_string
|
|
16
15
|
|
|
16
|
+
from dj_queue.cron import is_valid_cron
|
|
17
|
+
|
|
17
18
|
DEFAULT_WORKER = {
|
|
18
19
|
"queues": "*",
|
|
19
20
|
"threads": 3,
|
|
@@ -319,7 +320,7 @@ def _resolved_options(
|
|
|
319
320
|
resolved_options.update(settings_options)
|
|
320
321
|
|
|
321
322
|
config_path = cli_overrides.get("config") or env.get("DJ_QUEUE_CONFIG")
|
|
322
|
-
resolved_options.update(
|
|
323
|
+
resolved_options.update(_load_toml_options(config_path, backend_alias=backend_alias))
|
|
323
324
|
|
|
324
325
|
env_mode = env.get("DJ_QUEUE_MODE")
|
|
325
326
|
if env_mode is not None:
|
|
@@ -332,15 +333,16 @@ def _resolved_options(
|
|
|
332
333
|
return resolved_options
|
|
333
334
|
|
|
334
335
|
|
|
335
|
-
def
|
|
336
|
+
def _load_toml_options(config_path: Any, *, backend_alias: str) -> dict[str, Any]:
|
|
336
337
|
if not config_path:
|
|
337
338
|
return {}
|
|
338
339
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
340
|
+
try:
|
|
341
|
+
config_payload = tomllib.loads(Path(config_path).read_text(encoding="utf-8"))
|
|
342
|
+
except tomllib.TOMLDecodeError as exc:
|
|
343
|
+
raise ImproperlyConfigured(f"DJ_QUEUE_CONFIG TOML is invalid: {exc}") from exc
|
|
342
344
|
if not isinstance(config_payload, dict):
|
|
343
|
-
raise ImproperlyConfigured("DJ_QUEUE_CONFIG must point to a
|
|
345
|
+
raise ImproperlyConfigured("DJ_QUEUE_CONFIG must point to a TOML mapping")
|
|
344
346
|
|
|
345
347
|
raw_backends = config_payload.get("backends")
|
|
346
348
|
if raw_backends is None:
|
|
@@ -519,7 +521,7 @@ def _build_recurring_config(raw_recurring: Any) -> dict[str, RecurringTaskConfig
|
|
|
519
521
|
schedule = raw_entry.get("schedule")
|
|
520
522
|
if not task_path or not schedule:
|
|
521
523
|
raise ImproperlyConfigured(f"recurring task {key!r} requires task_path and schedule")
|
|
522
|
-
if not
|
|
524
|
+
if not is_valid_cron(str(schedule)):
|
|
523
525
|
raise ImproperlyConfigured(f"recurring task {key!r} has an invalid cron schedule")
|
|
524
526
|
|
|
525
527
|
recurring[str(key)] = RecurringTaskConfig(
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
try:
|
|
2
|
+
from prometheus_client import CollectorRegistry, generate_latest
|
|
3
|
+
from prometheus_client.core import GaugeMetricFamily
|
|
4
|
+
except ImportError:
|
|
5
|
+
DjQueueCollector = None
|
|
6
|
+
registry = None
|
|
7
|
+
generate_latest = None
|
|
8
|
+
else:
|
|
9
|
+
from dj_queue.metrics import metric_families
|
|
10
|
+
|
|
11
|
+
class DjQueueCollector:
|
|
12
|
+
"""Prometheus collector that exposes dj_queue metrics from the shared observability snapshot."""
|
|
13
|
+
|
|
14
|
+
def collect(self):
|
|
15
|
+
for family in metric_families():
|
|
16
|
+
gauge = GaugeMetricFamily(
|
|
17
|
+
family.name,
|
|
18
|
+
family.help_text,
|
|
19
|
+
labels=list(family.labels),
|
|
20
|
+
)
|
|
21
|
+
for sample in family.samples:
|
|
22
|
+
gauge.add_metric(list(sample.labels), sample.value)
|
|
23
|
+
yield gauge
|
|
24
|
+
|
|
25
|
+
registry = CollectorRegistry(auto_describe=False)
|
|
26
|
+
registry.register(DjQueueCollector())
|