Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ hide:

Full release notes for the Python SDK are available on [GitHub](https://github.com/taskbadger/taskbadger-python/releases).

## v2.4.0

**2026-07-30**

**Python SDK**

* **NEW** `heartbeat_interval` option for the [Celery](python-celery.md#keeping-long-running-tasks-fresh) and [Procrastinate](python-procrastinate.md#keeping-long-running-tasks-fresh) integrations. The worker updates running tasks for you so that long-running tasks don't go [`stale`](data_model.md#stale_timeout).
* **FIX** An eager Celery task no longer closes a Task Badger session opened by its caller.

## v2.3.1

**2026-07-27**
Expand Down
1 change: 1 addition & 0 deletions docs/data_model.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ The main attributes or a task are:
: This value can be used in conjunction with [actions](actions.md) and monitors to trigger alerts if a task
exceeds its expected runtime. The value is in seconds.

<a name="stale_timeout"></a>
`stale_timeout`

: This represents the maximum number of seconds allowed between task updates. If a task does not receive
Expand Down
51 changes: 51 additions & 0 deletions docs/python-celery.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ The `CelerySystemIntegration` class takes a number of optional parameters:

==Since v1.4.0==

- `heartbeat_interval`: Seconds between automatic task updates while a task is running. See
[Keeping Long-Running Tasks Fresh](#keeping-long-running-tasks-fresh).

==Since v2.4.0==

- `stale_timeout`: The [`stale_timeout`](data_model.md#stale_timeout) to set on tracked tasks.

==Since v2.4.0==

Exclusions take precedence over inclusions so if a task name matches both an include and an exclude, it will be
excluded.

Expand Down Expand Up @@ -176,6 +185,48 @@ def my_task(self, items):
creating the task, or the task is being run synchronously e.g. via `.apply()` or calling the task
using `.map` or `.starmap`, `.chunk`.

## Keeping Long-Running Tasks Fresh

==Since v2.4.0==

A task with a [`stale_timeout`](data_model.md#stale_timeout) is marked `stale` if it goes too long
without an update, so a long-running task that doesn't report progress will trip the timeout while it
is perfectly healthy. Setting `heartbeat_interval` (seconds) makes the worker update the task for you
while it runs, instead of having to do it from the task body.

The interval can be set on the system integration, on the task, or per call:

```python
# for all tracked tasks
taskbadger.init(
token="YOUR_API_KEY",
systems=[CelerySystemIntegration(heartbeat_interval=60)],
)

# on the task
@app.task(base=Task, taskbadger_heartbeat_interval=60)
def my_task():
...

# per call
my_task.apply_async(taskbadger_heartbeat_interval=60)
```

Unless `stale_timeout` is given explicitly it is set to twice the interval, so each of the examples
above creates the task with a `stale_timeout` of 120 seconds. Pass both to control it:

```python
my_task.apply_async(taskbadger_heartbeat_interval=60, taskbadger_stale_timeout=300)
```

As with the other options, values set on the task or on `apply_async` take precedence over the values
set on `CelerySystemIntegration`.

!!! note

All running tasks are updated from a single background thread per worker process, started the
first time a task with a heartbeat runs. Updates stop when the task finishes.

## Canvas primitives (map / starmap / chunks)

As of `v1.6.3`, Task Badger now tracks tasks created via Celery canvas primitives: `map`, `starmap`, and `chunks`. Previously these were executed as built-in `celery.map` / `celery.starmap` tasks and were filtered out; TaskBadger now creates task records for the *inner* tasks produced by these primitives.
Expand Down
52 changes: 52 additions & 0 deletions docs/python-procrastinate.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ The `ProcrastinateSystemIntegration` class takes the following parameters:
the patterns will not be tracked.
- `record_task_args`: If `True`, the job's keyword arguments will be recorded in the Task Badger task
data under `procrastinate_task_kwargs`.
- `heartbeat_interval`: Seconds between automatic task updates while a task is running. See
[Keeping Long-Running Tasks Fresh](#keeping-long-running-tasks-fresh). ==Since v2.4.0==
- `stale_timeout`: The [`stale_timeout`](data_model.md#stale_timeout) to set on tracked tasks.
==Since v2.4.0==

Patterns are matched against the full task name using `re.fullmatch`. Exclusions take precedence over
inclusions, so if a task name matches both an include and an exclude, it will be excluded.
Expand Down Expand Up @@ -112,6 +116,13 @@ task is created:
- `record_task_args`: If `True`, the job's keyword arguments are recorded under
`data["procrastinate_task_kwargs"]`. Defaults to inheriting the value from the
`ProcrastinateSystemIntegration` if one is configured, otherwise `False`.
- `heartbeat_interval`: Seconds between automatic task updates while the task is running. See
[Keeping Long-Running Tasks Fresh](#keeping-long-running-tasks-fresh). ==Since v2.4.0==
- `stale_timeout`: The [`stale_timeout`](data_model.md#stale_timeout) to set on the task.
==Since v2.4.0==

`record_task_args`, `heartbeat_interval` and `stale_timeout` are inherited from the
`ProcrastinateSystemIntegration` when they are not set on the decorator.

```python
@track(name="report", value_max=100, tags={"env": "prod"}, record_task_args=True)
Expand Down Expand Up @@ -144,6 +155,47 @@ async def report(rows):
`current_task()` returns `None` outside of a tracked job, if Task Badger has not been
[configured](python.md#configure), or if the task could not be fetched.

## Keeping Long-Running Tasks Fresh

==Since v2.4.0==

A task with a [`stale_timeout`](data_model.md#stale_timeout) is marked `stale` if it goes too long
without an update, so a long-running task that doesn't report progress will trip the timeout while it
is perfectly healthy. Setting `heartbeat_interval` (seconds) makes the worker update the task for you
while it runs, instead of having to do it from the job body.

The interval can be set on the task or on the system integration:

```python
# on the task
@track(heartbeat_interval=60)
@app.task
async def slow_job():
...


# for all tracked tasks
taskbadger.init(
token="YOUR_API_KEY",
systems=[ProcrastinateSystemIntegration(app=app, heartbeat_interval=60)],
)
```

Unless `stale_timeout` is given explicitly it is set to twice the interval, so both of the examples
above create tasks with a `stale_timeout` of 120 seconds. Pass both to control it:

```python
@track(heartbeat_interval=60, stale_timeout=300)
@app.task
async def slow_job():
...
```

!!! note

All running tasks are updated from a single background thread per worker process, started the
first time a task with a heartbeat runs. Updates stop when the task finishes.

## Periodic Tasks

Periodic tasks scheduled with `@app.periodic` are tracked as well. Each periodic deferral creates a new
Expand Down