Skip to content

Django Tasks integration

Goatbeat does not run tasks. Each due occurrence becomes one Task.enqueue() call; your configured Django Tasks backend and its workers do the rest.

How an occurrence is submitted

task.using(queue_name=..., priority=..., backend=...).enqueue(*args, **kwargs)

Routing comes from the schedule (queue=, priority=, backend=); options left unset keep the task's own defaults. An unknown queue or backend makes the submission fail and be retried, then marked failed.

The idempotency key

Each occurrence has a key such as 9f3c...:2026-10-15T09:00:00+00:00, or manual:<uuid> for extra runs. Django Tasks has no caller-provided id, so a task that wants the key declares a parameter for it:

@task
def send_report(goatbeat_key: str = "") -> None:
    if already_sent(goatbeat_key):
        return
    ...

Declare such a task with task=call(send_report): the extra parameter makes it a task with arguments for the type checker. Use the key to make the task idempotent: in rare crash windows an occurrence is submitted twice.

Completion tracking

Goatbeat listens to django.tasks.signals.task_finished and marks the execution completed or task_failed. Immediate backends report the outcome during enqueue. If a fast worker finishes before the scheduler recorded the dispatch, the completion is kept for a day and applied when the dispatch is recorded. Bookkeeping errors are logged and never fail the task.

Backends

Django 6.1 ships ImmediateBackend (runs inline, useful in development) and DummyBackend (stores results, for tests). Production needs a worker backend from a third-party package; any backend implementing the public Django Tasks API works with Goatbeat.