Become a Celery expert TODAY! Sign up for my newsletter.
Tell me where to send your free Celery Bootcamp lessons.

# distributedpython.com

Custom Celery task states

How to hack Celery task states

Published on September 28, 2018Estimated reading time: 8 minutes

Celery tasks always have a state. If a task finished executing successfully, its state is SUCCESS.
If a task execution resulted in an exception, its state is FAILURE. Celery knows
six built-in states:

PENDING (waiting for execution or unknown task id)

STARTED (task has been started)

SUCCESS (task executed successfully)

FAILURE (task execution resulted in exception)

RETRY (task is being retried)

REVOKED (task has been revoked)

In case you wonder why you have never come across the STARTED state,
it is not reported by default. You have to enable it explicitly via the Celery config,
setting task_track_started = True.

The update_state method

The Celery task object provides an update_state method. This method lets you
do three things:

set the task’s state to one of the built-in states

provide additional meta data

set the task’s state to any custom state you define.

All you need to define your own state is a unique name. It is just a string and
does not need to be registered anywhere. For example, if you have
a long running task, you can define a PROGRESS state and publish the
progress made via the meta json argument:

This task runs for ~30 seconds and sends a task state update every ~1 second,
broadcasting a custom PROGRESS state and the number of total and
completed iterations. Let’s execute the task asynchronously, wait for the
task to finish and capture the state and meta data while it’s still running:

This is a very simple example. But if we take a closer look, there are a few
very interesting learnings:

any string can be a custom state

a custom state is only temporary and is eventually overriden by a Celery built-in state as soon as the task finishes successfully - or throws an exception, is retried or revoked (the same applies if we uset update_state with a built-in state but custom meta data - the custom meta data is ultimatemy overwritten by Celery)

while the task is in a custom state, the meta argument we published via update_state is available as info property on the AsyncResult object (the object .delay() returns on the execution side)

when the task is in the built-in SUCCESS state, the info property returns the task result (when the task failed, the info property returns the exception type and stacktrace, try it yourself by throwing an exception in the implementation of the task function above)

Built-in state with manual task result handling

Say, you want to provide some additional custom data for a failed tasks. Unfortunately, as we established above,
Celery will overwrite the custom meta data, even if we use a built-in state type. Fortunately, there
is a way to prevent this, raising an celery.exceptions.Ignore() exception. This means,
no state will be recorded for the task, but the message is still removed from the queue

But, it turns out that, depending on the built-in task state, Celery expects the
corresponding meta data dictionary to be in a particular format. And here, the meta data
itself is incompatible with the FAILURE state:

And this time we can get the task’s state and info without Celery throwing an exception. And
we also have access to the custom field. Note that we have to retrieve the
result from the backend via task.backend.get(...) as Celery parses the result dict
depending on the task’s state.

Conclusion

Celery provides a lot of flexibility when it comes to custom task states and
custom meta data. Transient custom states in combination with custom
meta data can be used to implement task progress trackers. Or, you might have a good
reason to implement your own final custom task state, which Celery
can equally cater for. You can even enrich a built-in the FAILURE task state
with additional data. For further information, I encourage you to read the docs and play
around with a few code examples.