Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 5ca6730

Browse files
committedOct 21, 2024··
feat(models): add AbstractChordCounter and update ChordCounter
- Added new abstract model `AbstractChordCounter` in `abstract.py` for Chord synchronization, including fields for `group_id`, `sub_tasks`, and `count`. - Updated `ChordCounter` model in `generic.py` to inherit from `AbstractChordCounter`. - Moved `group_result` method from `ChordCounter` to `AbstractChordCounter`. - Added helper function `chordcounter_model` in `helpers.py` to return the active `ChordCounter` model. - Updated import statements in `database.py` to use `chordcounter_model` helper function. - Added `ChordCounterModel` attribute to `DatabaseBackend` class, and updated `create` and `get` methods to use `ChordCounterModel` instead of `ChordCounter`. Relates to: #305
1 parent a0ff0ea commit 5ca6730

File tree

4 files changed

+84
-49
lines changed

4 files changed

+84
-49
lines changed
 

‎django_celery_results/backends/database.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@
1111
from django.db.utils import InterfaceError
1212
from kombu.exceptions import DecodeError
1313

14-
from ..models import ChordCounter
15-
from ..models.helpers import groupresult_model, taskresult_model
14+
from ..models.helpers import chordcounter_model, groupresult_model, taskresult_model
1615
from ..settings import get_task_props_extension
1716

1817
EXCEPTIONS_TO_CATCH = (InterfaceError,)
@@ -31,6 +30,7 @@ class DatabaseBackend(BaseDictBackend):
3130

3231
TaskModel = taskresult_model()
3332
GroupModel = groupresult_model()
33+
ChordCounterModel = chordcounter_model()
3434
subpolling_interval = 0.5
3535

