Skip to content

Commit 93739ef

Browse files
authored
Merge pull request #18 from taskbadger/sk/heartbeat
Document heartbeat_interval (SDK v2.4.0)
2 parents 15c5dac + 60d1cf0 commit 93739ef

4 files changed

Lines changed: 113 additions & 0 deletions

File tree

docs/changelog.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ hide:
77

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

10+
## v2.4.0
11+
12+
**2026-07-30**
13+
14+
**Python SDK**
15+
16+
* **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).
17+
* **FIX** An eager Celery task no longer closes a Task Badger session opened by its caller.
18+
1019
## v2.3.1
1120

1221
**2026-07-27**

docs/data_model.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ The main attributes or a task are:
5454
: This value can be used in conjunction with [actions](actions.md) and monitors to trigger alerts if a task
5555
exceeds its expected runtime. The value is in seconds.
5656

57+
<a name="stale_timeout"></a>
5758
`stale_timeout`
5859

5960
: This represents the maximum number of seconds allowed between task updates. If a task does not receive

docs/python-celery.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,15 @@ The `CelerySystemIntegration` class takes a number of optional parameters:
4141

4242
==Since v1.4.0==
4343

44+
- `heartbeat_interval`: Seconds between automatic task updates while a task is running. See
45+
[Keeping Long-Running Tasks Fresh](#keeping-long-running-tasks-fresh).
46+
47+
==Since v2.4.0==
48+
49+
- `stale_timeout`: The [`stale_timeout`](data_model.md#stale_timeout) to set on tracked tasks.
50+
51+
==Since v2.4.0==
52+
4453
Exclusions take precedence over inclusions so if a task name matches both an include and an exclude, it will be
4554
excluded.
4655

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

188+
## Keeping Long-Running Tasks Fresh
189+
190+
==Since v2.4.0==
191+
192+
A task with a [`stale_timeout`](data_model.md#stale_timeout) is marked `stale` if it goes too long
193+
without an update, so a long-running task that doesn't report progress will trip the timeout while it
194+
is perfectly healthy. Setting `heartbeat_interval` (seconds) makes the worker update the task for you
195+
while it runs, instead of having to do it from the task body.
196+
197+
The interval can be set on the system integration, on the task, or per call:
198+
199+
```python
200+
# for all tracked tasks
201+
taskbadger.init(
202+
token="YOUR_API_KEY",
203+
systems=[CelerySystemIntegration(heartbeat_interval=60)],
204+
)
205+
206+
# on the task
207+
@app.task(base=Task, taskbadger_heartbeat_interval=60)
208+
def my_task():
209+
...
210+
211+
# per call
212+
my_task.apply_async(taskbadger_heartbeat_interval=60)
213+
```
214+
215+
Unless `stale_timeout` is given explicitly it is set to twice the interval, so each of the examples
216+
above creates the task with a `stale_timeout` of 120 seconds. Pass both to control it:
217+
218+
```python
219+
my_task.apply_async(taskbadger_heartbeat_interval=60, taskbadger_stale_timeout=300)
220+
```
221+
222+
As with the other options, values set on the task or on `apply_async` take precedence over the values
223+
set on `CelerySystemIntegration`.
224+
225+
!!! note
226+
227+
All running tasks are updated from a single background thread per worker process, started the
228+
first time a task with a heartbeat runs. Updates stop when the task finishes.
229+
179230
## Canvas primitives (map / starmap / chunks)
180231

181232
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.

docs/python-procrastinate.md

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ The `ProcrastinateSystemIntegration` class takes the following parameters:
5151
the patterns will not be tracked.
5252
- `record_task_args`: If `True`, the job's keyword arguments will be recorded in the Task Badger task
5353
data under `procrastinate_task_kwargs`.
54+
- `heartbeat_interval`: Seconds between automatic task updates while a task is running. See
55+
[Keeping Long-Running Tasks Fresh](#keeping-long-running-tasks-fresh). ==Since v2.4.0==
56+
- `stale_timeout`: The [`stale_timeout`](data_model.md#stale_timeout) to set on tracked tasks.
57+
==Since v2.4.0==
5458

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

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

158+
## Keeping Long-Running Tasks Fresh
159+
160+
==Since v2.4.0==
161+
162+
A task with a [`stale_timeout`](data_model.md#stale_timeout) is marked `stale` if it goes too long
163+
without an update, so a long-running task that doesn't report progress will trip the timeout while it
164+
is perfectly healthy. Setting `heartbeat_interval` (seconds) makes the worker update the task for you
165+
while it runs, instead of having to do it from the job body.
166+
167+
The interval can be set on the task or on the system integration:
168+
169+
```python
170+
# on the task
171+
@track(heartbeat_interval=60)
172+
@app.task
173+
async def slow_job():
174+
...
175+
176+
177+
# for all tracked tasks
178+
taskbadger.init(
179+
token="YOUR_API_KEY",
180+
systems=[ProcrastinateSystemIntegration(app=app, heartbeat_interval=60)],
181+
)
182+
```
183+
184+
Unless `stale_timeout` is given explicitly it is set to twice the interval, so both of the examples
185+
above create tasks with a `stale_timeout` of 120 seconds. Pass both to control it:
186+
187+
```python
188+
@track(heartbeat_interval=60, stale_timeout=300)
189+
@app.task
190+
async def slow_job():
191+
...
192+
```
193+
194+
!!! note
195+
196+
All running tasks are updated from a single background thread per worker process, started the
197+
first time a task with a heartbeat runs. Updates stop when the task finishes.
198+
147199
## Periodic Tasks
148200

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

0 commit comments

Comments
 (0)