dj-queue 0.14.2__tar.gz → 0.15.0__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.14.2 → dj_queue-0.15.0}/PKG-INFO +4 -4
- {dj_queue-0.14.2 → dj_queue-0.15.0}/README.md +3 -3
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/config.py +29 -10
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/contrib/asgi.py +2 -7
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/contrib/gunicorn.py +6 -8
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/dashboard.py +5 -11
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/dashboard_tables.py +2 -2
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/db.py +3 -3
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/hooks.py +2 -7
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/log.py +2 -12
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/management/commands/dj_queue_prune.py +5 -3
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/metrics.py +2 -2
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/_helpers.py +1 -38
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/claiming.py +19 -35
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/cleanup.py +10 -14
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/concurrency.py +58 -131
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/dispatch.py +8 -9
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/enqueue.py +25 -46
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/execution.py +23 -49
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/jobs.py +64 -196
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/queues.py +3 -3
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/recovery.py +26 -66
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/recurring.py +29 -49
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/queue_state.py +14 -84
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/reads/queues.py +13 -2
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/base.py +17 -64
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/connection_budget.py +1 -2
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/dispatcher.py +2 -33
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/errors.py +4 -6
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/notify.py +15 -34
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/pidfile.py +0 -7
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/pool.py +1 -12
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/scheduler.py +9 -75
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/supervisor.py +47 -180
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/worker.py +3 -38
- dj_queue-0.15.0/dj_queue/sql/common.py +114 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/dashboard.html +0 -1
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/queue_jobs.html +2 -2
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/wakeup.py +1 -4
- dj_queue-0.15.0/pyproject.toml +92 -0
- dj_queue-0.14.2/pyproject.toml → dj_queue-0.15.0/pyproject.toml.orig +2 -2
- dj_queue-0.14.2/dj_queue/runtime/procline.py +0 -11
- dj_queue-0.14.2/dj_queue/sql/common.py +0 -220
- {dj_queue-0.14.2 → dj_queue-0.15.0}/LICENSE +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/admin.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/admin_permissions.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/api.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/apps.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/backend.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/contrib/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/contrib/prometheus.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/cron.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/dashboard_actions.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/exceptions.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/health.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/management/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/management/commands/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/management/commands/dj_queue.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/management/commands/dj_queue_health.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/management/commands/dj_queue_postgres_autovacuum.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0001_initial.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0002_pause_semaphore.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0003_recurringtask_recurringexecution.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0004_dashboard.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0005_remove_recurringexecution_dj_queue_recurring_executions_task_key_run_at_unique_and_more.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0006_blockedexecution_dj_queue_bl_concurr_2d8393_idx_and_more.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0007_recurringtask_next_run_at.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0008_remove_blockedexecution_dj_queue_bl_concurr_1ce730_idx_and_more.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0009_remove_process_dj_queue_processes_name_supervisor_unique_and_more.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0010_remove_process_djq_pr_name_parent_uniq_and_more.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0011_remove_blockedexecution_djq_bl_b_conc_idx_and_more.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0012_job_djq_jobs_b_queue_id_idx_job_djq_jobs_b_conc_idx_and_more.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0013_failedexecution_retry_at_and_more.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0014_semaphore_active_count.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0015_job_concurrency_duration.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0016_job_concurrency_limit.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0017_job_concurrency_on_conflict.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/0018_recurringexecution_intended_job_id.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/migrations/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/models/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/models/jobs.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/models/recurring.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/models/runtime.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/observability.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/operations/_insert.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/postgres_diagnostics.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/py.typed +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/queue_selectors.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/reads/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/reads/controls.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/reads/jobs.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/reads/processes.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/routers.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/interruptible.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/runtime/topology.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/sql/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/sql/mysql.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/sql/postgres.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/sql/sqlite.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/sql/state.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/static/admin/dj_queue/breadcrumbs.css +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/task_results.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/_dashboard_process_rows.html +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/_dashboard_recurring_rows.html +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/_dashboard_section_table.html +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/_dashboard_semaphore_rows.html +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/_paginator.html +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/_queue_controls.html +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/_sortable_header_cells.html +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/change_form.html +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/change_list.html +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templates/admin/dj_queue/includes/fieldset.html +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templatetags/__init__.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/templatetags/dj_queue_admin.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/dj_queue/urls.py +0 -0
- {dj_queue-0.14.2 → dj_queue-0.15.0}/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.15.0
|
|
4
4
|
Summary: Database-backed task queue backend for Django’s Tasks framework.
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -369,7 +369,7 @@ The database floor follows the selected Django release:
|
|
|
369
369
|
| 6.1 | 15+ | 8.4+ | 10.11+ | 3.37+ |
|
|
370
370
|
|
|
371
371
|
CI keeps the floors on Django 6.0 and tests newer database lines through
|
|
372
|
-
PostgreSQL 18, MySQL 9.7 and 26.
|
|
372
|
+
PostgreSQL 18, MySQL 9.7 and 26.10, and MariaDB 12.3 and 13.0. Django 6.1 only runs with
|
|
373
373
|
database versions that it supports.
|
|
374
374
|
|
|
375
375
|
For MySQL or MariaDB, install and configure a Django-compatible driver following Django's database docs.
|
|
@@ -939,13 +939,14 @@ Global options:
|
|
|
939
939
|
| `default_concurrency_duration` | `180` | default semaphore TTL in seconds |
|
|
940
940
|
| `use_skip_locked` | `true` | use `SKIP LOCKED` when the active backend supports it |
|
|
941
941
|
| `listen_notify` | `true` | PostgreSQL-only worker wakeup optimization layered on top of polling |
|
|
942
|
-
| `silence_polling` | `true` | suppress `dj_queue`'s own poll-cycle noise without mutating Django's global SQL logger |
|
|
943
942
|
| `process_heartbeat_interval` | `60` | seconds between process heartbeat writes |
|
|
944
943
|
| `process_alive_threshold` | `300` | seconds before a process row is stale |
|
|
945
944
|
| `shutdown_timeout` | `5` | graceful drain window before standalone shutdown gives up on waiting |
|
|
946
945
|
| `supervisor_pidfile` | `null` | optional pidfile guard for standalone supervisors |
|
|
947
946
|
| `on_thread_error` | `null` | dotted callback path for runtime infrastructure exceptions |
|
|
948
947
|
|
|
948
|
+
`silence_polling` is deprecated: it never had an effect, still loads with a `FutureWarning`, and will be rejected in a future release, so remove it from settings and TOML overlays.
|
|
949
|
+
|
|
949
950
|
Worker entry options:
|
|
950
951
|
|
|
951
952
|
| Option | Default | Meaning |
|
|
@@ -1027,7 +1028,6 @@ database_alias = "queue"
|
|
|
1027
1028
|
preserve_finished_jobs = true
|
|
1028
1029
|
clear_finished_jobs_after = 86400
|
|
1029
1030
|
listen_notify = true
|
|
1030
|
-
silence_polling = true
|
|
1031
1031
|
|
|
1032
1032
|
[[workers]]
|
|
1033
1033
|
queues = ["default", "email*"]
|
|
@@ -342,7 +342,7 @@ The database floor follows the selected Django release:
|
|
|
342
342
|
| 6.1 | 15+ | 8.4+ | 10.11+ | 3.37+ |
|
|
343
343
|
|
|
344
344
|
CI keeps the floors on Django 6.0 and tests newer database lines through
|
|
345
|
-
PostgreSQL 18, MySQL 9.7 and 26.
|
|
345
|
+
PostgreSQL 18, MySQL 9.7 and 26.10, and MariaDB 12.3 and 13.0. Django 6.1 only runs with
|
|
346
346
|
database versions that it supports.
|
|
347
347
|
|
|
348
348
|
For MySQL or MariaDB, install and configure a Django-compatible driver following Django's database docs.
|
|
@@ -912,13 +912,14 @@ Global options:
|
|
|
912
912
|
| `default_concurrency_duration` | `180` | default semaphore TTL in seconds |
|
|
913
913
|
| `use_skip_locked` | `true` | use `SKIP LOCKED` when the active backend supports it |
|
|
914
914
|
| `listen_notify` | `true` | PostgreSQL-only worker wakeup optimization layered on top of polling |
|
|
915
|
-
| `silence_polling` | `true` | suppress `dj_queue`'s own poll-cycle noise without mutating Django's global SQL logger |
|
|
916
915
|
| `process_heartbeat_interval` | `60` | seconds between process heartbeat writes |
|
|
917
916
|
| `process_alive_threshold` | `300` | seconds before a process row is stale |
|
|
918
917
|
| `shutdown_timeout` | `5` | graceful drain window before standalone shutdown gives up on waiting |
|
|
919
918
|
| `supervisor_pidfile` | `null` | optional pidfile guard for standalone supervisors |
|
|
920
919
|
| `on_thread_error` | `null` | dotted callback path for runtime infrastructure exceptions |
|
|
921
920
|
|
|
921
|
+
`silence_polling` is deprecated: it never had an effect, still loads with a `FutureWarning`, and will be rejected in a future release, so remove it from settings and TOML overlays.
|
|
922
|
+
|
|
922
923
|
Worker entry options:
|
|
923
924
|
|
|
924
925
|
| Option | Default | Meaning |
|
|
@@ -1000,7 +1001,6 @@ database_alias = "queue"
|
|
|
1000
1001
|
preserve_finished_jobs = true
|
|
1001
1002
|
clear_finished_jobs_after = 86400
|
|
1002
1003
|
listen_notify = true
|
|
1003
|
-
silence_polling = true
|
|
1004
1004
|
|
|
1005
1005
|
[[workers]]
|
|
1006
1006
|
queues = ["default", "email*"]
|
|
@@ -62,12 +62,13 @@ DEFAULT_OPTIONS = {
|
|
|
62
62
|
"database_alias": "default",
|
|
63
63
|
"use_skip_locked": True,
|
|
64
64
|
"listen_notify": True,
|
|
65
|
-
"silence_polling": True,
|
|
66
65
|
"async_thread_sensitive": False,
|
|
67
66
|
"async_close_connections": False,
|
|
68
67
|
"on_thread_error": None,
|
|
69
68
|
}
|
|
70
69
|
|
|
70
|
+
DEPRECATED_OPTIONS = ("silence_polling",)
|
|
71
|
+
|
|
71
72
|
DJ_QUEUE_BACKEND_PATH = "dj_queue.backend.DjQueueBackend"
|
|
72
73
|
|
|
73
74
|
TRUTHY_ENV_VALUES = {"1", "true", "yes", "on"}
|
|
@@ -138,7 +139,6 @@ class BackendConfig(ConfigValue):
|
|
|
138
139
|
database_alias: str = "default"
|
|
139
140
|
use_skip_locked: bool = True
|
|
140
141
|
listen_notify: bool = True
|
|
141
|
-
silence_polling: bool = True
|
|
142
142
|
async_thread_sensitive: bool = False
|
|
143
143
|
async_close_connections: bool = False
|
|
144
144
|
on_thread_error: str | None = None
|
|
@@ -150,6 +150,14 @@ class BackendConfig(ConfigValue):
|
|
|
150
150
|
def has_scheduler_work(self) -> bool:
|
|
151
151
|
return self.scheduler is not None
|
|
152
152
|
|
|
153
|
+
@property
|
|
154
|
+
def cleanup_enabled(self) -> bool:
|
|
155
|
+
return (
|
|
156
|
+
(self.preserve_finished_jobs and self.clear_finished_jobs_after is not None)
|
|
157
|
+
or self.clear_failed_jobs_after is not None
|
|
158
|
+
or self.clear_recurring_executions_after is not None
|
|
159
|
+
)
|
|
160
|
+
|
|
153
161
|
|
|
154
162
|
def resolve_backend_config(
|
|
155
163
|
backend_alias: str = "default", config: BackendConfig | None = None
|
|
@@ -293,7 +301,6 @@ def _build_backend_config(
|
|
|
293
301
|
database_alias=_string_option(resolved_options["database_alias"], "database_alias"),
|
|
294
302
|
use_skip_locked=_bool_option(resolved_options["use_skip_locked"], "use_skip_locked"),
|
|
295
303
|
listen_notify=_bool_option(resolved_options["listen_notify"], "listen_notify"),
|
|
296
|
-
silence_polling=_bool_option(resolved_options["silence_polling"], "silence_polling"),
|
|
297
304
|
async_thread_sensitive=_bool_option(
|
|
298
305
|
resolved_options["async_thread_sensitive"], "async_thread_sensitive"
|
|
299
306
|
),
|
|
@@ -389,6 +396,7 @@ def _resolved_options(
|
|
|
389
396
|
settings_options = backend_block.get("OPTIONS", {})
|
|
390
397
|
if not isinstance(settings_options, Mapping):
|
|
391
398
|
raise ImproperlyConfigured("TASKS backend OPTIONS must be a mapping")
|
|
399
|
+
settings_options = _without_deprecated_options(settings_options, "TASKS backend OPTIONS")
|
|
392
400
|
_reject_unknown_options(settings_options, DEFAULT_OPTIONS, "TASKS backend OPTIONS")
|
|
393
401
|
|
|
394
402
|
resolved_options = dict(DEFAULT_OPTIONS)
|
|
@@ -444,11 +452,27 @@ def _load_toml_options(config_path: Any, *, backend_alias: str) -> dict[str, Any
|
|
|
444
452
|
|
|
445
453
|
|
|
446
454
|
def _validated_toml_options(options: Mapping[str, Any], setting_name: str) -> dict[str, Any]:
|
|
447
|
-
options =
|
|
455
|
+
options = _without_deprecated_options(
|
|
456
|
+
_json_serializable_options(options, setting_name), setting_name
|
|
457
|
+
)
|
|
448
458
|
_reject_unknown_options(options, DEFAULT_OPTIONS, setting_name)
|
|
449
459
|
return options
|
|
450
460
|
|
|
451
461
|
|
|
462
|
+
def _without_deprecated_options(options: Mapping[str, Any], setting_name: str) -> dict[str, Any]:
|
|
463
|
+
options = dict(options)
|
|
464
|
+
for name in DEPRECATED_OPTIONS:
|
|
465
|
+
if name in options:
|
|
466
|
+
del options[name]
|
|
467
|
+
warnings.warn(
|
|
468
|
+
f"dj_queue {setting_name} option {name!r} has no effect and will be rejected in a "
|
|
469
|
+
"future release; remove it",
|
|
470
|
+
FutureWarning,
|
|
471
|
+
stacklevel=2,
|
|
472
|
+
)
|
|
473
|
+
return options
|
|
474
|
+
|
|
475
|
+
|
|
452
476
|
def _json_serializable_options(options: Mapping[str, Any], setting_name: str) -> dict[str, Any]:
|
|
453
477
|
try:
|
|
454
478
|
json.dumps(options, sort_keys=True, separators=(",", ":"), allow_nan=False)
|
|
@@ -669,12 +693,7 @@ def _build_recurring_config(
|
|
|
669
693
|
|
|
670
694
|
|
|
671
695
|
def _scheduler_has_work(config: BackendConfig) -> bool:
|
|
672
|
-
|
|
673
|
-
(config.preserve_finished_jobs and config.clear_finished_jobs_after is not None)
|
|
674
|
-
or config.clear_failed_jobs_after is not None
|
|
675
|
-
or config.clear_recurring_executions_after is not None
|
|
676
|
-
)
|
|
677
|
-
return config.scheduler.dynamic_tasks_enabled or bool(config.recurring) or has_cleanup
|
|
696
|
+
return config.scheduler.dynamic_tasks_enabled or bool(config.recurring) or config.cleanup_enabled
|
|
678
697
|
|
|
679
698
|
|
|
680
699
|
def _as_queue_selectors(value: Any) -> tuple[str, ...]:
|
|
@@ -24,14 +24,9 @@ class DjQueueLifespan:
|
|
|
24
24
|
self.supervisor is not None and self._poll_stop is not None and not self._poll_stop.is_set()
|
|
25
25
|
):
|
|
26
26
|
try:
|
|
27
|
-
|
|
28
|
-
keep_polling = await asyncio.to_thread(poll_once)
|
|
27
|
+
keep_polling = await asyncio.to_thread(self.supervisor.poll_once_if_running)
|
|
29
28
|
except Exception as error:
|
|
30
|
-
handle_thread_error(
|
|
31
|
-
error,
|
|
32
|
-
context="supervisor.run",
|
|
33
|
-
backend_alias=self.supervisor.backend_alias,
|
|
34
|
-
)
|
|
29
|
+
handle_thread_error(error, context="supervisor.run", config=self.supervisor.backend_config)
|
|
35
30
|
keep_polling = True
|
|
36
31
|
|
|
37
32
|
if keep_polling is False:
|
|
@@ -4,6 +4,7 @@ import tempfile
|
|
|
4
4
|
import threading
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
+
from dj_queue.config import load_backend_config
|
|
7
8
|
from dj_queue.runtime.errors import handle_thread_error
|
|
8
9
|
|
|
9
10
|
LOCK_PATH_PREFIX = "dj_queue_gunicorn_supervisor"
|
|
@@ -68,15 +69,10 @@ def _start_embedded_supervisor(worker, *, backend_alias="default"):
|
|
|
68
69
|
stop_event = worker._dj_queue_supervisor_poll_stop
|
|
69
70
|
while stop_event.wait(supervisor.polling_interval) is False:
|
|
70
71
|
try:
|
|
71
|
-
|
|
72
|
-
if poll_once() is False:
|
|
72
|
+
if supervisor.poll_once_if_running() is False:
|
|
73
73
|
return
|
|
74
74
|
except Exception as error:
|
|
75
|
-
handle_thread_error(
|
|
76
|
-
error,
|
|
77
|
-
context="supervisor.run",
|
|
78
|
-
backend_alias=supervisor.backend_alias,
|
|
79
|
-
)
|
|
75
|
+
handle_thread_error(error, context="supervisor.run", config=supervisor.backend_config)
|
|
80
76
|
|
|
81
77
|
poll_thread = threading.Thread(target=poll_supervisor, daemon=True)
|
|
82
78
|
worker._dj_queue_supervisor_poll_thread = poll_thread
|
|
@@ -94,7 +90,9 @@ def _start_lock_retry_loop(worker, *, backend_alias="default"):
|
|
|
94
90
|
try:
|
|
95
91
|
supervisor = _start_embedded_supervisor(worker, backend_alias=backend_alias)
|
|
96
92
|
except Exception as error:
|
|
97
|
-
handle_thread_error(
|
|
93
|
+
handle_thread_error(
|
|
94
|
+
error, context="gunicorn.supervisor", config=load_backend_config(backend_alias)
|
|
95
|
+
)
|
|
98
96
|
continue
|
|
99
97
|
if supervisor is not None:
|
|
100
98
|
retry_stop.set()
|
|
@@ -12,9 +12,9 @@ from dj_queue.dashboard_tables import OVERVIEW_COUNT_LABELS, OVERVIEW_SORTS, QUE
|
|
|
12
12
|
from dj_queue.db import database_capabilities, get_database_alias
|
|
13
13
|
from dj_queue.observability import backend_choices, configured_backend_aliases
|
|
14
14
|
from dj_queue.queue_state import (
|
|
15
|
-
QUEUE_STATE_DEFINITIONS,
|
|
16
15
|
QUEUE_STATE_LABELS,
|
|
17
16
|
QUEUE_STATES,
|
|
17
|
+
SUMMARY_STATE_DEFINITIONS,
|
|
18
18
|
queue_state_count_key,
|
|
19
19
|
queue_state_queryset,
|
|
20
20
|
)
|
|
@@ -154,13 +154,13 @@ def queue_page_context(*, backend_alias, queue_name, state, page_number, query_p
|
|
|
154
154
|
)
|
|
155
155
|
queue_info = QueueInfo(queue_name, backend_alias=backend_alias)
|
|
156
156
|
state_counts = {
|
|
157
|
-
definition.name: queue_row[definition.count_key] for definition in
|
|
157
|
+
definition.name: queue_row[definition.count_key] for definition in SUMMARY_STATE_DEFINITIONS
|
|
158
158
|
}
|
|
159
159
|
state_tabs = [
|
|
160
160
|
{
|
|
161
161
|
"name": state_name,
|
|
162
162
|
"label": label,
|
|
163
|
-
"count": state_counts
|
|
163
|
+
"count": state_counts.get(state_name),
|
|
164
164
|
"selected": state_name == state,
|
|
165
165
|
}
|
|
166
166
|
for state_name, label in QUEUE_STATES
|
|
@@ -239,7 +239,6 @@ def _summary_cards(*, backend_alias, queue_rows, process_rows, recurring_count,
|
|
|
239
239
|
scheduled_count = sum(row[queue_state_count_key("scheduled")] for row in queue_rows)
|
|
240
240
|
failed_count = sum(row[queue_state_count_key("failed")] for row in queue_rows)
|
|
241
241
|
blocked_count = sum(row[queue_state_count_key("blocked")] for row in queue_rows)
|
|
242
|
-
invalid_count = sum(row[queue_state_count_key("invalid")] for row in queue_rows)
|
|
243
242
|
live_processes = sum(1 for row in process_rows if row["is_live"])
|
|
244
243
|
stale_processes = len(process_rows) - live_processes
|
|
245
244
|
|
|
@@ -256,22 +255,17 @@ def _summary_cards(*, backend_alias, queue_rows, process_rows, recurring_count,
|
|
|
256
255
|
},
|
|
257
256
|
{
|
|
258
257
|
"label": "attention",
|
|
259
|
-
"value": failed_count + blocked_count
|
|
258
|
+
"value": failed_count + blocked_count,
|
|
260
259
|
"detail_parts": (
|
|
261
260
|
{
|
|
262
261
|
"label": f"{failed_count} failed",
|
|
263
262
|
"url": _job_changelist_url(backend_alias=backend_alias, status="failed"),
|
|
264
263
|
},
|
|
265
|
-
{"label": "
|
|
264
|
+
{"label": "and"},
|
|
266
265
|
{
|
|
267
266
|
"label": f"{blocked_count} blocked",
|
|
268
267
|
"url": _job_changelist_url(backend_alias=backend_alias, status="blocked"),
|
|
269
268
|
},
|
|
270
|
-
{"label": "and"},
|
|
271
|
-
{
|
|
272
|
-
"label": f"{invalid_count} invalid",
|
|
273
|
-
"url": _job_changelist_url(backend_alias=backend_alias, status="invalid"),
|
|
274
|
-
},
|
|
275
269
|
),
|
|
276
270
|
},
|
|
277
271
|
{
|
|
@@ -7,7 +7,7 @@ from django.core.paginator import Paginator
|
|
|
7
7
|
from django.db.models import F
|
|
8
8
|
|
|
9
9
|
from dj_queue.queue_state import (
|
|
10
|
-
|
|
10
|
+
SUMMARY_STATE_DEFINITIONS,
|
|
11
11
|
)
|
|
12
12
|
|
|
13
13
|
OVERVIEW_COUNT_LABELS = {
|
|
@@ -30,7 +30,7 @@ OVERVIEW_SORTS = {
|
|
|
30
30
|
"key": definition.count_key,
|
|
31
31
|
"default_desc": True,
|
|
32
32
|
}
|
|
33
|
-
for definition in
|
|
33
|
+
for definition in SUMMARY_STATE_DEFINITIONS
|
|
34
34
|
},
|
|
35
35
|
"paused": {"label": "paused", "key": "paused", "default_desc": True},
|
|
36
36
|
"latency": {"label": "latency", "key": "latency_seconds", "default_desc": True},
|
|
@@ -8,7 +8,7 @@ from django.core.exceptions import ImproperlyConfigured
|
|
|
8
8
|
from django.db import DEFAULT_DB_ALIAS, connections
|
|
9
9
|
from django.db.utils import OperationalError
|
|
10
10
|
|
|
11
|
-
from dj_queue.config import
|
|
11
|
+
from dj_queue.config import load_backend_config
|
|
12
12
|
|
|
13
13
|
TRANSIENT_DATABASE_RETRY_ATTEMPTS = 3
|
|
14
14
|
TRANSIENT_DATABASE_RETRY_SLEEP_BASE = 0.01
|
|
@@ -45,8 +45,8 @@ class DatabaseCapabilities:
|
|
|
45
45
|
uses_serialized_writes: bool
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def get_database_alias(backend_alias: str = "default"
|
|
49
|
-
return
|
|
48
|
+
def get_database_alias(backend_alias: str = "default") -> str:
|
|
49
|
+
return load_backend_config(backend_alias).database_alias
|
|
50
50
|
|
|
51
51
|
|
|
52
52
|
def locked_queryset(qs, use_skip_locked: bool = True):
|
|
@@ -67,17 +67,12 @@ def on_scheduler_exit(fn: Callable[..., Any]):
|
|
|
67
67
|
return register_hook("scheduler.exit", fn)
|
|
68
68
|
|
|
69
69
|
|
|
70
|
-
def fire_hooks(event: str, process: Any, *,
|
|
70
|
+
def fire_hooks(event: str, process: Any, *, config):
|
|
71
71
|
for hook in tuple(_hooks.get(event, ())):
|
|
72
72
|
try:
|
|
73
73
|
hook(process)
|
|
74
74
|
except Exception as error:
|
|
75
|
-
handle_thread_error(
|
|
76
|
-
error,
|
|
77
|
-
context=f"hook:{event}",
|
|
78
|
-
backend_alias=backend_alias,
|
|
79
|
-
config=config,
|
|
80
|
-
)
|
|
75
|
+
handle_thread_error(error, context=f"hook:{event}", config=config)
|
|
81
76
|
|
|
82
77
|
|
|
83
78
|
def clear_hooks(event: str | None = None):
|
|
@@ -1,19 +1,10 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
|
-
from dj_queue.config import load_backend_config
|
|
5
|
-
|
|
6
4
|
logger = logging.getLogger("dj_queue")
|
|
7
5
|
|
|
8
6
|
|
|
9
|
-
def event_logging_enabled(
|
|
10
|
-
level: int = logging.INFO,
|
|
11
|
-
*,
|
|
12
|
-
backend_alias: str = "default",
|
|
13
|
-
polling: bool = False,
|
|
14
|
-
):
|
|
15
|
-
if polling and load_backend_config(backend_alias).silence_polling:
|
|
16
|
-
return False
|
|
7
|
+
def event_logging_enabled(level: int = logging.INFO):
|
|
17
8
|
return logger.isEnabledFor(level)
|
|
18
9
|
|
|
19
10
|
|
|
@@ -22,10 +13,9 @@ def log_event(
|
|
|
22
13
|
*,
|
|
23
14
|
level: int = logging.INFO,
|
|
24
15
|
backend_alias: str = "default",
|
|
25
|
-
polling: bool = False,
|
|
26
16
|
**fields: Any,
|
|
27
17
|
):
|
|
28
|
-
if not event_logging_enabled(level
|
|
18
|
+
if not event_logging_enabled(level):
|
|
29
19
|
return
|
|
30
20
|
|
|
31
21
|
logger.log(
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from django.core.exceptions import ImproperlyConfigured
|
|
2
2
|
from django.core.management.base import BaseCommand, CommandError
|
|
3
3
|
|
|
4
|
+
from dj_queue.config import load_backend_config
|
|
4
5
|
from dj_queue.operations.cleanup import (
|
|
5
6
|
clear_failed_jobs,
|
|
6
7
|
clear_finished_jobs,
|
|
@@ -57,23 +58,24 @@ class Command(BaseCommand):
|
|
|
57
58
|
raise CommandError("--batch-size must be positive")
|
|
58
59
|
|
|
59
60
|
try:
|
|
61
|
+
config = load_backend_config(options["backend"])
|
|
60
62
|
finished_deleted = clear_finished_jobs(
|
|
61
63
|
older_than=options["older_than"],
|
|
62
64
|
task_path=options["task_path"],
|
|
63
65
|
batch_size=batch_size,
|
|
64
|
-
|
|
66
|
+
config=config,
|
|
65
67
|
)
|
|
66
68
|
failed_deleted = clear_failed_jobs(
|
|
67
69
|
older_than=options["failed_older_than"],
|
|
68
70
|
task_path=options["task_path"],
|
|
69
71
|
batch_size=batch_size,
|
|
70
|
-
|
|
72
|
+
config=config,
|
|
71
73
|
)
|
|
72
74
|
recurring_deleted = clear_recurring_executions(
|
|
73
75
|
older_than=options["recurring_older_than"],
|
|
74
76
|
task_key=options["task_key"],
|
|
75
77
|
batch_size=batch_size,
|
|
76
|
-
|
|
78
|
+
config=config,
|
|
77
79
|
)
|
|
78
80
|
except ImproperlyConfigured as exc:
|
|
79
81
|
raise CommandError(str(exc)) from exc
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
|
|
3
3
|
from dj_queue import observability
|
|
4
|
-
from dj_queue.queue_state import
|
|
4
|
+
from dj_queue.queue_state import SUMMARY_STATE_DEFINITIONS
|
|
5
5
|
|
|
6
6
|
|
|
7
7
|
@dataclass(frozen=True, slots=True)
|
|
@@ -60,7 +60,7 @@ def _queue_metric_families(snapshots):
|
|
|
60
60
|
for snapshot in snapshots:
|
|
61
61
|
for queue in snapshot.queue_rows:
|
|
62
62
|
labels = (snapshot.backend_alias, queue["name"])
|
|
63
|
-
for definition in
|
|
63
|
+
for definition in SUMMARY_STATE_DEFINITIONS:
|
|
64
64
|
jobs.append(
|
|
65
65
|
MetricSample(labels=(*labels, definition.name), value=queue[definition.count_key])
|
|
66
66
|
)
|
|
@@ -155,29 +155,6 @@ def _ready_execution_row(
|
|
|
155
155
|
|
|
156
156
|
|
|
157
157
|
def _create_ready_execution(
|
|
158
|
-
alias,
|
|
159
|
-
job,
|
|
160
|
-
*,
|
|
161
|
-
backend_alias,
|
|
162
|
-
queue_name=None,
|
|
163
|
-
priority=None,
|
|
164
|
-
ready_at=None,
|
|
165
|
-
created_at=None,
|
|
166
|
-
):
|
|
167
|
-
queue_name = _execution_queue_name(job, queue_name)
|
|
168
|
-
return _create_ready_execution_locked(
|
|
169
|
-
alias,
|
|
170
|
-
job,
|
|
171
|
-
backend_alias=backend_alias,
|
|
172
|
-
queue_name=queue_name,
|
|
173
|
-
priority=priority,
|
|
174
|
-
ready_at=ready_at,
|
|
175
|
-
created_at=created_at,
|
|
176
|
-
check_conflicts=True,
|
|
177
|
-
)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
def _create_ready_execution_locked(
|
|
181
158
|
alias,
|
|
182
159
|
job,
|
|
183
160
|
*,
|
|
@@ -190,7 +167,6 @@ def _create_ready_execution_locked(
|
|
|
190
167
|
):
|
|
191
168
|
if check_conflicts:
|
|
192
169
|
_ensure_no_other_execution_state(alias, job)
|
|
193
|
-
_lock_active_pauses(alias, backend_alias, {queue_name})
|
|
194
170
|
return ReadyExecution.objects.using(alias).create(
|
|
195
171
|
**_ready_execution_fields(
|
|
196
172
|
job,
|
|
@@ -203,29 +179,16 @@ def _create_ready_execution_locked(
|
|
|
203
179
|
)
|
|
204
180
|
|
|
205
181
|
|
|
206
|
-
def
|
|
182
|
+
def _bulk_create_ready_executions(alias, ready_rows, *, check_conflicts):
|
|
207
183
|
ready_rows = tuple(ready_rows)
|
|
208
184
|
if not ready_rows:
|
|
209
185
|
return None
|
|
210
186
|
|
|
211
187
|
if check_conflicts:
|
|
212
188
|
_ensure_job_ids_have_no_other_execution_state(alias, [row.job_id for row in ready_rows])
|
|
213
|
-
_lock_active_pauses(alias, backend_alias, {row.queue_name for row in ready_rows})
|
|
214
189
|
return _bulk_create(alias, ReadyExecution, ready_rows)
|
|
215
190
|
|
|
216
191
|
|
|
217
|
-
def _ready_execution_rows(jobs, *, backend_alias, ready_at=None, created_at=None):
|
|
218
|
-
return [
|
|
219
|
-
_ready_execution_row(
|
|
220
|
-
job,
|
|
221
|
-
backend_alias=backend_alias,
|
|
222
|
-
ready_at=ready_at,
|
|
223
|
-
created_at=created_at,
|
|
224
|
-
)
|
|
225
|
-
for job in jobs
|
|
226
|
-
]
|
|
227
|
-
|
|
228
|
-
|
|
229
192
|
def _scheduled_execution_row(job, *, backend_alias, scheduled_at=None, created_at=None):
|
|
230
193
|
return ScheduledExecution(
|
|
231
194
|
**_scheduled_execution_fields(
|
|
@@ -9,7 +9,6 @@ from django.utils import timezone
|
|
|
9
9
|
from dj_queue.config import resolve_backend_config
|
|
10
10
|
from dj_queue.db import (
|
|
11
11
|
database_capabilities,
|
|
12
|
-
get_database_alias,
|
|
13
12
|
locked_queryset,
|
|
14
13
|
retry_transient_database_errors,
|
|
15
14
|
)
|
|
@@ -46,29 +45,26 @@ def claim_ready_jobs(
|
|
|
46
45
|
queues: str | Sequence[str] | None = None,
|
|
47
46
|
process: Process | None = None,
|
|
48
47
|
backend_alias: str = "default",
|
|
49
|
-
use_skip_locked: bool | None = None,
|
|
50
48
|
config=None,
|
|
51
49
|
) -> list[ClaimedJob]:
|
|
52
50
|
if limit <= 0:
|
|
53
51
|
return []
|
|
54
52
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
use_skip_locked = resolve_backend_config(backend_alias, config).use_skip_locked
|
|
58
|
-
|
|
53
|
+
config = resolve_backend_config(backend_alias, config)
|
|
54
|
+
alias = config.database_alias
|
|
59
55
|
claimed_jobs = retry_transient_database_errors(
|
|
60
56
|
lambda: _claim_ready_jobs_once(
|
|
61
57
|
limit=limit,
|
|
62
58
|
queues=queues,
|
|
63
59
|
process=process,
|
|
64
60
|
backend_alias=backend_alias,
|
|
65
|
-
use_skip_locked=use_skip_locked,
|
|
61
|
+
use_skip_locked=config.use_skip_locked,
|
|
66
62
|
alias=alias,
|
|
67
63
|
),
|
|
68
64
|
using=alias,
|
|
69
65
|
)
|
|
70
66
|
|
|
71
|
-
if event_logging_enabled(
|
|
67
|
+
if event_logging_enabled():
|
|
72
68
|
for claimed_job in claimed_jobs:
|
|
73
69
|
log_event(
|
|
74
70
|
"job.claimed",
|
|
@@ -174,10 +170,6 @@ def _claim_ready_jobs_once(
|
|
|
174
170
|
]
|
|
175
171
|
|
|
176
172
|
|
|
177
|
-
def _filter_queue_selectors(queryset, queues):
|
|
178
|
-
return filter_by_queue_selectors(queryset, queues)
|
|
179
|
-
|
|
180
|
-
|
|
181
173
|
def _select_ready_rows(queryset, *, limit, queues, use_skip_locked, alias, backend_alias):
|
|
182
174
|
if selectors_match_all(queues):
|
|
183
175
|
select_ready_row_ids = getattr(backend_sql(alias), "select_ready_row_ids", None)
|
|
@@ -195,36 +187,29 @@ def _select_ready_rows(queryset, *, limit, queues, use_skip_locked, alias, backe
|
|
|
195
187
|
|
|
196
188
|
selectors = normalize_queue_selectors(queues)
|
|
197
189
|
selected_rows = []
|
|
198
|
-
selected_ids = set()
|
|
199
190
|
|
|
200
191
|
star_index = selectors.index("*") if "*" in selectors else None
|
|
201
192
|
ordered_selectors = selectors if star_index is None else selectors[:star_index]
|
|
202
193
|
|
|
203
194
|
if ordered_selectors:
|
|
204
195
|
if _selectors_are_exact_queues(ordered_selectors):
|
|
205
|
-
|
|
196
|
+
selected_rows = _select_exact_queue_rows(
|
|
206
197
|
queryset,
|
|
207
198
|
ordered_selectors,
|
|
208
199
|
limit=limit,
|
|
209
200
|
use_skip_locked=use_skip_locked,
|
|
210
|
-
selected_ids=selected_ids,
|
|
211
201
|
alias=alias,
|
|
212
202
|
backend_alias=backend_alias,
|
|
213
203
|
)
|
|
214
204
|
else:
|
|
215
|
-
ordered = _ordered_selector_rows_queryset(
|
|
216
|
-
|
|
217
|
-
ordered_selectors,
|
|
218
|
-
)
|
|
219
|
-
rows = list(locked_queryset(ordered, use_skip_locked=use_skip_locked)[:limit])
|
|
220
|
-
selected_rows.extend(rows)
|
|
221
|
-
selected_ids.update(row.pk for row in rows)
|
|
205
|
+
ordered = _ordered_selector_rows_queryset(queryset, ordered_selectors)
|
|
206
|
+
selected_rows = list(locked_queryset(ordered, use_skip_locked=use_skip_locked)[:limit])
|
|
222
207
|
|
|
223
208
|
remaining = limit - len(selected_rows)
|
|
224
209
|
if remaining <= 0 or star_index is None:
|
|
225
210
|
return selected_rows
|
|
226
211
|
|
|
227
|
-
ordered = queryset.exclude(pk__in=
|
|
212
|
+
ordered = queryset.exclude(pk__in=[row.pk for row in selected_rows]).order_by("-priority", "id")
|
|
228
213
|
rows = list(locked_queryset(ordered, use_skip_locked=use_skip_locked)[:remaining])
|
|
229
214
|
selected_rows.extend(rows)
|
|
230
215
|
return selected_rows
|
|
@@ -240,22 +225,21 @@ def _select_exact_queue_rows(
|
|
|
240
225
|
*,
|
|
241
226
|
limit,
|
|
242
227
|
use_skip_locked,
|
|
243
|
-
selected_ids,
|
|
244
228
|
alias,
|
|
245
229
|
backend_alias,
|
|
246
230
|
):
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
)
|
|
231
|
+
select_ready_rows = getattr(backend_sql(alias), "select_ready_rows_by_exact_queues", None)
|
|
232
|
+
if select_ready_rows is not None:
|
|
233
|
+
return select_ready_rows(
|
|
234
|
+
alias,
|
|
235
|
+
backend_alias=backend_alias,
|
|
236
|
+
selectors=selectors,
|
|
237
|
+
limit=limit,
|
|
238
|
+
use_skip_locked=use_skip_locked,
|
|
239
|
+
)
|
|
257
240
|
|
|
258
241
|
selected_rows = []
|
|
242
|
+
selected_ids = set()
|
|
259
243
|
for selector in selectors:
|
|
260
244
|
remaining = limit - len(selected_rows)
|
|
261
245
|
if remaining <= 0:
|
|
@@ -285,7 +269,7 @@ def _create_claimed_executions(alias, jobs, *, process, claimed_at):
|
|
|
285
269
|
|
|
286
270
|
|
|
287
271
|
def _ordered_selector_rows_queryset(queryset, selectors):
|
|
288
|
-
filtered =
|
|
272
|
+
filtered = filter_by_queue_selectors(queryset, selectors)
|
|
289
273
|
selector_rank = Case(
|
|
290
274
|
*[
|
|
291
275
|
When(queue_selector_condition((selector,)), then=Value(index))
|