Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2951a9c
Consistently crash task runners on DB failure
mxsrc Jul 18, 2026
52b9df7
Unify task retry semantics
mxsrc Jul 18, 2026
4aff904
Apply task lease consistently to all task runners
mxsrc Jul 18, 2026
c49b88b
Reclassify backup policy evaluation as service
mxsrc Jul 18, 2026
b77632d
Introduce generic task runner
mxsrc Jul 26, 2026
c6a003d
Handover documents
mxsrc Jul 27, 2026
ee0e34a
Reset task result before each handler attempt
mxsrc Aug 5, 2026
ef28b05
Migrate FDB backup runner onto the task runner
mxsrc Aug 5, 2026
e12506e
Migrate JC compression resume runner onto the task runner
mxsrc Aug 5, 2026
161b25c
Migrate replication cutover runner onto the task runner
mxsrc Aug 5, 2026
cf7898f
Migrate lvol sync runner onto the task runner
mxsrc Aug 5, 2026
fefd4fe
Migrate backup and cluster expand runners onto the task runner
mxsrc Aug 5, 2026
dcd1fa4
Migrate node add runner onto the task runner
mxsrc Aug 5, 2026
af20738
Update handover document
mxsrc Aug 5, 2026
5176ecb
Commit task transitions by compare-and-set
mxsrc Aug 5, 2026
844071d
Revise plan for the upstream restart hardening
mxsrc Aug 5, 2026
efa72da
Cancel tasks by compare-and-set
mxsrc Aug 5, 2026
2cd55bf
Cancel a node's tasks by compare-and-set on shutdown
mxsrc Aug 5, 2026
bf55257
Add handler checkpoints and a per-cycle hook to the task runner
mxsrc Aug 5, 2026
70b97d7
Migrate restart runner onto the task runner
mxsrc Aug 5, 2026
8d6ce10
Update handover document
mxsrc Aug 5, 2026
135391b
Migrate the device migration runners onto the task runner
mxsrc Aug 5, 2026
4ade335
Migrate node removal and port allow runners onto the task runner
mxsrc Aug 6, 2026
2523be0
Update handover document
mxsrc Aug 6, 2026
b0b9728
Report task failures through the task runner
mxsrc Aug 22, 2026
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
103 changes: 103 additions & 0 deletions HANDOFF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# Task-runner rework — handoff

Continuation notes for the `rework-task-runners` branch. The full design lives
in **`TASK_RUNNER_REWORK_PLAN.md`** (same dir) — read it first; this file is
just the current state + how to continue.

## Where the work is

Branch `rework-task-runners`, rebased onto `origin/main` (2026-08-05, commit
`4cb16a20b`). Pre-rebase safety backup: branch `backup/rework-pre-rebase3`.

```
fa26b7121 Migrate node add runner… (B3)
f954be2b9 Migrate backup and cluster expand… (B2)
d6cc94478 Migrate lvol sync runner… (B1d)
b436fa79d Migrate replication cutover runner… (B1c)
9c9118761 Migrate JC compression resume runner… (B1b)
d17c20064 Migrate FDB backup runner… (B1a)
3899c2452 Reset task result before each handler attempt
ad4768462 Introduce generic task runner (B0)
<A5/A3/A2/A1 commits below>
```

## Status: the planned scope is complete

All thirteen runners in scope are on the driver: `fdb_backup`, `jc_comp`,
`replication_final`, `sync_lvol_del`, `backup`, `cluster_expand`, `node_add`,
`restart`, `migration`, `new_dev_migration`, `failed_migration`, `node_removal`,
`port_allow`.

Nothing outside a runner writes task lifecycle state any more:
`utils.handle_task_result` and `tasks_controller.defer_task_for_expansion` are
gone, and the three cancellation paths commit by CAS.

**Deferred to a follow-up PR:** `lvol_migration` and `batch_migration` — under
active upstream rewrite (~17 commits in the last window, several still labelled
`TEMP:`). They keep their own loops meanwhile, which is why
`tasks_cluster_status.py` is not the only file left untouched. Migrating them is
the same exercise: their pre-run guard chains become `is_eligible`, their poll
loops `TaskProgress`, and their `_suspend_task(charge_retry=...)` helper maps
directly onto TaskDefer vs TaskRetry.

## What the driver grew during the migrations

Beyond what the plan describes, `task_runner_base` gained:

- **`function_result` is cleared before each handler attempt**, so a task that
fails and later succeeds doesn't finish carrying the stale failure message.
- **`RunnerSpec.on_finish(task)`** — cleanup called after the task reaches
STATUS_DONE and is written, on *every* terminal path (success, `TaskAbort`,
cancel, retry ceiling). Needed because a handler never sees the terminal
paths the driver owns, yet resources it holds must still be released:
`sync_lvol_del` frees the primary's del-sync lock there, `backup` fails/
un-merges the backup resource there. Both are written to be no-ops when the
handler already finished the resource.
- **All task writes are compare-and-set** (`_commit`), never full-object
`write_to_db`. This is not a refinement — the original driver reproduced the
lost update behind upstream's 2026-07-29 double-restart incident, and held the
stale copy for the whole handler duration. See the plan's "Upstream
reconciliation (2026-08)".
- **One dispatch path**: serialized execution submits to the pool and waits
rather than running inline, so the inflight registry is the single
mutual-exclusion authority. `RunnerSpec.serialize` is a per-task predicate,
because restart picks its mode from live cluster state.
- **`checkpoint(task, **params)`** — persist handler progress mid-handler, for a
destructive step that must not repeat after a crash. Doubles as the
cancellation probe before the next destructive step.
- **`RunnerSpec.on_cycle(cluster)`** — per-cluster upkeep attached to no task
(restart's orphaned-node watchdog).
- **`RunnerSpec.backoff(retry)`** — override the default curve where a runner
has a tuned one (restart's 1-minute lead-in).

## Gotchas

- **`tests/unit/tasks/test_retry_ceiling.py` no longer hangs** — upstream fixed
it before this rebase. It parametrizes over runners *discovered from source*
by the presence of `.retry += 1`, so a runner migrating to the driver silently
drops out of it. Each migration therefore also moves the runner into that
file's `_DRIVER_MIGRATED` set, which is asserted to really have handed the
retry counter over (`test_migrated_runners_delegate_retry`).
- Per-runner behaviour tests for migrated runners live in
**`tests/unit/tasks/test_runner_specs.py`** (one section per runner: handler
outcome vocabulary + eligibility + `on_finish`). Extend it as you migrate.
- Several migrations fix latent bugs (a failure path that suspended without ever
incrementing retry, so a declared `max_retry` could never bind). Each is called
out in its commit message — keep doing that rather than folding them in
silently.

## Verification

```bash
tox run-parallel -e lint,types
tox run -e unit -- tests/unit/tasks/ tests/unit/test_lvol_sync_op_task.py \
tests/unit/test_task_cancellation.py
tox run -e unit # full unit tier, ~45s, currently green
```

Do not run `tox run` (the integration tier is broken independently of this work).

## Before opening the PR

Delete these two handoff files (`HANDOFF.md`, `TASK_RUNNER_REWORK_PLAN.md`) —
they are transfer artifacts, not part of the change.
Loading
Loading