dj-queue 0.6.3__tar.gz → 0.6.4__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.6.3 → dj_queue-0.6.4}/PKG-INFO +1 -1
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/backend.py +1 -1
- dj_queue-0.6.4/dj_queue/migrations/0006_blockedexecution_dj_queue_bl_concurr_2d8393_idx_and_more.py +59 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/models/jobs.py +16 -64
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/models/recurring.py +4 -1
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/operations/cleanup.py +8 -4
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/operations/concurrency.py +9 -10
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/operations/jobs.py +35 -15
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/supervisor.py +49 -4
- {dj_queue-0.6.3 → dj_queue-0.6.4}/pyproject.toml +1 -1
- {dj_queue-0.6.3 → dj_queue-0.6.4}/LICENSE +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/README.md +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/__init__.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/admin.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/api.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/apps.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/config.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/contrib/__init__.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/contrib/asgi.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/contrib/gunicorn.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/contrib/prometheus.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/dashboard.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/db.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/exceptions.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/hooks.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/log.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/management/__init__.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/management/commands/__init__.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/management/commands/dj_queue.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/management/commands/dj_queue_health.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/management/commands/dj_queue_prune.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/migrations/0001_initial.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/migrations/0002_pause_semaphore.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/migrations/0003_recurringtask_recurringexecution.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/migrations/0004_dashboard.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/migrations/0005_remove_recurringexecution_dj_queue_recurring_executions_task_key_run_at_unique_and_more.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/migrations/__init__.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/models/__init__.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/models/runtime.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/observability.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/operations/__init__.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/operations/_insert.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/operations/recurring.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/routers.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/__init__.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/base.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/dispatcher.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/errors.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/interruptible.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/notify.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/pidfile.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/pool.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/procline.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/scheduler.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/runtime/worker.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_dashboard_process_rows.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_dashboard_recurring_rows.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_dashboard_section_table.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_dashboard_semaphore_rows.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_paginator.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_queue_controls.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_sortable_header_cells.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/change_form.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/change_list.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/dashboard.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/includes/fieldset.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/queue_jobs.html +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templatetags/__init__.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templatetags/dj_queue_admin.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/urls.py +0 -0
- {dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/views.py +0 -0
dj_queue-0.6.4/dj_queue/migrations/0006_blockedexecution_dj_queue_bl_concurr_2d8393_idx_and_more.py
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Generated by Django 6.0.4 on 2026-05-06 05:12
|
|
2
|
+
|
|
3
|
+
from django.db import migrations, models
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Migration(migrations.Migration):
|
|
7
|
+
dependencies = [
|
|
8
|
+
(
|
|
9
|
+
"dj_queue",
|
|
10
|
+
"0005_remove_recurringexecution_dj_queue_recurring_executions_task_key_run_at_unique_and_more",
|
|
11
|
+
),
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
operations = [
|
|
15
|
+
migrations.AddIndex(
|
|
16
|
+
model_name="blockedexecution",
|
|
17
|
+
index=models.Index(
|
|
18
|
+
fields=["concurrency_key", "-priority", "id"], name="dj_queue_bl_concurr_2d8393_idx"
|
|
19
|
+
),
|
|
20
|
+
),
|
|
21
|
+
migrations.AddIndex(
|
|
22
|
+
model_name="blockedexecution",
|
|
23
|
+
index=models.Index(
|
|
24
|
+
fields=["expires_at", "-priority", "id"], name="dj_queue_bl_expires_f4b090_idx"
|
|
25
|
+
),
|
|
26
|
+
),
|
|
27
|
+
migrations.AddIndex(
|
|
28
|
+
model_name="failedexecution",
|
|
29
|
+
index=models.Index(fields=["created_at", "job"], name="dj_queue_fa_created_343d7d_idx"),
|
|
30
|
+
),
|
|
31
|
+
migrations.AddIndex(
|
|
32
|
+
model_name="job",
|
|
33
|
+
index=models.Index(
|
|
34
|
+
fields=["backend_alias", "finished_at", "id"], name="dj_queue_jo_backend_ee105e_idx"
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
migrations.AddIndex(
|
|
38
|
+
model_name="readyexecution",
|
|
39
|
+
index=models.Index(fields=["-priority", "id"], name="dj_queue_re_priorit_ee6ffe_idx"),
|
|
40
|
+
),
|
|
41
|
+
migrations.AddIndex(
|
|
42
|
+
model_name="readyexecution",
|
|
43
|
+
index=models.Index(
|
|
44
|
+
fields=["queue_name", "-priority", "id"], name="dj_queue_re_queue_n_612755_idx"
|
|
45
|
+
),
|
|
46
|
+
),
|
|
47
|
+
migrations.AddIndex(
|
|
48
|
+
model_name="recurringexecution",
|
|
49
|
+
index=models.Index(
|
|
50
|
+
fields=["backend_alias", "run_at", "id"], name="dj_queue_re_backend_a68bf1_idx"
|
|
51
|
+
),
|
|
52
|
+
),
|
|
53
|
+
migrations.AddIndex(
|
|
54
|
+
model_name="scheduledexecution",
|
|
55
|
+
index=models.Index(
|
|
56
|
+
fields=["scheduled_at", "-priority", "id"], name="dj_queue_sc_schedul_edba95_idx"
|
|
57
|
+
),
|
|
58
|
+
),
|
|
59
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import uuid
|
|
2
2
|
|
|
3
|
-
from django.core.exceptions import ObjectDoesNotExist
|
|
3
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
4
4
|
from django.db import models
|
|
5
5
|
from django.db.models import Q
|
|
6
6
|
from django.utils.module_loading import import_string
|
|
@@ -65,6 +65,7 @@ class Job(models.Model):
|
|
|
65
65
|
models.Index(fields=["queue_name", "finished_at"]),
|
|
66
66
|
models.Index(fields=["scheduled_at", "finished_at"]),
|
|
67
67
|
models.Index(fields=["finished_at"]),
|
|
68
|
+
models.Index(fields=["backend_alias", "finished_at", "id"]),
|
|
68
69
|
]
|
|
69
70
|
|
|
70
71
|
@property
|
|
@@ -125,16 +126,10 @@ class ReadyExecution(models.Model):
|
|
|
125
126
|
indexes = [
|
|
126
127
|
models.Index(fields=["priority", "id"]),
|
|
127
128
|
models.Index(fields=["queue_name", "priority", "id"]),
|
|
129
|
+
models.Index(fields=["-priority", "id"]),
|
|
130
|
+
models.Index(fields=["queue_name", "-priority", "id"]),
|
|
128
131
|
]
|
|
129
132
|
|
|
130
|
-
def clean(self):
|
|
131
|
-
super().clean()
|
|
132
|
-
_validate_live_state(self)
|
|
133
|
-
|
|
134
|
-
def save(self, *args, **kwargs):
|
|
135
|
-
self.full_clean()
|
|
136
|
-
return super().save(*args, **kwargs)
|
|
137
|
-
|
|
138
133
|
@classmethod
|
|
139
134
|
def discard_all_in_batches(cls, *, batch_size=500, backend_alias="default"):
|
|
140
135
|
operation = import_string("dj_queue.operations.jobs.discard_ready_jobs")
|
|
@@ -159,15 +154,10 @@ class ScheduledExecution(models.Model):
|
|
|
159
154
|
|
|
160
155
|
class Meta:
|
|
161
156
|
db_table = "dj_queue_scheduled_executions"
|
|
162
|
-
indexes = [
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
_validate_live_state(self)
|
|
167
|
-
|
|
168
|
-
def save(self, *args, **kwargs):
|
|
169
|
-
self.full_clean()
|
|
170
|
-
return super().save(*args, **kwargs)
|
|
157
|
+
indexes = [
|
|
158
|
+
models.Index(fields=["scheduled_at", "priority", "id"]),
|
|
159
|
+
models.Index(fields=["scheduled_at", "-priority", "id"]),
|
|
160
|
+
]
|
|
171
161
|
|
|
172
162
|
|
|
173
163
|
class ClaimedExecution(models.Model):
|
|
@@ -189,14 +179,6 @@ class ClaimedExecution(models.Model):
|
|
|
189
179
|
db_table = "dj_queue_claimed_executions"
|
|
190
180
|
indexes = [models.Index(fields=["process", "job"])]
|
|
191
181
|
|
|
192
|
-
def clean(self):
|
|
193
|
-
super().clean()
|
|
194
|
-
_validate_live_state(self)
|
|
195
|
-
|
|
196
|
-
def save(self, *args, **kwargs):
|
|
197
|
-
self.full_clean()
|
|
198
|
-
return super().save(*args, **kwargs)
|
|
199
|
-
|
|
200
182
|
@classmethod
|
|
201
183
|
def discard_all_in_batches(cls, **_kwargs):
|
|
202
184
|
raise UndiscardableError("cannot discard in-progress jobs")
|
|
@@ -219,16 +201,10 @@ class BlockedExecution(models.Model):
|
|
|
219
201
|
indexes = [
|
|
220
202
|
models.Index(fields=["concurrency_key", "priority", "id"]),
|
|
221
203
|
models.Index(fields=["expires_at", "concurrency_key"]),
|
|
204
|
+
models.Index(fields=["concurrency_key", "-priority", "id"]),
|
|
205
|
+
models.Index(fields=["expires_at", "-priority", "id"]),
|
|
222
206
|
]
|
|
223
207
|
|
|
224
|
-
def clean(self):
|
|
225
|
-
super().clean()
|
|
226
|
-
_validate_live_state(self)
|
|
227
|
-
|
|
228
|
-
def save(self, *args, **kwargs):
|
|
229
|
-
self.full_clean()
|
|
230
|
-
return super().save(*args, **kwargs)
|
|
231
|
-
|
|
232
208
|
@classmethod
|
|
233
209
|
def discard_all_in_batches(cls, *, batch_size=500, backend_alias="default"):
|
|
234
210
|
operation = import_string("dj_queue.operations.jobs.discard_blocked_jobs")
|
|
@@ -253,14 +229,7 @@ class FailedExecution(models.Model):
|
|
|
253
229
|
|
|
254
230
|
class Meta:
|
|
255
231
|
db_table = "dj_queue_failed_executions"
|
|
256
|
-
|
|
257
|
-
def clean(self):
|
|
258
|
-
super().clean()
|
|
259
|
-
_validate_live_state(self)
|
|
260
|
-
|
|
261
|
-
def save(self, *args, **kwargs):
|
|
262
|
-
self.full_clean()
|
|
263
|
-
return super().save(*args, **kwargs)
|
|
232
|
+
indexes = [models.Index(fields=["created_at", "job"])]
|
|
264
233
|
|
|
265
234
|
def retry(self):
|
|
266
235
|
return _retry_failed_job(self.job_id, backend_alias=self.job.backend_alias)
|
|
@@ -301,28 +270,11 @@ def _discard_jobs_for_state(model, operation, *, batch_size, backend_alias):
|
|
|
301
270
|
alias = get_database_alias(backend_alias)
|
|
302
271
|
deleted = 0
|
|
303
272
|
while True:
|
|
304
|
-
job_ids = list(
|
|
273
|
+
job_ids = list(
|
|
274
|
+
model.objects.using(alias)
|
|
275
|
+
.filter(job__backend_alias=backend_alias)
|
|
276
|
+
.values_list("job_id", flat=True)[:batch_size]
|
|
277
|
+
)
|
|
305
278
|
if not job_ids:
|
|
306
279
|
return deleted
|
|
307
280
|
deleted += operation(job_ids=job_ids, batch_size=batch_size, backend_alias=backend_alias)
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
def _validate_live_state(instance):
|
|
311
|
-
if not instance.job_id:
|
|
312
|
-
return
|
|
313
|
-
|
|
314
|
-
for model in LIVE_STATE_MODELS:
|
|
315
|
-
queryset = model._default_manager.filter(job_id=instance.job_id)
|
|
316
|
-
if model is instance.__class__ and instance.pk is not None:
|
|
317
|
-
queryset = queryset.exclude(pk=instance.pk)
|
|
318
|
-
if queryset.exists():
|
|
319
|
-
raise ValidationError({"job": "job already has a live execution state"})
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
LIVE_STATE_MODELS = (
|
|
323
|
-
ReadyExecution,
|
|
324
|
-
ScheduledExecution,
|
|
325
|
-
ClaimedExecution,
|
|
326
|
-
BlockedExecution,
|
|
327
|
-
FailedExecution,
|
|
328
|
-
)
|
|
@@ -57,4 +57,7 @@ class RecurringExecution(models.Model):
|
|
|
57
57
|
name="dj_queue_recur_exec_backend_run_at_unique",
|
|
58
58
|
)
|
|
59
59
|
]
|
|
60
|
-
indexes = [
|
|
60
|
+
indexes = [
|
|
61
|
+
models.Index(fields=["backend_alias", "task_key", "run_at"]),
|
|
62
|
+
models.Index(fields=["backend_alias", "run_at", "id"]),
|
|
63
|
+
]
|
|
@@ -25,7 +25,11 @@ def clear_finished_jobs(
|
|
|
25
25
|
if now is None:
|
|
26
26
|
now = timezone.now()
|
|
27
27
|
cutoff = now - timedelta(seconds=older_than)
|
|
28
|
-
queryset =
|
|
28
|
+
queryset = (
|
|
29
|
+
Job.objects.using(alias)
|
|
30
|
+
.filter(backend_alias=backend_alias, finished_at__lt=cutoff)
|
|
31
|
+
.order_by("finished_at", "id")
|
|
32
|
+
)
|
|
29
33
|
if task_path is not None:
|
|
30
34
|
queryset = queryset.filter(task_path=task_path)
|
|
31
35
|
|
|
@@ -33,7 +37,7 @@ def clear_finished_jobs(
|
|
|
33
37
|
if not job_ids:
|
|
34
38
|
return 0
|
|
35
39
|
|
|
36
|
-
Job.objects.using(alias).filter(pk__in=job_ids).delete()
|
|
40
|
+
Job.objects.using(alias).filter(backend_alias=backend_alias, pk__in=job_ids).delete()
|
|
37
41
|
return len(job_ids)
|
|
38
42
|
|
|
39
43
|
|
|
@@ -57,7 +61,7 @@ def clear_failed_jobs(
|
|
|
57
61
|
cutoff = now - timedelta(seconds=older_than)
|
|
58
62
|
queryset = (
|
|
59
63
|
FailedExecution.objects.using(alias)
|
|
60
|
-
.filter(created_at__lt=cutoff)
|
|
64
|
+
.filter(job__backend_alias=backend_alias, created_at__lt=cutoff)
|
|
61
65
|
.order_by("created_at", "job_id")
|
|
62
66
|
)
|
|
63
67
|
if task_path is not None:
|
|
@@ -67,7 +71,7 @@ def clear_failed_jobs(
|
|
|
67
71
|
if not job_ids:
|
|
68
72
|
return 0
|
|
69
73
|
|
|
70
|
-
Job.objects.using(alias).filter(pk__in=job_ids).delete()
|
|
74
|
+
Job.objects.using(alias).filter(backend_alias=backend_alias, pk__in=job_ids).delete()
|
|
71
75
|
return len(job_ids)
|
|
72
76
|
|
|
73
77
|
|
|
@@ -8,7 +8,7 @@ from django.utils.module_loading import import_string
|
|
|
8
8
|
from dj_queue.config import load_backend_config
|
|
9
9
|
from dj_queue.db import get_database_alias, locked_queryset
|
|
10
10
|
from dj_queue.log import log_event
|
|
11
|
-
from dj_queue.models import BlockedExecution,
|
|
11
|
+
from dj_queue.models import BlockedExecution, Pause, ReadyExecution, Semaphore
|
|
12
12
|
from dj_queue.operations._insert import create_ignore_conflicts
|
|
13
13
|
from dj_queue.runtime import notify as runtime_notify
|
|
14
14
|
|
|
@@ -77,7 +77,10 @@ def unblock_next_blocked_job(
|
|
|
77
77
|
|
|
78
78
|
with transaction.atomic(using=alias):
|
|
79
79
|
queryset = (
|
|
80
|
-
BlockedExecution.objects.using(alias)
|
|
80
|
+
BlockedExecution.objects.using(alias)
|
|
81
|
+
.select_related("job")
|
|
82
|
+
.filter(concurrency_key=key, job__backend_alias=backend_alias)
|
|
83
|
+
.order_by("-priority", "id")
|
|
81
84
|
)
|
|
82
85
|
blocked = locked_queryset(queryset, use_skip_locked=use_skip_locked).first()
|
|
83
86
|
if blocked is None:
|
|
@@ -91,7 +94,7 @@ def unblock_next_blocked_job(
|
|
|
91
94
|
):
|
|
92
95
|
return None
|
|
93
96
|
|
|
94
|
-
job =
|
|
97
|
+
job = blocked.job
|
|
95
98
|
queue_name = blocked.queue_name
|
|
96
99
|
priority = blocked.priority
|
|
97
100
|
blocked.delete(using=alias)
|
|
@@ -134,20 +137,16 @@ def promote_expired_blocked_jobs(*, batch_size=500, backend_alias="default", use
|
|
|
134
137
|
with transaction.atomic(using=alias):
|
|
135
138
|
queryset = (
|
|
136
139
|
BlockedExecution.objects.using(alias)
|
|
137
|
-
.
|
|
140
|
+
.select_related("job")
|
|
141
|
+
.filter(job__backend_alias=backend_alias, expires_at__lte=now)
|
|
138
142
|
.order_by("expires_at", "-priority", "id")
|
|
139
143
|
)
|
|
140
144
|
blocked_rows = list(locked_queryset(queryset, use_skip_locked=use_skip_locked)[:batch_size])
|
|
141
145
|
if not blocked_rows:
|
|
142
146
|
return []
|
|
143
147
|
|
|
144
|
-
jobs_by_id = {
|
|
145
|
-
job.id: job
|
|
146
|
-
for job in Job.objects.using(alias).filter(pk__in=[b.job_id for b in blocked_rows])
|
|
147
|
-
}
|
|
148
|
-
|
|
149
148
|
for blocked in blocked_rows:
|
|
150
|
-
job =
|
|
149
|
+
job = blocked.job
|
|
151
150
|
limit, duration_seconds = task_settings.get(job.task_path, (None, None))
|
|
152
151
|
if limit is None:
|
|
153
152
|
task = import_string(job.task_path)
|
|
@@ -219,7 +219,11 @@ def claim_ready_jobs(
|
|
|
219
219
|
)
|
|
220
220
|
|
|
221
221
|
with transaction.atomic(using=alias):
|
|
222
|
-
queryset =
|
|
222
|
+
queryset = (
|
|
223
|
+
ReadyExecution.objects.using(alias)
|
|
224
|
+
.select_related("job")
|
|
225
|
+
.filter(job__backend_alias=backend_alias)
|
|
226
|
+
)
|
|
223
227
|
if paused_queue_names:
|
|
224
228
|
queryset = queryset.exclude(queue_name__in=paused_queue_names)
|
|
225
229
|
ready_rows = _select_ready_rows(
|
|
@@ -231,9 +235,7 @@ def claim_ready_jobs(
|
|
|
231
235
|
if not ready_rows:
|
|
232
236
|
return []
|
|
233
237
|
|
|
234
|
-
|
|
235
|
-
jobs_by_id = {job.id: job for job in Job.objects.using(alias).filter(pk__in=job_ids)}
|
|
236
|
-
jobs = [jobs_by_id[job_id] for job_id in job_ids]
|
|
238
|
+
jobs = [row.job for row in ready_rows]
|
|
237
239
|
|
|
238
240
|
ReadyExecution.objects.using(alias).filter(pk__in=[row.pk for row in ready_rows]).delete()
|
|
239
241
|
_bulk_create(
|
|
@@ -250,7 +252,9 @@ def claim_ready_jobs(
|
|
|
250
252
|
def execute_claimed_job(job_id, *, backend_alias="default"):
|
|
251
253
|
alias = get_database_alias(backend_alias)
|
|
252
254
|
claimed = (
|
|
253
|
-
ClaimedExecution.objects.using(alias)
|
|
255
|
+
ClaimedExecution.objects.using(alias)
|
|
256
|
+
.select_related("job", "process")
|
|
257
|
+
.get(job_id=job_id, job__backend_alias=backend_alias)
|
|
254
258
|
)
|
|
255
259
|
job = claimed.job
|
|
256
260
|
|
|
@@ -279,7 +283,12 @@ def complete_claimed_job(job_id, return_value, *, backend_alias="default"):
|
|
|
279
283
|
alias = get_database_alias(backend_alias)
|
|
280
284
|
|
|
281
285
|
with transaction.atomic(using=alias):
|
|
282
|
-
claimed =
|
|
286
|
+
claimed = (
|
|
287
|
+
ClaimedExecution.objects.using(alias)
|
|
288
|
+
.select_for_update()
|
|
289
|
+
.select_related("job")
|
|
290
|
+
.get(job_id=job_id, job__backend_alias=backend_alias)
|
|
291
|
+
)
|
|
283
292
|
job = claimed.job
|
|
284
293
|
now = timezone.now()
|
|
285
294
|
config = load_backend_config(job.backend_alias)
|
|
@@ -301,7 +310,12 @@ def fail_claimed_job(job_id, error, *, traceback_text="", backend_alias="default
|
|
|
301
310
|
alias = get_database_alias(backend_alias)
|
|
302
311
|
|
|
303
312
|
with transaction.atomic(using=alias):
|
|
304
|
-
claimed =
|
|
313
|
+
claimed = (
|
|
314
|
+
ClaimedExecution.objects.using(alias)
|
|
315
|
+
.select_for_update()
|
|
316
|
+
.select_related("job")
|
|
317
|
+
.get(job_id=job_id, job__backend_alias=backend_alias)
|
|
318
|
+
)
|
|
305
319
|
job = claimed.job
|
|
306
320
|
claimed.delete(using=alias)
|
|
307
321
|
FailedExecution.objects.using(alias).create(
|
|
@@ -330,16 +344,15 @@ def promote_scheduled_jobs(*, batch_size, backend_alias="default", use_skip_lock
|
|
|
330
344
|
with transaction.atomic(using=alias):
|
|
331
345
|
queryset = (
|
|
332
346
|
ScheduledExecution.objects.using(alias)
|
|
333
|
-
.
|
|
347
|
+
.select_related("job")
|
|
348
|
+
.filter(job__backend_alias=backend_alias, scheduled_at__lte=now)
|
|
334
349
|
.order_by("scheduled_at", "-priority", "id")
|
|
335
350
|
)
|
|
336
351
|
scheduled_rows = list(locked_queryset(queryset, use_skip_locked=use_skip_locked)[:batch_size])
|
|
337
352
|
if not scheduled_rows:
|
|
338
353
|
return []
|
|
339
354
|
|
|
340
|
-
|
|
341
|
-
jobs_by_id = {job.id: job for job in Job.objects.using(alias).filter(pk__in=job_ids)}
|
|
342
|
-
jobs = [jobs_by_id[job_id] for job_id in job_ids]
|
|
355
|
+
jobs = [row.job for row in scheduled_rows]
|
|
343
356
|
|
|
344
357
|
ScheduledExecution.objects.using(alias).filter(
|
|
345
358
|
pk__in=[row.pk for row in scheduled_rows]
|
|
@@ -388,7 +401,12 @@ def retry_failed_job(job_id, *, backend_alias="default"):
|
|
|
388
401
|
alias = get_database_alias(backend_alias)
|
|
389
402
|
|
|
390
403
|
with transaction.atomic(using=alias):
|
|
391
|
-
failed =
|
|
404
|
+
failed = (
|
|
405
|
+
FailedExecution.objects.using(alias)
|
|
406
|
+
.select_for_update()
|
|
407
|
+
.select_related("job")
|
|
408
|
+
.get(job_id=job_id, job__backend_alias=backend_alias)
|
|
409
|
+
)
|
|
392
410
|
job = failed.job
|
|
393
411
|
failed.delete(using=alias)
|
|
394
412
|
job.return_value = None
|
|
@@ -408,7 +426,7 @@ _KEEP_RUN_AFTER = object()
|
|
|
408
426
|
|
|
409
427
|
def enqueue_job_again(job_id, *, backend_alias="default", run_after=_KEEP_RUN_AFTER):
|
|
410
428
|
alias = get_database_alias(backend_alias)
|
|
411
|
-
source_job = Job.objects.using(alias).get(pk=job_id)
|
|
429
|
+
source_job = Job.objects.using(alias).get(pk=job_id, backend_alias=backend_alias)
|
|
412
430
|
task = import_string(source_job.task_path)
|
|
413
431
|
source_run_after = source_job.scheduled_at if run_after is _KEEP_RUN_AFTER else run_after
|
|
414
432
|
if hasattr(task, "using"):
|
|
@@ -426,6 +444,7 @@ def enqueue_job_again(job_id, *, backend_alias="default", run_after=_KEEP_RUN_AF
|
|
|
426
444
|
|
|
427
445
|
def discard_failed_jobs(*, job_ids=None, batch_size=500, backend_alias="default"):
|
|
428
446
|
alias = get_database_alias(backend_alias)
|
|
447
|
+
config = load_backend_config(backend_alias)
|
|
429
448
|
|
|
430
449
|
with transaction.atomic(using=alias):
|
|
431
450
|
queryset = (
|
|
@@ -433,7 +452,9 @@ def discard_failed_jobs(*, job_ids=None, batch_size=500, backend_alias="default"
|
|
|
433
452
|
)
|
|
434
453
|
if job_ids is not None:
|
|
435
454
|
queryset = queryset.filter(job_id__in=job_ids)
|
|
436
|
-
failed_rows = list(
|
|
455
|
+
failed_rows = list(
|
|
456
|
+
locked_queryset(queryset, use_skip_locked=config.use_skip_locked)[:batch_size]
|
|
457
|
+
)
|
|
437
458
|
if not failed_rows:
|
|
438
459
|
return 0
|
|
439
460
|
|
|
@@ -502,7 +523,6 @@ def discard_scheduled_jobs(*, job_ids=None, batch_size=500, backend_alias="defau
|
|
|
502
523
|
Job.objects.using(alias).filter(pk__in=[row.job_id for row in scheduled_rows]).delete()
|
|
503
524
|
|
|
504
525
|
for job in jobs:
|
|
505
|
-
_release_concurrency_slot(job)
|
|
506
526
|
log_event("job.discarded", job_id=str(job.id), reason="scheduled")
|
|
507
527
|
return len(jobs)
|
|
508
528
|
|
|
@@ -4,6 +4,7 @@ import socket
|
|
|
4
4
|
import threading
|
|
5
5
|
import time
|
|
6
6
|
|
|
7
|
+
from django.db import connections
|
|
7
8
|
from django.utils import timezone
|
|
8
9
|
from datetime import timedelta
|
|
9
10
|
|
|
@@ -301,8 +302,11 @@ class AsyncSupervisor(Supervisor):
|
|
|
301
302
|
def _fail_crashed_runner_jobs(self, runner):
|
|
302
303
|
if runner.process is None:
|
|
303
304
|
return
|
|
305
|
+
alias = get_database_alias(self.backend_alias)
|
|
304
306
|
claimed_job_ids = list(
|
|
305
|
-
ClaimedExecution.objects.
|
|
307
|
+
ClaimedExecution.objects.using(alias)
|
|
308
|
+
.filter(process=runner.process)
|
|
309
|
+
.values_list("job_id", flat=True)
|
|
306
310
|
)
|
|
307
311
|
with app_executor():
|
|
308
312
|
for job_id in claimed_job_ids:
|
|
@@ -430,12 +434,21 @@ class ForkSupervisor(Supervisor):
|
|
|
430
434
|
return process
|
|
431
435
|
|
|
432
436
|
def stop(self):
|
|
437
|
+
timeout = max(float(self.config.shutdown_timeout), 0)
|
|
433
438
|
for pid in tuple(self.children):
|
|
434
439
|
try:
|
|
435
440
|
self._killer(pid, signal.SIGTERM)
|
|
436
441
|
except ProcessLookupError:
|
|
437
442
|
pass
|
|
438
|
-
|
|
443
|
+
|
|
444
|
+
self._wait_for_children(timeout)
|
|
445
|
+
for pid in tuple(self.children):
|
|
446
|
+
try:
|
|
447
|
+
self._killer(pid, signal.SIGKILL)
|
|
448
|
+
except ProcessLookupError:
|
|
449
|
+
pass
|
|
450
|
+
self._fail_claimed_jobs_for_pid(pid)
|
|
451
|
+
self.children.pop(pid, None)
|
|
439
452
|
return super().stop()
|
|
440
453
|
|
|
441
454
|
def register_signal_handlers(self):
|
|
@@ -485,6 +498,22 @@ class ForkSupervisor(Supervisor):
|
|
|
485
498
|
)
|
|
486
499
|
return replacement_pid
|
|
487
500
|
|
|
501
|
+
def _wait_for_children(self, timeout):
|
|
502
|
+
deadline = time.monotonic() + timeout
|
|
503
|
+
while self.children and time.monotonic() < deadline:
|
|
504
|
+
try:
|
|
505
|
+
pid, _status = self._waitpid(-1, os.WNOHANG)
|
|
506
|
+
except ChildProcessError:
|
|
507
|
+
self.children.clear()
|
|
508
|
+
return None
|
|
509
|
+
|
|
510
|
+
if not pid:
|
|
511
|
+
time.sleep(min(0.05, max(deadline - time.monotonic(), 0)))
|
|
512
|
+
continue
|
|
513
|
+
|
|
514
|
+
self.children.pop(pid, None)
|
|
515
|
+
return None
|
|
516
|
+
|
|
488
517
|
def poll_once(self):
|
|
489
518
|
super().poll_once()
|
|
490
519
|
return self.check_children()
|
|
@@ -566,9 +595,25 @@ class ForkSupervisor(Supervisor):
|
|
|
566
595
|
return specs
|
|
567
596
|
|
|
568
597
|
def _default_launcher(self, spec):
|
|
598
|
+
connections.close_all()
|
|
569
599
|
pid = os.fork()
|
|
570
600
|
if pid == 0:
|
|
571
|
-
|
|
572
|
-
|
|
601
|
+
connections.close_all()
|
|
602
|
+
try:
|
|
603
|
+
runner = spec["runner_class"](**spec["kwargs"])
|
|
604
|
+
self._register_child_signal_handlers(runner)
|
|
605
|
+
runner.run()
|
|
606
|
+
finally:
|
|
607
|
+
connections.close_all()
|
|
573
608
|
self._exit_fn(0)
|
|
609
|
+
connections.close_all()
|
|
574
610
|
return pid
|
|
611
|
+
|
|
612
|
+
def _register_child_signal_handlers(self, runner):
|
|
613
|
+
def request_stop(*_args):
|
|
614
|
+
runner.request_stop()
|
|
615
|
+
return True
|
|
616
|
+
|
|
617
|
+
signal.signal(signal.SIGTERM, request_stop)
|
|
618
|
+
signal.signal(signal.SIGINT, request_stop)
|
|
619
|
+
signal.signal(signal.SIGQUIT, lambda *_args: self._exit_fn(1))
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/migrations/0003_recurringtask_recurringexecution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_dashboard_process_rows.html
RENAMED
|
File without changes
|
{dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_dashboard_recurring_rows.html
RENAMED
|
File without changes
|
{dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_dashboard_section_table.html
RENAMED
|
File without changes
|
{dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_dashboard_semaphore_rows.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dj_queue-0.6.3 → dj_queue-0.6.4}/dj_queue/templates/admin/dj_queue/_sortable_header_cells.html
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|