ReLab

Break it. Watch it recover.

← all runs

Run

Status
SUCCEEDED
Workflow
effectful v1
Run id
96ea5bfd-1536-42c2-ac1b-72b7101efeb7
Seed
1337
Scenario
worker-crash-after-effect
Duration
1.89s

This run was disrupted and finished anyway

1 worker holding work for this run went away. 1 lease expired and the work came back through the reaper. 1 side effect already performed was suppressed by the idempotency ledger, so the retry did not repeat work that had already happened. All of it is in the 23 events below.

What happened

The milestones of this run, in the order they were recorded. Each one is a real event; the full sequence is in the timeline below.

  1. Run started#6 23:31:09.439
    RUN_STARTED

    A worker picked up the first task in the run.

  2. Failure injected on purpose charge#11 23:31:09.648
    FAULT_INJECTED

    ReLab degraded the real system here. This is the break, and everything after it is the recovery.

  3. Worker declared gone#12 23:31:11.020
    WORKER_LOST

    The holder of this run's task is gone and its leases are released. Usually that is five missed heartbeats; a worker that shuts down deliberately while holding work writes the same event, because what the run experienced is the same either way.

  4. Worker stopped responding charge#13 23:31:11.220
    TASK_LEASE_EXPIRED

    Nobody renewed the hold on this task, so another process concluded the holder is gone. This is the mechanism that also works when a machine loses power.

  5. Task returned to the queue charge#14 23:31:11.220
    TASK_REQUEUED

    The work a vanished worker was holding became claimable again.

  6. Task running charge#16 23:31:11.255
    TASK_STARTED

    The handler began executing. Each attempt writes one of these.

  7. Duplicate effect prevented charge#17 23:31:11.257
    SIDE_EFFECT_SKIPPED

    The retry asked to perform an effect already recorded under the same key, so it was not performed a second time.

  8. Task finished charge#18 23:31:11.259
    TASK_SUCCEEDED

    The handler returned without an error and the result was recorded.

  9. Workflow completed#23 23:31:11.299
    RUN_SUCCEEDED

    Every task finished. This event is the run's last: a finished run's story cannot change.

What the journal proves

4
Attempts
3
Workers involved
1
Faults injected
1
Leases expired
1
Tasks requeued
0
Retries scheduled
1
Workers lost
1
Effects suppressed
0
Dead-lettered
1.65s
Recovery

Every number above is a count of events in this run’s journal. Recovery is measured from the first fault, lease expiry, task failure or lost worker to the run completing, the same interval relab test asserts on.

Tasks

Each task in the run, with the attempt it reached and the worker that last held it.
StatusTaskAttemptWorkerError
SUCCEEDEDcharge2/3611a59e4·
SUCCEEDEDimport1/3611a59e4·
SUCCEEDEDreport1/3305c1862·

Timeline

The run’s complete recorded history, in sequence order. This is the same journal relab replay reduces. Filtering hides rows from this view; it never changes the sequence numbers, so a gap in them is still a gap.

16 of 23 recorded events, oldest first.
SeqTimeWhat happenedTaskWorkerTechnical detail
223:31:09.411Task ready to runTASK_SCHEDULEDimport·
attempt=0

The task's dependencies are satisfied, so it entered the queue.

Event
TASK_SCHEDULED
Sequence
2
Task
import
Worker
·
Occurred at
2026-09-02T23:31:09.411212Z
{
  "v": 1,
  "attempt": 0,
  "scheduled_at": "2026-09-02T23:31:09.411212807Z"
}
423:31:09.431Task claimed by a workerTASK_LEASEDimport611a59e4
attempt=1

One worker holds the task for a bounded time and renews that hold while it works. The hold is on this attempt: if it expires while that worker is still running, a second worker may take the task under a new attempt number.

