Skip to content

feat: retry failed batches with exponential backoff - #34

Merged
samber merged 4 commits into
feat/batch-and-flushfrom
feat/retry-failed-batches
Oct 13, 2026
Merged

samber merged 4 commits into
feat/batch-and-flushfrom
feat/retry-failed-batches

Conversation

@samber

@samber samber commented Oct 13, 2026 •

Copy link
Copy Markdown
Owner

Stacked on #33.

A batch hit by a network error, a 429 or a 5xx was lost on the first failure.

  • retry those with a doubling, jittered delay (100% to 150% of the backoff): MaxRetries (3, -1 disables), RetryBackoff (500ms), the doubling delay is capped at 30s before jitter
  • other 4xx and marshaling failures are not retried: they fail the same way every time
  • the last error is reported through OnError once retries are exhausted

A batch hit by a network error, a 429 or a 5xx was lost on the first
failure.

Retry those with a doubling delay (MaxRetries, RetryBackoff), capped at
30s per wait. Other 4xx responses and marshaling failures are not
retried: they fail the same way every time. The last error is reported
through OnError once retries are exhausted.
@codecov

codecov Bot commented Oct 13, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 88.88889% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.27%. Comparing base (eb7aa34) to head (ea165c5).
⚠️ Report is 1 commits behind head on feat/batch-and-flush.

Files with missing lines Patch % Lines
dispatcher.go 88.23% 1 Missing and 1 partial ⚠️
handler.go 89.47% 2 Missing ⚠️
Additional details and impacted files
@@                   Coverage Diff                    @@
##           feat/batch-and-flush      #34      +/-   ##
========================================================
- Coverage                 91.42%   91.27%   -0.15%     
========================================================
  Files                         3        3              
  Lines                       140      172      +32     
========================================================
+ Hits                        128      157      +29     
- Misses                        7        9       +2     
- Partials                      5        6       +1     
Flag Coverage Δ
unittests 91.27% <88.88%> (-0.15%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Handlers that failed together, for example during a server outage,
retried at the same instants.

Wait a random duration between 50% and 100% of the backoff.
Wait a random duration between 100% and 150% of the backoff, so the
configured RetryBackoff stays a lower bound.
@samber
samber marked this pull request as ready for review October 13, 2026 15:46
@samber
samber merged commit df10636 into feat/batch-and-flush Oct 13, 2026
12 of 14 checks passed
@samber
samber deleted the feat/retry-failed-batches branch October 13, 2026 15:46
samber added a commit that referenced this pull request Oct 13, 2026
* feat: retry failed batches with exponential backoff

A batch hit by a network error, a 429 or a 5xx was lost on the first
failure.

Retry those with a doubling delay (MaxRetries, RetryBackoff), capped at
30s per wait. Other 4xx responses and marshaling failures are not
retried: they fail the same way every time. The last error is reported
through OnError once retries are exhausted.

* feat: add jitter to retry backoff

Handlers that failed together, for example during a server outage,
retried at the same instants.

Wait a random duration between 50% and 100% of the backoff.

* feat: make retry jitter additive

Wait a random duration between 100% and 150% of the backoff, so the
configured RetryBackoff stays a lower bound.

* docs: clarify that the backoff cap applies before jitter
samber added a commit that referenced this pull request Oct 13, 2026
* feat: retry failed batches with exponential backoff

A batch hit by a network error, a 429 or a 5xx was lost on the first
failure.

Retry those with a doubling delay (MaxRetries, RetryBackoff), capped at
30s per wait. Other 4xx responses and marshaling failures are not
retried: they fail the same way every time. The last error is reported
through OnError once retries are exhausted.

* feat: add jitter to retry backoff

Handlers that failed together, for example during a server outage,
retried at the same instants.

Wait a random duration between 50% and 100% of the backoff.

* feat: make retry jitter additive

Wait a random duration between 100% and 150% of the backoff, so the
configured RetryBackoff stays a lower bound.

* docs: clarify that the backoff cap applies before jitter
samber added a commit that referenced this pull request Oct 13, 2026
* feat: retry failed batches with exponential backoff

A batch hit by a network error, a 429 or a 5xx was lost on the first
failure.

Retry those with a doubling delay (MaxRetries, RetryBackoff), capped at
30s per wait. Other 4xx responses and marshaling failures are not
retried: they fail the same way every time. The last error is reported
through OnError once retries are exhausted.

* feat: add jitter to retry backoff

Handlers that failed together, for example during a server outage,
retried at the same instants.

Wait a random duration between 50% and 100% of the backoff.

* feat: make retry jitter additive

Wait a random duration between 100% and 150% of the backoff, so the
configured RetryBackoff stays a lower bound.

* docs: clarify that the backoff cap applies before jitter
samber added a commit that referenced this pull request Oct 13, 2026
* feat: batch records and flush them on Close

Each record was sent in its own goroutine, so records logged right
before the process exited were lost and a log burst opened one request
per record.

Records now go through a bounded queue to a single worker that sends
batches (BatchSize, FlushInterval, QueueSize). Close delivers what is
still queued. When the queue is full, records are dropped and reported
through OnError, so logging never blocks the caller.

NewBetterstackHandler now returns *BetterstackHandler, which still
implements slog.Handler.

* feat: retry failed batches with exponential backoff (#34)

* feat: retry failed batches with exponential backoff

A batch hit by a network error, a 429 or a 5xx was lost on the first
failure.

Retry those with a doubling delay (MaxRetries, RetryBackoff), capped at
30s per wait. Other 4xx responses and marshaling failures are not
retried: they fail the same way every time. The last error is reported
through OnError once retries are exhausted.

* feat: add jitter to retry backoff

Handlers that failed together, for example during a server outage,
retried at the same instants.

Wait a random duration between 50% and 100% of the backoff.

* feat: make retry jitter additive

Wait a random duration between 100% and 150% of the backoff, so the
configured RetryBackoff stays a lower bound.

* docs: clarify that the backoff cap applies before jitter

* test: rename batch_test.go to dispatcher_test.go

Match the file it covers, dispatcher.go.

* test: close the handler in the default OnError test
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant