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
Copy file name to clipboardExpand all lines: docs/failure-modes.md
+14Lines changed: 14 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -27,6 +27,20 @@ For the concepts behind these scenarios, see [concepts.md](./concepts.md). For t
27
27
28
28
---
29
29
30
+
## Upload retry strategy
31
+
32
+
A failed fragment upload is always retried; the fragment is never abandoned, because dropping it would advance the manifest over a range that is not durable in S3 and leave a silent hole (issue #206). How the retry is paced depends on the error class.
33
+
34
+
**Transient errors** (throttling, 5xx, timeouts, connection errors) are expected to clear quickly. The first retry is immediate, preserving responsiveness for a one-off blip. Successive consecutive failures of the same fragment back off exponentially from `stream_s3.task_retry_delay_constant` (10ms) by a factor of `stream_s3.task_retry_delay_exponent` (2), capped at `stream_s3.task_retry_delay_max_ms` (5s).
35
+
36
+
**Non-transient errors** (a confirmed checksum mismatch, an unexpected 4xx) are unlikely to clear on a tight retry, so the pipeline stalls at the failed offset and retries with a backoff starting at `upload_retry_delay_ms` (1000ms), growing to `upload_retry_delay_max_ms` (30s). Local-tier cleanup also stalls at that offset, so the only durable copy is retained until the upload succeeds.
37
+
38
+
Both profiles apply equal jitter (the delay is uniformly distributed in `[delay/2, delay]`) so that many streams stalled by a shared incident do not retry against S3 in lockstep. The per-fragment attempt counter resets once the fragment is durable.
39
+
40
+
**Detection.**`rate(rabbitmq_stream_s3_transfer_retries[5m])` rising means uploads are not succeeding on the first try. `rate(rabbitmq_stream_s3_nontransient_transfer_retries[5m]) > 0` or `rabbitmq_stream_s3_upload_stalled_offset > 0` means a fragment is failing with a confirmed-fatal error and the pipeline is wedged at that offset; the accompanying WARNING log names the error and offset.
41
+
42
+
---
43
+
30
44
## Leader election
31
45
32
46
**Trigger.** Writer node fails or is shut down. A new writer is elected.
|`rabbitmq_stream_s3_transfers_completed`| counter | Fragment uploads that succeeded |
165
165
|`rabbitmq_stream_s3_transfers_failed`| counter | Fragment uploads that failed for any reason |
166
+
|`rabbitmq_stream_s3_transfer_retries`| counter | Fragment upload retries scheduled (transient and non-transient errors) |
167
+
|`rabbitmq_stream_s3_nontransient_transfer_retries`| counter | Fragment upload retries for a confirmed-but-non-transient error (a checksum mismatch, an unexpected 4xx); the pipeline stalls at that offset until the fragment is durable |
166
168
|`rabbitmq_stream_s3_bytes_transferred`| counter | Total payload bytes uploaded |
167
169
|`rabbitmq_stream_s3_groups_created`| counter | Group manifest objects uploaded |
|`rabbitmq_stream_s3_transfers_in_flight`| gauge | Fragments cut and submitted that have not yet completed |
174
+
|`rabbitmq_stream_s3_upload_stalled_offset`| gauge | Offset at which the pipeline is stalled on a non-transient error, or 0 when not stalled |
172
175
173
176
#### Pipeline (drain to persist)
174
177
@@ -372,6 +375,7 @@ The numbers below are starting points. Tune for your traffic patterns.
372
375
-**Persistent transfer failures.**`rate(rabbitmq_stream_s3_transfers_failed[5m]) > 0` for more than ten minutes. Sustained failures usually mean S3 connectivity, throttling, or credentials problems.
373
376
-**Persist conflicts.**`rate(rabbitmq_stream_s3_persist_conflicts[5m]) > 0`. A non-zero rate indicates competing writers, likely from a partition or a struggling leader election.
374
377
-**Stuck uploads.**`rabbitmq_stream_s3_transfers_in_flight` non-zero and `rate(rabbitmq_stream_s3_transfers_completed[5m]) == 0` together. Drain loop is producing fragments but the governor is not flushing them.
378
+
-**Stalled pipeline (non-transient error).**`rabbitmq_stream_s3_upload_stalled_offset > 0`, or `rate(rabbitmq_stream_s3_nontransient_transfer_retries[5m]) > 0`. A fragment is failing with a confirmed-fatal error (for example a persistent checksum mismatch or an unexpected 4xx) and the pipeline is stuck at that offset, retrying with a backoff. Inspect the accompanying WARNING log for the error and offset.
375
379
-**Local log ahead recoveries.**`rate(rabbitmq_stream_s3_local_log_ahead_recoveries[1h]) > 0` for any stream. Indicates local retention is racing ahead of uploads. See [failure-modes.md](./failure-modes.md).
376
380
-**Khepri conflicts.**`rate(rabbitmq_stream_s3_put_conflicts[5m]) > 0` matches `rabbitmq_stream_s3_persist_conflicts` above and surfaces the same problem from the metadata store side.
377
381
-**S3 errors.**`rate(rabbitmq_stream_s3_response_500[5m]) > 0` or `rate(rabbitmq_stream_s3_response_503[5m]) > 0`. The plugin retries automatically; sustained high rates indicate an S3-side incident.
0 commit comments