Event
TASK_LEASED
Sequence
4
Task
import
Worker
611a59e4-70e0-4980-a80c-c4e4373cb4b4
Occurred at
2026-09-02T23:31:09.431167Z
{
  "v": 1,
  "attempt": 1,
  "lease_expires_at": "2026-09-02T23:31:11.431167845Z"
}
523:31:09.439Task runningTASK_STARTEDimport611a59e4
attempt=1

The handler began executing. Each attempt writes one of these.

Event
TASK_STARTED
Sequence
5
Task
import
Worker
611a59e4-70e0-4980-a80c-c4e4373cb4b4
Occurred at
2026-09-02T23:31:09.439943Z
{
  "v": 1,
  "attempt": 1,
  "handler": "import_csv"
}
723:31:09.443Task finishedTASK_SUCCEEDEDimport611a59e4
attempt=1 duration_ms=0

The handler returned without an error and the result was recorded.

Event
TASK_SUCCEEDED
Sequence
7
Task
import
Worker
611a59e4-70e0-4980-a80c-c4e4373cb4b4
Occurred at
2026-09-02T23:31:09.44316Z
{
  "v": 1,
  "output": {
    "rows": 1000
  },
  "attempt": 1,
  "artifacts": [
    {
      "name": "imported.csv",
      "size": 9,
      "sha256": "6eb31300aed462386f9f52f25d0155b3ca1a9ebce4c811950777282d69a81c64",
      "content_type": "text/csv"
    }
  ],
  "duration_ms": 0
}
823:31:09.443Task ready to runTASK_SCHEDULEDcharge·
attempt=0

The task's dependencies are satisfied, so it entered the queue.

Event
TASK_SCHEDULED
Sequence
8
Task
charge
Worker
·
Occurred at
2026-09-02T23:31:09.44316Z
{
  "v": 1,
  "attempt": 0,
  "depends_on": [
    "import"
  ],
  "scheduled_at": "2026-09-02T23:31:09.443160569Z"
}
923:31:09.634Task claimed by a workerTASK_LEASEDchargeec633076
attempt=1

One worker holds the task for a bounded time and renews that hold while it works. The hold is on this attempt: if it expires while that worker is still running, a second worker may take the task under a new attempt number.

Event
TASK_LEASED
Sequence
9
Task
charge
Worker
ec633076-8f0b-400c-b7e8-dc38b38bb823
Occurred at
2026-09-02T23:31:09.634095Z
{
  "v": 1,
  "attempt": 1,
  "lease_expires_at": "2026-09-02T23:31:11.634095745Z"
}
1023:31:09.643Task runningTASK_STARTEDchargeec633076
attempt=1

The handler began executing. Each attempt writes one of these.

Event
TASK_STARTED
Sequence
10
Task
charge
Worker
ec633076-8f0b-400c-b7e8-dc38b38bb823
Occurred at
2026-09-02T23:31:09.643824Z
{
  "v": 1,
  "attempt": 1,
  "handler": "charge_once"
}
1323:31:11.220Worker stopped respondingTASK_LEASE_EXPIREDchargeec633076
attempt=1

Nobody renewed the hold on this task, so another process concluded the holder is gone. This is the mechanism that also works when a machine loses power.

Event
TASK_LEASE_EXPIRED
Sequence
13
Task
charge
Worker
ec633076-8f0b-400c-b7e8-dc38b38bb823
Occurred at
2026-09-02T23:31:11.220414Z
{
  "v": 1,
  "attempt": 1,
  "lease_expired_at": "2026-09-02T23:31:11.020985Z"
}
1423:31:11.220Task returned to the queueTASK_REQUEUEDcharge·
attempt=1 next_attempt=2 reason=lease expired

The work a vanished worker was holding became claimable again.

Event
TASK_REQUEUED
Sequence
14
Task
charge
Worker
·
Occurred at
2026-09-02T23:31:11.220414Z
{
  "v": 1,
  "reason": "lease expired",
  "attempt": 1,
  "next_attempt": 2
}
1523:31:11.251Task claimed by a workerTASK_LEASEDcharge611a59e4
attempt=2

