@@ -282,6 +282,83 @@ interval, and current interval. The polling-only warning is also emitted as
282282adapters should return ` true ` for a notification and ` false ` for a timeout; an
283283older adapter that returns ` nil ` remains compatible and keeps the fast cadence.
284284
285+ ## Dead letters, retry, and redrive
286+
287+ A message that exhausts its attempts becomes a dead letter. An effect or a
288+ broadcast that exhausts its attempts stays in its own table with
289+ ` status = 'dead' ` . All three are read and retried through one receiver, which
290+ carries the kind:
291+
292+ ``` ruby
293+ SolidObjects .dead_letters.all(authorization_context: current_admin)
294+ SolidObjects .dead_letters.retry(dead_letter_id, authorization_context: current_admin)
295+
296+ SolidObjects .dead_letters.effects.all(authorization_context: current_admin)
297+ SolidObjects .dead_letters.effects.retry(effect_id, authorization_context: current_admin)
298+ SolidObjects .dead_letters.broadcasts.retry(broadcast_id, authorization_context: current_admin)
299+ ```
300+
301+ An effect or broadcast retry returns the row to pending with a zero attempt
302+ count, no claim, and immediate availability. It keeps the stable id, so a
303+ handler that deduplicates on ` effect_id ` still sees the same key. An effect is
304+ at-least-once by contract, so a retried effect can run twice.
305+
306+ Retry acts only on a dead row. A row that is pending, processing, or completed
307+ comes back unchanged, so pressing a button twice cannot double-enqueue and
308+ cannot take a row away from a worker that holds it.
309+
310+ An incident produces dead rows in the hundreds, so a scope also answers
311+ ` redrive ` :
312+
313+ ``` ruby
314+ task = SolidObjects .dead_letters.effects.redrive(
315+ actor_type: " payments" ,
316+ failed_after: 6 .hours.ago,
317+ limit: 5_000 ,
318+ authorization_context: current_admin
319+ )
320+
321+ task.id # => "redrive_..."
322+ task.status # => "running"
323+ task.moved # => 412
324+ task.remaining # => 4_588
325+
326+ task.cancel(authorization_context: current_admin)
327+ ```
328+
329+ ` redrive ` returns at once. The task is durable, and the supervisor advances one
330+ bounded batch per pass, so a redrive of thousands of rows never holds a
331+ transaction longer than one batch. ` redrive_batch_size ` defaults to 100 and
332+ ` redrive_batch_pause ` to 0.05 seconds.
333+
334+ A redrive is idempotent over its scope and its filters. Starting the same one
335+ while it runs returns the running task rather than a second one, which a
336+ dashboard button an operator can press twice needs. A different scope or a
337+ different filter starts its own task, and the same scope can be redriven again
338+ once the first task finishes.
339+
340+ Read tasks back with ` SolidObjects.redrives ` :
341+
342+ ``` ruby
343+ SolidObjects .redrives.find(task.id, authorization_context: current_admin)
344+ SolidObjects .redrives.all(status: :running , authorization_context: current_admin)
345+ ```
346+
347+ A running task reports what is left to move rather than a stored estimate,
348+ because rows die and are retried while it runs.
349+
350+ Retry, redrive, and cancel each go through ` authorize_administration ` under
351+ their own resource name: ` dead_letters ` , ` effect_dead_letters ` ,
352+ ` broadcast_dead_letters ` , and ` redrives ` . Every retry and every task transition
353+ writes one row to ` solid_objects_administration_events ` , holding the action, the
354+ kind, the subject, the identity, and when it happened. The identity comes from
355+ ` administration_identity ` , which receives the authorization context the caller
356+ passed and defaults to its ` to_s ` . A refused caller writes nothing.
357+
358+ Automatic redrive on a schedule is deliberately absent. A dead row means a
359+ person decided something, and these APIs give that person an alternative to an
360+ ` UPDATE ` against a runtime table.
361+
285362## Graceful shutdown
286363
287364The supervisor requests shutdown, stops new claims, lets active loops return,
0 commit comments