Store a target's number of delivery attempts, at least one (closes #540)
check / check (push) Waiting to run

Target.MaxRetries (max_retries) becomes Target.MaxAttempts
(max_attempts), and the form field, parser and ceiling are renamed to
match. The column is NOT NULL and a check constraint refuses a value
below 1, so every target, whatever its type, is stored with at least
1. 1 is a single attempt with no circuit breaker; above 1 retries
behind the breaker up to that many attempts. Both target forms refuse
0 and less; a blank field on the add target form gives 1, and on the
edit form keeps the stored value. The target page shows the plain
number, and the README's two paragraphs on 0 against 1 become one
sentence.

Model: opus-5-5
This commit was merged in pull request #547.
This commit is contained in:
2026-10-15 19:38:14 +02:00
parent 147d58dd96
commit 40e8ecaab4
51 changed files with 1027 additions and 820 deletions
+12 -12
View File
@@ -53,10 +53,10 @@ type httpCore struct {
}
// deliver runs one delivery attempt through the retry core.
// A maxRetries of 0 is fire-and-forget: a single attempt is
// recorded and no circuit breaker is consulted. A positive
// maxRetries gates the attempt on the circuit breaker and
// schedules a backed-off retry on failure.
// A maxAttempts of 1 is a single attempt, recorded without
// consulting a circuit breaker. A higher maxAttempts gates the
// attempt on the circuit breaker and schedules a backed-off retry
// on failure, until maxAttempts attempts have been made.
//
// ctx is the context attempt sends its request with. Stopping the
// engine cancels it; see interruptedByStop.
@@ -66,17 +66,17 @@ func (c *httpCore) deliver(
d *database.Delivery,
task *Task,
sched Scheduler,
maxRetries int,
maxAttempts int,
attempt func() attemptResult,
) {
if maxRetries == 0 {
if maxAttempts == 1 {
c.fireAndForget(ctx, webhookDB, d, attempt())
return
}
c.withRetry(
ctx, webhookDB, d, task, sched, maxRetries, attempt,
ctx, webhookDB, d, task, sched, maxAttempts, attempt,
)
}
@@ -153,7 +153,7 @@ func (c *httpCore) withRetry(
d *database.Delivery,
task *Task,
sched Scheduler,
maxRetries int,
maxAttempts int,
attempt func() attemptResult,
) {
cb := c.getCircuitBreaker(task.TargetID)
@@ -205,7 +205,7 @@ func (c *httpCore) withRetry(
cb.RecordFailure()
c.handleRetry(
webhookDB, d, task, sched, maxRetries, attemptNum,
webhookDB, d, task, sched, maxAttempts, attemptNum,
)
}
@@ -266,10 +266,10 @@ func (c *httpCore) handleRetry(
d *database.Delivery,
task *Task,
sched Scheduler,
maxRetries int,
maxAttempts int,
attemptNum int,
) {
if attemptNum >= maxRetries {
if attemptNum >= maxAttempts {
c.eng.settleStatus(
webhookDB, d, d.Target.Type,
database.DeliveryStatusFailed,
@@ -439,7 +439,7 @@ func (t *httpTarget) Deliver(
t.deliver(
ctx, webhookDB, d, task, sched,
d.Target.MaxRetries, attempt,
d.Target.MaxAttempts, attempt,
)
}