One worker holds the task for a bounded time and renews that hold while it works. The hold is on this attempt: if it expires while that worker is still running, a second worker may take the task under a new attempt number.

Event
TASK_LEASED
Sequence
15
Task
charge
Worker
611a59e4-70e0-4980-a80c-c4e4373cb4b4
Occurred at
2026-09-02T23:31:11.251555Z
{
  "v": 1,
  "attempt": 2,
  "lease_expires_at": "2026-09-02T23:31:13.251555072Z"
}
1623:31:11.255Task runningTASK_STARTEDcharge611a59e4
attempt=2

The handler began executing. Each attempt writes one of these.

Event
TASK_STARTED
Sequence
16
Task
charge
Worker
611a59e4-70e0-4980-a80c-c4e4373cb4b4
Occurred at
2026-09-02T23:31:11.255466Z
{
  "v": 1,
  "attempt": 2,
  "handler": "charge_once"
}
1823:31:11.259Task finishedTASK_SUCCEEDEDcharge611a59e4
attempt=2 duration_ms=2

The handler returned without an error and the result was recorded.

Event
TASK_SUCCEEDED
Sequence
18
Task
charge
Worker
611a59e4-70e0-4980-a80c-c4e4373cb4b4
Occurred at
2026-09-02T23:31:11.259463Z
{
  "v": 1,
  "output": {
    "effect": {
      "charged": true,
      "by_attempt": 1
    },
    "attempt": 2
  },
  "attempt": 2,
  "duration_ms": 2
}
1923:31:11.259Task ready to runTASK_SCHEDULEDreport·
attempt=0

The task's dependencies are satisfied, so it entered the queue.

Event
TASK_SCHEDULED
Sequence
19
Task
report
Worker
·
Occurred at
2026-09-02T23:31:11.259463Z
{
  "v": 1,
  "attempt": 0,
  "depends_on": [
    "charge"
  ],
  "scheduled_at": "2026-09-02T23:31:11.25946313Z"
}
2023:31:11.285Task claimed by a workerTASK_LEASEDreport305c1862
attempt=1

One worker holds the task for a bounded time and renews that hold while it works. The hold is on this attempt: if it expires while that worker is still running, a second worker may take the task under a new attempt number.

Event
TASK_LEASED
Sequence
20
Task
report
Worker
305c1862-7cf6-4fc1-904e-78c187ce1342
Occurred at
2026-09-02T23:31:11.285046Z
{
  "v": 1,
  "attempt": 1,
  "lease_expires_at": "2026-09-02T23:31:13.28504691Z"
}
2123:31:11.295Task runningTASK_STARTEDreport305c1862
attempt=1

The handler began executing. Each attempt writes one of these.

Event
TASK_STARTED
Sequence
21
Task
report
Worker
305c1862-7cf6-4fc1-904e-78c187ce1342
Occurred at
2026-09-02T23:31:11.295679Z
{
  "v": 1,
  "attempt": 1,
  "handler": "summarize"
}
2223:31:11.299Task finishedTASK_SUCCEEDEDreport305c1862
attempt=1 duration_ms=0

The handler returned without an error and the result was recorded.

Event
TASK_SUCCEEDED
Sequence
22
Task
report
Worker
305c1862-7cf6-4fc1-904e-78c187ce1342
Occurred at
2026-09-02T23:31:11.299163Z
{
  "v": 1,
  "output": {
    "inputs": 1
  },
  "attempt": 1,
  "artifacts": [
    {
      "name": "summary.json",
      "size": 65,
      "sha256": "6daf054bc88db6a09bffab080e3e33c0d9b070208a7517141a96c34ebc8938e3",
      "content_type": "application/json"
    }
  ],
  "duration_ms": 0
}
RECORDINGfive real runs exported from real PostgreSQL by scripts/record-demo.sh, crashes delivered by SIGKILLrelab 241c136-dirty, recorded