Add Cloudflare email and collaboration workflows
This commit is contained in:
@@ -3,6 +3,7 @@ package email
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
@@ -211,17 +212,28 @@ func (q *Queue) deliver(ctx context.Context, row QueueRow) error {
|
||||
}
|
||||
|
||||
// Failure: retry or give up.
|
||||
if row.Attempts >= row.MaxAttempts {
|
||||
var permanent *PermanentError
|
||||
if errors.As(err, &permanent) {
|
||||
if _, ferr := q.db.ExecContext(ctx, `
|
||||
UPDATE email_queue SET status = 'failed', last_error = ? WHERE id = ?
|
||||
`, truncateErr(err.Error()), row.ID); ferr != nil {
|
||||
return fmt.Errorf("mark permanent failure: %w", ferr)
|
||||
}
|
||||
q.logger.Warn("email permanently rejected", "id", row.ID, "attempts", row.Attempts+1, "error", err)
|
||||
return nil
|
||||
}
|
||||
currentAttempt := row.Attempts + 1
|
||||
if currentAttempt >= row.MaxAttempts {
|
||||
if _, ferr := q.db.ExecContext(ctx, `
|
||||
UPDATE email_queue SET status = 'failed', last_error = ? WHERE id = ?
|
||||
`, truncateErr(err.Error()), row.ID); ferr != nil {
|
||||
return fmt.Errorf("mark failed: %w", ferr)
|
||||
}
|
||||
q.logger.Warn("email permanently failed", "id", row.ID, "attempts", row.Attempts, "error", err)
|
||||
q.logger.Warn("email permanently failed", "id", row.ID, "attempts", currentAttempt, "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
backoff := q.backoff(row.Attempts)
|
||||
backoff := q.backoff(currentAttempt)
|
||||
next := time.Now().UTC().Add(backoff)
|
||||
if _, err := q.db.ExecContext(ctx, `
|
||||
UPDATE email_queue SET status = 'pending', next_attempt_at = ?, last_error = ? WHERE id = ?
|
||||
@@ -229,9 +241,9 @@ func (q *Queue) deliver(ctx context.Context, row QueueRow) error {
|
||||
return fmt.Errorf("schedule retry: %w", err)
|
||||
}
|
||||
|
||||
// TemporaryError (Postmark 5xx) is retried with the same backoff as
|
||||
// other errors; the max-attempts cap still bounds total work.
|
||||
q.logger.Info("email retry scheduled", "id", row.ID, "attempt", row.Attempts, "backoff", backoff, "error", err)
|
||||
// Temporary provider failures and unclassified sender failures are retried;
|
||||
// PermanentError is handled above without wasting additional attempts.
|
||||
q.logger.Info("email retry scheduled", "id", row.ID, "attempt", currentAttempt, "backoff", backoff, "error", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user