mirror of
https://github.com/django/django.git
synced 2025-04-06 22:46:41 +00:00
Flesh out tasks topic more
This commit is contained in:
parent
a09bc9313c
commit
358e0f4bf5
@ -712,7 +712,7 @@ Signals sent by the :doc:`tasks </ref/tasks>` framework.
|
||||
.. data:: django.tasks.signals.task_enqueued
|
||||
|
||||
Sent once a task has been enqueued. If
|
||||
:attr:`django.tasks.task.Task.enqueue_on_commit` is set, the signal is only sent
|
||||
:attr:`django.tasks.Task.enqueue_on_commit` is set, the signal is only sent
|
||||
once the transaction commits successfully.
|
||||
|
||||
Arguments that are sent with this signal:
|
||||
@ -721,7 +721,7 @@ Arguments that are sent with this signal:
|
||||
The backend class which the task was enqueued on to.
|
||||
|
||||
``task_result``
|
||||
The enqueued :class:`TaskResult <django.tasks.task.TaskResult>`.
|
||||
The enqueued :class:`TaskResult <django.tasks.TaskResult>`.
|
||||
|
||||
.. data:: django.tasks.signals.task_finished
|
||||
|
||||
@ -733,4 +733,4 @@ Arguments that are sent with this signal:
|
||||
The backend class which the task was enqueued on to.
|
||||
|
||||
``task_result``
|
||||
The finished :class:`TaskResult <django.tasks.task.TaskResult>`.
|
||||
The finished :class:`TaskResult <django.tasks.TaskResult>`.
|
||||
|
@ -5,86 +5,9 @@ Tasks
|
||||
.. module:: django.tasks
|
||||
:synopsis: Django's built-in background task system.
|
||||
|
||||
Backends
|
||||
========
|
||||
|
||||
Base backend
|
||||
------------
|
||||
|
||||
.. module:: django.tasks.backends.base
|
||||
|
||||
.. class:: BaseTaskBackend
|
||||
|
||||
``BaseTaskBackend`` is the parent class for all task backends.
|
||||
|
||||
.. method:: BaseTaskBackend.get_result(result_id)
|
||||
|
||||
Retrieve a result by its id. If the result does not exist,
|
||||
:exc:`ResultDoesNotExist <django.tasks.exceptions.ResultDoesNotExist>`
|
||||
is raised.
|
||||
|
||||
If the backend does not support ``get_result``, :exc:`NotImplementedError`
|
||||
is raised.
|
||||
|
||||
.. method:: BaseTaskBackend.aget_result(result_id)
|
||||
|
||||
The ``async`` variant of :meth:`BaseTaskBackend.get_result`.
|
||||
|
||||
Introspection
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
Introspection allows for identifying the supported functionality of a
|
||||
backend, and potentially changing behavior accordingly.
|
||||
|
||||
.. attribute:: BaseTaskBackend.supports_defer
|
||||
|
||||
Whether the backend supports enqueueing tasks to be
|
||||
executed after a specific time using the ``run_after`` attribute.
|
||||
|
||||
.. attribute:: BaseTaskBackend.supports_async_task
|
||||
|
||||
Whether the backend supports enqueueing async functions (coroutines).
|
||||
|
||||
.. attribute:: BaseTaskBackend.supports_get_result
|
||||
|
||||
Whether the backend supports retrieving task results from another thread
|
||||
after they have been enqueued.
|
||||
|
||||
Available backends
|
||||
------------------
|
||||
|
||||
Immediate backend
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. module:: django.tasks.backends.immediate
|
||||
|
||||
.. class:: ImmediateBackend
|
||||
|
||||
The immediate backend executes tasks immediately, rather than in the background.
|
||||
|
||||
Dummy backend
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. module:: django.tasks.backends.dummy
|
||||
|
||||
.. class:: DummyBackend
|
||||
|
||||
The dummy backend doesn't execute enqueued tasks at all, instead storing results
|
||||
for later use.
|
||||
|
||||
.. attribute:: DummyBackend.results
|
||||
|
||||
A list of results for the enqueued tasks, in the order they were enqueued.
|
||||
|
||||
.. method:: DummyBackend.clear
|
||||
|
||||
Clears the list of stored results.
|
||||
|
||||
Task definition
|
||||
===============
|
||||
|
||||
.. module:: django.tasks.task
|
||||
|
||||
.. function:: task
|
||||
|
||||
A decorator defining a :class:`Task`::
|
||||
@ -117,7 +40,8 @@ Attributes of ``Task`` cannot be modified.
|
||||
|
||||
.. attribute:: Task.priority
|
||||
|
||||
The priority of the task. Priorities must be between -100 and 100.
|
||||
The priority of the task. Priorities must be between -100 and 100, where
|
||||
larger numbers are higher priority, and will be run sooner.
|
||||
|
||||
By default, tasks are enqueued with a priority of 0.
|
||||
|
||||
@ -161,8 +85,9 @@ Attributes of ``Task`` cannot be modified.
|
||||
* :attr:`queue_name <Task.queue_name>`
|
||||
* :attr:`run_after <Task.run_after>`
|
||||
|
||||
``run_after`` may also be provided as a :class:`timedelta <datetime.timedelta>`, which
|
||||
is used relative to the current time (when ``using`` is called).
|
||||
``run_after`` may also be provided as a :class:`timedelta <datetime.timedelta>`,
|
||||
which is used relative to the current time (when ``using`` is called), or a
|
||||
timezone-aware :class:`datetime <datetime.datetime>`.
|
||||
|
||||
.. method:: Task.enqueue(*args, **kwargs)
|
||||
|
||||
@ -279,6 +204,81 @@ Attributes of ``TaskResult`` cannot be modified.
|
||||
|
||||
The ``async`` variant of :meth:`TaskResult.refresh`.
|
||||
|
||||
Backends
|
||||
========
|
||||
|
||||
Base backend
|
||||
------------
|
||||
|
||||
.. module:: django.tasks.backends.base
|
||||
|
||||
.. class:: BaseTaskBackend
|
||||
|
||||
``BaseTaskBackend`` is the parent class for all task backends.
|
||||
|
||||
.. method:: BaseTaskBackend.get_result(result_id)
|
||||
|
||||
Retrieve a result by its id. If the result does not exist,
|
||||
:exc:`ResultDoesNotExist <django.tasks.exceptions.ResultDoesNotExist>`
|
||||
is raised.
|
||||
|
||||
If the backend does not support ``get_result``, :exc:`NotImplementedError`
|
||||
is raised.
|
||||
|
||||
.. method:: BaseTaskBackend.aget_result(result_id)
|
||||
|
||||
The ``async`` variant of :meth:`BaseTaskBackend.get_result`.
|
||||
|
||||
Features
|
||||
~~~~~~~~
|
||||
|
||||
Some backends may not support all features Django provides. It's possible to
|
||||
identifying the supported functionality of a backend, and potentially
|
||||
changing behavior accordingly.
|
||||
|
||||
.. attribute:: BaseTaskBackend.supports_defer
|
||||
|
||||
Whether the backend supports enqueueing tasks to be
|
||||
executed after a specific time using the ``run_after`` attribute.
|
||||
|
||||
.. attribute:: BaseTaskBackend.supports_async_task
|
||||
|
||||
Whether the backend supports enqueueing async functions (coroutines).
|
||||
|
||||
.. attribute:: BaseTaskBackend.supports_get_result
|
||||
|
||||
Whether the backend supports retrieving task results from another thread
|
||||
after they have been enqueued.
|
||||
|
||||
Available backends
|
||||
------------------
|
||||
|
||||
Immediate backend
|
||||
~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. module:: django.tasks.backends.immediate
|
||||
|
||||
.. class:: ImmediateBackend
|
||||
|
||||
The immediate backend executes tasks immediately, rather than in the background.
|
||||
|
||||
Dummy backend
|
||||
~~~~~~~~~~~~~
|
||||
|
||||
.. module:: django.tasks.backends.dummy
|
||||
|
||||
.. class:: DummyBackend
|
||||
|
||||
The dummy backend doesn't execute enqueued tasks at all, instead storing results
|
||||
for later use.
|
||||
|
||||
.. attribute:: DummyBackend.results
|
||||
|
||||
A list of results for the enqueued tasks, in the order they were enqueued.
|
||||
|
||||
.. method:: DummyBackend.clear
|
||||
|
||||
Clears the list of stored results.
|
||||
|
||||
Exceptions
|
||||
==========
|
||||
@ -292,5 +292,5 @@ Exceptions
|
||||
|
||||
.. exception:: InvalidTaskError
|
||||
|
||||
Raised when the :class:`Task <django.tasks.task.Task>` attempting to be
|
||||
Raised when the :class:`Task <django.tasks.Task>` attempting to be
|
||||
enqueued is invalid.
|
||||
|
@ -133,3 +133,205 @@ Django has developing support for asynchronous task backends.
|
||||
``django.tasks.backends.base.BaseTaskBackend`` has async variants of all base
|
||||
methods. By convention, the asynchronous versions of all methods are prefixed
|
||||
with ``a``. The arguments for both variants are the same.
|
||||
|
||||
Retrieving backends
|
||||
-------------------
|
||||
|
||||
Backends can be retrieved using the ``tasks`` connection handler::
|
||||
|
||||
from django.tasks import tasks
|
||||
|
||||
tasks["default"] # The default backend
|
||||
tasks["reserve"] # Another backend
|
||||
|
||||
The "default" backend is available as ``default_task_backend``::
|
||||
|
||||
from django.tasks import default_task_backend
|
||||
|
||||
Backend features
|
||||
----------------
|
||||
|
||||
Django's tasks framework is designed to be backend-agnostic. This means some
|
||||
backends may not implement the same features as others, such as executing ``async``
|
||||
functions.
|
||||
|
||||
Depending on the backend configured, it may be necessary to gracefully degrade
|
||||
functionality, or use a system check to prevent the application from starting at all.
|
||||
|
||||
To facilitate this, certain features can be checked on a backend:
|
||||
|
||||
* :attr:`supports_defer <django.tasks.backends.base.BaseTaskBackend.supports_defer>`:
|
||||
Can tasks be executed after a specific time using ``run_after``?
|
||||
|
||||
* :attr:`supports_async_task <django.tasks.backends.base.BaseTaskBackend.supports_async_task>`:
|
||||
Can ``async`` functions (coroutines) be used as task functions?
|
||||
|
||||
* :attr:`supports_get_result <django.tasks.backends.base.BaseTaskBackend.supports_get_result>`:
|
||||
Can a task's results be retrieved from another thread or process?
|
||||
|
||||
Defining tasks
|
||||
==============
|
||||
|
||||
Tasks are defined using the :meth:`django.tasks.task` decorator on a
|
||||
module-level function::
|
||||
|
||||
from django_tasks import task
|
||||
|
||||
|
||||
@task()
|
||||
def calculate_meaning_of_life(answer=42):
|
||||
return answer
|
||||
|
||||
Returned in a :class:`django.tasks.Task` instance.
|
||||
|
||||
.. note::
|
||||
|
||||
All arguments and return values must be JSON serializable, and round-trip
|
||||
as the same types.
|
||||
|
||||
This means complex types like model instances, as well as many built-in types
|
||||
like ``datetime`` and ``tuple`` cannot be used in tasks without additional
|
||||
conversion.
|
||||
|
||||
The ``task`` decorator accepts a few keyword arguments to customize the task:
|
||||
|
||||
* ``priority``: The priority of the task. Higher numbers will be executed sooner.
|
||||
|
||||
* ``queue_name``: The name of the queue the task will be executed on
|
||||
|
||||
* ``backend``: The name of the backend this task must use (as defined in
|
||||
:setting:`TASKS`).
|
||||
|
||||
* ``enqueue_on_commit``: Whether the task is enqueued when the current transaction
|
||||
commits successfully, or enqueued immediately.
|
||||
|
||||
These arguments correspond to attributes on the created
|
||||
:class:`Task <django.tasks.Task>`.
|
||||
|
||||
By convention, tasks should be defined in a ``tasks.py`` file, however this is
|
||||
not enforced.
|
||||
|
||||
Modifying tasks
|
||||
---------------
|
||||
|
||||
Before enqueueing tasks, it may be necessary to modify certain parameters of the task.
|
||||
For example, to give it a higher priority than it would normally.
|
||||
|
||||
A ``Task`` instance cannot be modified directly. Instead, a modified instance
|
||||
can be created with the :meth:`using <django.tasks.Task.using>` method, leaving
|
||||
the original as-is::
|
||||
|
||||
calculate_meaning_of_life.priority # 0
|
||||
|
||||
calculate_meaning_of_life.using(priority=10).priority # 10
|
||||
|
||||
``using`` allows modifying the following attributes:
|
||||
|
||||
* :attr:`priority <django.tasks.Task.priority>`
|
||||
* :attr:`backend <django.tasks.Task.backend>`
|
||||
* :attr:`queue_name <django.tasks.Task.queue_name>`
|
||||
* :attr:`run_after <django.tasks.Task.run_after>`
|
||||
|
||||
``run_after`` may also be provided as a :class:`timedelta <datetime.timedelta>`,
|
||||
which is used relative to the current time (when ``using`` is called), or a
|
||||
timezone-aware :class:`datetime <datetime.datetime>`.
|
||||
|
||||
Enqueueing tasks
|
||||
================
|
||||
|
||||
To add the task to the queue store, so it will be executed, call the ``enqueue``
|
||||
method on it::
|
||||
|
||||
result = calculate_meaning_of_life.enqueue()
|
||||
|
||||
Returned is a :class:`django.tasks.TaskResult`, which can be used to retrieve
|
||||
the result of the task once it has finished executing.
|
||||
|
||||
If the task takes arguments, these can be passed as-is to ``enqueue``::
|
||||
|
||||
result = calculate_meaning_of_life.enqueue(answer=42)
|
||||
|
||||
To enqueue tasks in an ``async`` context, :meth:`aenqueue <django.tasks.Task.aenqueue>`
|
||||
is available as an ``async`` variant of ``enqueue``.
|
||||
|
||||
Transactions
|
||||
------------
|
||||
|
||||
By default, tasks are enqueued after the current transaction (if there is one)
|
||||
commits successfully (using :meth:`transaction.on_commit <django.db.transaction.on_commit>`),
|
||||
rather than enqueueing immediately.
|
||||
|
||||
This behavior can be changed by changing the :setting:`TASKS-ENQUEUE_ON_COMMIT`
|
||||
setting for the backend, or for a specific task using the ``enqueue_on_commit``
|
||||
parameter.
|
||||
|
||||
Task results
|
||||
============
|
||||
|
||||
When enqueueing a ``Task``, you receive a :class:`django.tasks.TaskResult`,
|
||||
however it's likely useful to retrieve the result from somewhere else (for example
|
||||
another request or another task).
|
||||
|
||||
Each ``TaskResult`` has a unique :attr:`id <django.tasks.TaskResult.id>`, which
|
||||
can be used to identify and retrieve the result once the code which enqueued the
|
||||
task has finished.
|
||||
|
||||
The :meth:`django.tasks.Task.get_result` method can retrieve a result based on
|
||||
its ``id``::
|
||||
|
||||
# Later, somewhere else...
|
||||
result = calculate_meaning_of_life.get_result(result_id)
|
||||
|
||||
If the ``TaskResult`` exists, it is returned. If it doesn't exist, or isn't a
|
||||
result for ``calculate_meaning_of_life``, :exc:`django.tasks.exceptions.ResultDoesNotExist`
|
||||
is raised.
|
||||
|
||||
To retrieve a ``TaskResult``, regardless of which kind of ``Task`` it was from,
|
||||
use the ``get_result`` method on the API
|
||||
|
||||
To retrieve results in an ``async`` context, :meth:`aget_result <django.tasks.Task.aget_result>`
|
||||
is available as an ``async`` variant of ``get_result`` on both the backend and ``Task``.
|
||||
|
||||
Some backends, such as the built in ``ImmediateBackend`` do not support ``get_result``.
|
||||
Calling ``get_result`` on these backends will raise :exc:`NotImplementedError`.
|
||||
|
||||
Updating results
|
||||
----------------
|
||||
|
||||
A ``TaskResult`` contains the status of a task's execution at the point it was
|
||||
retrieved. If the task finishes after ``get_result`` is called, it will not update.
|
||||
|
||||
To refresh the values, call the :meth:`django.tasks.TaskResult.refresh` method::
|
||||
|
||||
result.status # RUNNING
|
||||
|
||||
result.refresh() # or await result.arefresh()
|
||||
|
||||
result.status # COMPLETE
|
||||
|
||||
Return values
|
||||
-------------
|
||||
|
||||
If your task function returns something, it can be retrieved from the
|
||||
:attr:`django.tasks.TaskResult.return_value` attribute::
|
||||
|
||||
result.return_value # 42
|
||||
|
||||
If the task has not finished executing, or has failed, :exc:`ValueError` is raised.
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
|
||||
If the task doesn't complete successfully, and instead raises an exception, either
|
||||
as part of the task or as part of running it, the exception instance is saved
|
||||
to the :attr:`django.tasks.TaskResult.exception` attribute::
|
||||
|
||||
assert isinstance(result.exception, ValueError)
|
||||
|
||||
As part of the serialization process for exceptions, some information is lost.
|
||||
The traceback information is reduced to a string which you can use to help
|
||||
debugging::
|
||||
|
||||
print(result.traceback)
|
||||
|
||||
If the exception could not be serialized, ``exception`` is ``None``.
|
||||
|
Loading…
x
Reference in New Issue
Block a user