Skip to content

Commit

Permalink
Store current time when TaskResult started (#432)
Browse files Browse the repository at this point in the history
* store current time when TaskResult started

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* add an example

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
yktakaha4 and pre-commit-ci[bot] authored Jun 15, 2024
1 parent 0c1b1b7 commit 015830e
Show file tree
Hide file tree
Showing 7 changed files with 86 additions and 5 deletions.
4 changes: 3 additions & 1 deletion django_celery_results/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class TaskResultAdmin(admin.ModelAdmin):
'status', 'worker')
list_filter = ('status', 'date_done', 'periodic_task_name', 'task_name',
'worker')
readonly_fields = ('date_created', 'date_done', 'result', 'meta')
readonly_fields = ('date_created', 'date_started', 'date_done',
'result', 'meta')
search_fields = ('task_name', 'task_id', 'status', 'task_args',
'task_kwargs')
fieldsets = (
Expand All @@ -49,6 +50,7 @@ class TaskResultAdmin(admin.ModelAdmin):
'fields': (
'result',
'date_created',
'date_started',
'date_done',
'traceback',
'meta',
Expand Down
6 changes: 5 additions & 1 deletion django_celery_results/backends/database.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import binascii
import json

from celery import maybe_signature
from celery import maybe_signature, states
from celery.backends.base import BaseDictBackend, get_current_task
from celery.exceptions import ChordError
from celery.result import GroupResult, allow_join_result, result_from_tuple
from celery.utils.log import get_logger
from celery.utils.serialization import b64decode, b64encode
from django.db import connection, router, transaction
from django.db.models.functions import Now
from django.db.utils import InterfaceError
from kombu.exceptions import DecodeError

Expand Down Expand Up @@ -144,6 +145,9 @@ def _store_result(
self._get_extended_properties(request, traceback)
)

if status == states.STARTED:
task_props['date_started'] = Now()

self.TaskModel._default_manager.store_result(**task_props)
return result

Expand Down
5 changes: 4 additions & 1 deletion django_celery_results/managers.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def store_result(self, content_type, content_encoding,
traceback=None, meta=None,
periodic_task_name=None,
task_name=None, task_args=None, task_kwargs=None,
worker=None, using=None):
worker=None, using=None, **kwargs):
"""Store the result and status of a task.
Arguments:
Expand Down Expand Up @@ -161,6 +161,9 @@ def store_result(self, content_type, content_encoding,
'task_kwargs': task_kwargs,
'worker': worker
}
if 'date_started' in kwargs:
fields['date_started'] = kwargs['date_started']

obj, created = self.using(using).get_or_create(task_id=task_id,
defaults=fields)
if not created:
Expand Down
23 changes: 23 additions & 0 deletions django_celery_results/migrations/0012_taskresult_date_started.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.13 on 2024-06-02 07:56

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('django_celery_results', '0011_taskresult_periodic_task_name'),
]

operations = [
migrations.AddField(
model_name='taskresult',
name='date_started',
field=models.DateTimeField(
default=None,
help_text='Datetime field when the task was started in UTC',
null=True,
verbose_name='Started DateTime',
),
),
]
4 changes: 4 additions & 0 deletions django_celery_results/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,10 @@ class TaskResult(models.Model):
auto_now_add=True,
verbose_name=_('Created DateTime'),
help_text=_('Datetime field when the task result was created in UTC'))
date_started = models.DateTimeField(
null=True, default=None,
verbose_name=_('Started DateTime'),
help_text=_('Datetime field when the task was started in UTC'))
date_done = models.DateTimeField(
auto_now=True,
verbose_name=_('Completed DateTime'),
Expand Down
15 changes: 13 additions & 2 deletions docs/getting_started.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,19 @@ To use :pypi:`django-celery-results` with your project you need to follow these
CELERY_RESULT_EXTENDED = True
If you want to track the execution duration of your tasks (by comparing `date_created` and `date_done` in TaskResult), enable the :setting:`track_started` setting.
If you want to track the execution duration of your tasks (by comparing `date_started` and `date_done` in TaskResult), enable the :setting:`track_started` setting.

.. code-block:: python
CELERY_TASK_TRACK_STARTED = True
For example, if you write [additional codes](https://github.com/celery/django-celery-results/issues/286#issuecomment-1789094153) to record when a task becomes PENDING, you can calculate the waiting time in the queue or the actual processing time of the worker.

.. code-block:: python
task_result = TaskResult.objects.get(task_id='xxx')
waiting_time = task_result.date_started - task_result.date_created
processing_time = task_result.date_done - task_result.date_started
total_time = task_result.date_done - task_result.date_created
print(f'result: {waiting_time=}, {processing_time=}, {total_time=}')
34 changes: 34 additions & 0 deletions t/unit/backends/test_database.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import datetime
import json
import pickle
import re
Expand Down Expand Up @@ -549,6 +550,39 @@ def test_backend__task_result_meta_injection(self):
tr = TaskResult.objects.get(task_id=tid2)
assert json.loads(tr.meta) == {'key': 'value', 'children': []}

def test_backend__task_result_date(self):
tid2 = uuid()

self.b.store_result(tid2, None, states.PENDING)

tr = TaskResult.objects.get(task_id=tid2)
assert tr.status == states.PENDING
assert isinstance(tr.date_created, datetime.datetime)
assert tr.date_started is None
assert isinstance(tr.date_done, datetime.datetime)

date_created = tr.date_created
date_done = tr.date_done

self.b.mark_as_started(tid2)

tr = TaskResult.objects.get(task_id=tid2)
assert tr.status == states.STARTED
assert date_created == tr.date_created
assert isinstance(tr.date_started, datetime.datetime)
assert tr.date_done > date_done

date_started = tr.date_started
date_done = tr.date_done

self.b.mark_as_done(tid2, 42)

tr = TaskResult.objects.get(task_id=tid2)
assert tr.status == states.SUCCESS
assert tr.date_created == date_created
assert tr.date_started == date_started
assert tr.date_done > date_done

def xxx_backend(self):
tid = uuid()

Expand Down

0 comments on commit 015830e

Please sign in to comment.