You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Addresses a full design + docs re-review of the GTF resilience mechanisms.
Design:
- R1 (reaper): claim the FAILURE transition via CAS *before* firing destructive
cleanup. Revoke and out-of-band query cancellation now run only after the
reaper wins the CAS, so a briefly-stalled-but-healthy worker that revives and
commits its own terminal status keeps its warehouse query (no false-positive
kill, which would also cascade FAILURE to dependents).
- R2 (context): elect the abort/timeout/self-fence winner under a lock so abort
handlers run exactly once across the three daemon threads (listener, timeout
timer, heartbeat) that can race to abort.
- R3 (DAO): anchor the heartbeat write and the orphan scan on the *database*
clock (dialect-aware naive-UTC now, app-side fallback) so worker/reaper host
clock skew can't reap a live task early.
- R5 (heartbeat): only self-fence once the fence callback is armed (task is
executing); a stalled heartbeat during the pre-execution DAG wait keeps
retrying so recovered connectivity resumes the task instead of forfeiting it.
Docs:
- Document get_dependency_payloads() (+ the immediate=True pairing), add a
reap_orphaned_tasks beat-schedule snippet, name the
TASK_PROGRESS_UPDATE_THROTTLE_INTERVAL / TASK_ABORT_POLLING_DEFAULT_INTERVAL
tunables, and note orphan-reap/self-fence as FAILURE causes.
- Fix stale 'prune cron reaps' comments (reap.py, models/tasks.py, migration).
Tests: reaper no longer cancels/revokes on a lost CAS; concurrent abort election
runs handlers once; heartbeat does not fence before armed.
|`FAILURE`| Failed with error or abort/cleanup handler exception |
110
+
|`FAILURE`| Failed with error, abort/cleanup handler exception, orphan reaping, or worker self-fence|
111
111
|`ABORTED`| Cancelled by user/admin |
112
112
|`TIMED_OUT`| Exceeded configured timeout |
113
113
@@ -158,7 +158,7 @@ In the Task List UI, when a payload is defined, an info icon appears in the **De
158
158
159
159
#### Forcing an Immediate Write
160
160
161
-
By default `update_task()` throttles database writes (batching frequent updates to limit metastore load). Pass `immediate=True` to bypass throttling and write synchronously:
161
+
By default `update_task()` throttles database writes (batching frequent updates to limit metastore load, at most one write per `TASK_PROGRESS_UPDATE_THROTTLE_INTERVAL` seconds, default 2). Pass `immediate=True` to bypass throttling and write synchronously:
@@ -370,6 +370,21 @@ Because dependents hold a worker slot while awaiting their prerequisites, a deep
370
370
371
371
Cycles (including self-dependencies) are rejected at schedule time. Dependency edges are removed automatically when either endpoint task is pruned.
372
372
373
+
**Reading a prerequisite's output.** A dependent reads the payloads its prerequisites published via `ctx.get_dependency_payloads()`, which returns the prerequisites' payloads in dependency-edge order. Pair it with the prerequisite writing its result with `ctx.update_task(payload=..., immediate=True)` so the value is flushed (not held in the write-throttle buffer) by the time the dependency gate releases the dependent:
374
+
375
+
```python
376
+
@task
377
+
deftotals_task() -> None:
378
+
ctx = get_context()
379
+
# immediate=True so the dependent observes this the moment the gate releases.
@@ -422,12 +437,22 @@ A task whose worker dies mid-execution (OOM kill, crash, lost broker message) wo
422
437
423
438
Enable the `reap_orphaned_tasks` beat schedule on a short interval (e.g. every minute) so orphaned tasks — and their warehouse queries — do not linger; it is separate from `prune_tasks` (a heavier retention delete that runs infrequently). Keep `GTF_ORPHAN_TASK_TIMEOUT` comfortably larger than the heartbeat interval (≥ ~3×) so a brief pause or CPU-bound stretch is not mistaken for a dead worker.
424
439
440
+
```python
441
+
# In your superset_config.py, add to your Celery beat schedule:
"schedule": crontab(minute="*", hour="*"), # Run every minute
445
+
}
446
+
```
447
+
448
+
Unlike `prune_tasks`, the reaper takes no kwargs — it reads `GTF_ORPHAN_TASK_TIMEOUT` from config.
449
+
425
450
:::note Cancelling the underlying query
426
451
For long-running work backed by an external query, register an `on_abort` handler that cancels it (this is how async chart-data query tasks cancel the warehouse query on engines that support cancellation). Without such a handler an abort/timeout frees the task but cannot stop the external work.
427
452
:::
428
453
429
454
:::tip Distributed Coordination for Faster Notifications
430
-
By default, abort detection and sync join-and-wait poll the task row in the metadata database. Configure `DISTRIBUTED_COORDINATION_CONFIG` (Redis/Valkey) and these become event-driven: completion and abort are signalled over Redis **Streams**, so a waiter wakes when the signal lands instead of polling the database. Because stream entries are persisted, a waiter that reads slightly late, reconnects, or fails over still receives the signal. Each signal stream keeps only its latest entry and is given a TTL, so streams for tasks that are never awaited do not accumulate; set the retention window with `DISTRIBUTED_COORDINATION_SIGNAL_TTL` (default 24h). See [Distributed Coordination Backend](/admin-docs/configuration/cache#signal-cache-backend) for configuration details.
455
+
By default, abort detection and sync join-and-wait poll the task row in the metadata database (every `TASK_ABORT_POLLING_DEFAULT_INTERVAL` seconds, default 10). Configure `DISTRIBUTED_COORDINATION_CONFIG` (Redis/Valkey) and these become event-driven: completion and abort are signalled over Redis **Streams**, so a waiter wakes when the signal lands instead of polling the database. Because stream entries are persisted, a waiter that reads slightly late, reconnects, or fails over still receives the signal. Each signal stream keeps only its latest entry and is given a TTL, so streams for tasks that are never awaited do not accumulate; set the retention window with `DISTRIBUTED_COORDINATION_SIGNAL_TTL` (default 24h). See [Distributed Coordination Backend](/admin-docs/configuration/cache#signal-cache-backend) for configuration details.
431
456
:::
432
457
433
458
## API Reference
@@ -451,6 +476,7 @@ By default, abort detection and sync join-and-wait poll the task row in the meta
0 commit comments