From ddb29855964aa545fa72424d004ee2d18e5f76fe Mon Sep 17 00:00:00 2001 From: maybe-sybr <58414429+maybe-sybr@users.noreply.github.com> Date: Tue, 13 Oct 2020 16:31:29 +1100 Subject: [PATCH] fix: Pass back real result for single task chains When chains are delayed, they are first frozen as part of preparation which causes the sub-tasks to also be frozen. Afterward, the final (0th since we reverse the tasks/result order when freezing) result object from the freezing process would be passed back to the caller. This caused problems in signaling completion of groups contained in chains because the group relies on a promise which is fulfilled by a barrier linked to each of its applied subtasks. By constructing two `GroupResult` objects (one during freezing, one when the chain sub-tasks are applied), this resulted in there being two promises; only one of which would actually be fulfilled by the group subtasks. This change ensures that in the special case where the final task of a chain is a group, we pass back the `GroupResult` object constructed when the group was actually applied. The caller can then await the result confidently! --- celery/canvas.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/celery/canvas.py b/celery/canvas.py index f8278c6002..c26312a28c 100644 --- a/celery/canvas.py +++ b/celery/canvas.py @@ -662,8 +662,16 @@ def run(self, args=None, kwargs=None, group_id=None, chord=None, first_task = tasks.pop() options = _prepare_chain_from_options(options, tasks, use_link) - first_task.apply_async(**options) - return results[0] + real_result = first_task.apply_async(**options) + # If we only have a single task, it may be important that we pass + # the real result object rather than the one obtained via freezing. + # e.g. For `GroupResult`s, we need to pass back the result object + # which will actually have its promise fulfilled by the subtasks, + # something that will never occur for the frozen result. + if not tasks: + return real_result + else: + return results[0] def freeze(self, _id=None, group_id=None, chord=None, root_id=None, parent_id=None, group_index=None):