Description
I'm using a chord to run a task upon completion of a parallel group of tasks. The results of those tasks have already been dealt with, so the body of the chord just needs data which I pass in via an immutable signature.
Here's an approximate example:
@shared_task
def do_expensive_thing(doc_id):
return some_expensive_function(doc_id)
@shared_task
def save_result(result, doc_id):
# note no return value
save_to_db(doc_id, result)
@shared_task
def all_completed(task_id):
mark_completed(task_id)
doc_tasks = []
for doc in all_documents:
doc_tasks.append(
do_expensive_thing.si(doc_id=doc.id) |
save_result.s(doc_id=doc.id)
)
# note it's an immutable signature
completed_task = all_completed.si(task_id=123)
my_chord = chord(header=doc_tasks, body=completed_task)
Note that the chord body signature is set as immutable with .si()
When I run this with a large body size (len(all_documents) = 1000
), I get the following error:
[2025-06-16 10:53:29,059: ERROR/ForkPoolWorker-16] Chord '98b726ca-8b3d-40bd-946f-dc0f373a3074' raised: TimeoutError('join operation timed out')
Traceback (most recent call last):
File "/path/to/venv/lib/python3.12/site-packages/django_celery_results/backends/database.py", line 298, in trigger_callback
ret = j(timeout=app.conf.result_chord_join_timeout, propagate=True)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/path/to/venv/lib/python3.12/site-packages/celery/result.py", line 774, in join
raise TimeoutError('join operation timed out')
celery.exceptions.TimeoutError: join operation timed out
This makes some sense, as it's trying to join over 1,000 results. However, per the Celery Canvas docs:
The body of a chord can also be immutable, so that the return value of the group isn’t passed on to the callback
So I would expect, given that the body of the chord is immutable, that it doesn't attempt to join the header group's results, as they won't be used by the body task.
This appears to be an issue with django-celery-results, as the whole process of going from a chord part returning to executing the join happens in django-celery-results code (either this method or this function).
Another slightly weird thing is that save_result
doesn't actually return anything and thus the return value of each task in the chord header is None
. So it's just timing out from the number of the tasks and has nothing to do with the size of the result data.
Versions:
- django_celery_results 2.6.0
- celery 5.4.0 (I have checked and there's nothing relevant in the changelog since)