3636
def exception_safe_to_retry(self, exc):
@@ -235,7 +235,7 @@ def apply_chord(self, header_result_args, body, **kwargs):
235235
results = [r.as_tuple() for r in header_result]
236236
chord_size = body.get("chord_size", None) or len(results)
237237
data = json.dumps(results)
238-
ChordCounter.objects.create(
238+
self.ChordCounterModel.objects.create(
239239
group_id=header_result.id, sub_tasks=data, count=chord_size
240240
)
241241

@@ -252,10 +252,10 @@ def on_chord_part_return(self, request, state, result, **kwargs):
252252
# SELECT FOR UPDATE is not supported on all databases
253253
try:
254254
chord_counter = (
255-
ChordCounter.objects.select_for_update()
255+
self.ChordCounterModel.objects.select_for_update()
256256
.get(group_id=gid)
257257
)
258-
except ChordCounter.DoesNotExist:
258+
except self.ChordCounterModel.DoesNotExist:
259259
logger.warning("Can't find ChordCounter for Group %s", gid)
260260
return
261261
chord_counter.count -= 1

‎django_celery_results/models/abstract.py

+51
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
"""Abstract models."""
22

3+
import json
4+
35
from celery import states
6+
from celery.result import result_from_tuple
47
from django.conf import settings
58
from django.db import models
69
from django.utils.translation import gettext_lazy as _
710

811
from .. import managers
12+
from ..models.helpers import groupresult_model
913

1014
ALL_STATES = sorted(states.ALL_STATES)
1115
TASK_STATE_CHOICES = sorted(zip(ALL_STATES, ALL_STATES))
@@ -127,6 +131,53 @@ def __str__(self):
127131
return '<Task: {0.task_id} ({0.status})>'.format(self)
128132

129133

134+
class AbstractChordCounter(models.Model):
135+
"""Abstract Chord synchronisation."""
136+
137+
group_id = models.CharField(
138+
max_length=getattr(
139+
settings,
140+
"DJANGO_CELERY_RESULTS_TASK_ID_MAX_LENGTH",
141+
255
142+
),
143+
unique=True,
144+
verbose_name=_("Group ID"),
145+
help_text=_("Celery ID for the Chord header group"),
146+
)
147+
sub_tasks = models.TextField(
148+
help_text=_(
149+
"JSON serialized list of task result tuples. "
150+
"use .group_result() to decode"
151+
)
152+
)
153+
count = models.PositiveIntegerField(
154+
help_text=_(
155+
"Starts at len(chord header) and decrements after each task is "
156+
"finished"
157+
)
158+
)
159+
160+
class Meta:
161+
"""Table information."""
162+
163+
abstract = True
164+
165+
def group_result(self, app=None):
166+
"""Return the GroupResult of self.
167+
168+
Arguments:
169+
---------
170+
app (Celery): app instance to create the GroupResult with.
171+
172+
"""
173+
return groupresult_model()(
174+
self.group_id,
175+
[result_from_tuple(r, app=app)
176+
for r in json.loads(self.sub_tasks)],
177+
app=app
178+
)
179+
180+
130181
class AbstractGroupResult(models.Model):
131182
"""Abstract Task Group result/status."""
132183

‎django_celery_results/models/generic.py

+4-43
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,10 @@
22

33
import json
44

5-
from celery.result import GroupResult as CeleryGroupResult
6-
from celery.result import result_from_tuple
7-
from django.conf import settings
8-
from django.db import models
95
from django.utils.translation import gettext_lazy as _
106

117
from django_celery_results.models.abstract import (
8+
AbstractChordCounter,
129
AbstractGroupResult,
1310
AbstractTaskResult,
1411
)
@@ -24,49 +21,13 @@ class Meta(AbstractTaskResult.Meta):
2421
app_label = "django_celery_results"
2522

2623

27-
class ChordCounter(models.Model):
24+
class ChordCounter(AbstractChordCounter):
2825
"""Chord synchronisation."""
2926

30-
group_id = models.CharField(
31-
max_length=getattr(
32-
settings,
33-
"DJANGO_CELERY_RESULTS_TASK_ID_MAX_LENGTH",
34-
255),
35-
unique=True,
36-
verbose_name=_("Group ID"),
37-
help_text=_("Celery ID for the Chord header group"),
38-
)
39-
sub_tasks = models.TextField(
40-
help_text=_(
41-
"JSON serialized list of task result tuples. "
42-
"use .group_result() to decode"
43-
)
44-
)
45-
count = models.PositiveIntegerField(
46-
help_text=_(
47-
"Starts at len(chord header) and decrements after each task is "
48-
"finished"
49-
)
50-
)
51-
52-
class Meta:
27+
class Meta(AbstractChordCounter.Meta):
28+
abstract = False
5329
app_label = "django_celery_results"
5430

55-
def group_result(self, app=None):
56-
"""Return the GroupResult of self.
57-
58-
Arguments:
59-
---------
60-
app (Celery): app instance to create the GroupResult with.
61-
62-
"""
63-
return CeleryGroupResult(
64-
self.group_id,
65-
[result_from_tuple(r, app=app)
66-
for r in json.loads(self.sub_tasks)],
67-
app=app
68-
)
69-
7031

7132
class GroupResult(AbstractGroupResult):
7233
"""Task Group result/status."""

‎django_celery_results/models/helpers.py

+24-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from django.conf import settings
33
from django.core.exceptions import ImproperlyConfigured
44

5-
from .generic import GroupResult, TaskResult
5+
from .generic import ChordCounter, GroupResult, TaskResult
66

77

88
def taskresult_model():
@@ -27,6 +27,29 @@ def taskresult_model():
2727
)
2828

2929

30+
def chordcounter_model():
31+
"""Return the ChordCounter model that is active in this project."""
32+
33+
if not hasattr(settings, 'CELERY_RESULTS_CHORDCOUNTER_MODEL'):
34+
return ChordCounter
35+
36+
try:
37+
return apps.get_model(
38+
settings.CELERY_RESULTS_CHORDCOUNTER_MODEL
39+
)
40+
except ValueError:
41+
raise ImproperlyConfigured(
42+
"CELERY_RESULTS_CHORDCOUNTER_MODEL must be of the form "
43+
"'app_label.model_name'"
44+
)
45+
except LookupError:
46+
raise ImproperlyConfigured(
47+
"CELERY_RESULTS_CHORDCOUNTER_MODEL refers to model "
48+
f"'{settings.CELERY_RESULTS_CHORDCOUNTER_MODEL}' that has not "
49+
"been installed"
50+
)
51+
52+
3053
def groupresult_model():
3154
"""Return the GroupResult model that is active in this project."""
3255
if not hasattr(settings, 'CELERY_RESULTS_GROUPRESULT_MODEL'):

0 commit comments

Comments
 (0)
Please sign in to comment.