Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
102 commits
Select commit Hold shift + click to select a range
663f3bb
feat(transport): add HTTP retry logic with exponential backoff
jpnurmi Feb 12, 2026
52a273e
fix(retry): filter startup scan by timestamp, create cache dir
jpnurmi Feb 12, 2026
e230f8b
fix(retry): use wall clock time instead of monotonic time
jpnurmi Feb 12, 2026
3810f2b
ref(retry): define backoff base in seconds
jpnurmi Feb 12, 2026
a910f32
ref(retry): replace scan+free_paths with foreach callback API
jpnurmi Feb 12, 2026
8b8921b
feat(transport): set 15s request timeout for curl and winhttp
jpnurmi Feb 12, 2026
46d2a9e
fix(retry): avoid duplicate delayed retry tasks on startup
jpnurmi Feb 12, 2026
4f3d625
ref(retry): take options in sentry__retry_new, own path construction
jpnurmi Feb 12, 2026
199bd30
ref(retry): set startup_time at creation, remove setter
jpnurmi Feb 12, 2026
94a7ca7
fix(retry): return total file count so polling continues during backoff
jpnurmi Feb 12, 2026
011fbb5
fix(retry): use callback return value to track remaining retry files
jpnurmi Feb 12, 2026
f673193
ref(retry): rename constants to SENTRY_RETRY_INTERVAL and SENTRY_RETR…
jpnurmi Feb 12, 2026
caef6c1
ref(retry): encapsulate retry scheduling into the retry module
jpnurmi Feb 12, 2026
9c7c99d
ref(transport): remove unnecessary includes, restore blank line
jpnurmi Feb 12, 2026
3813feb
ref(retry): move precondition checks to callers
jpnurmi Feb 12, 2026
ea5233f
ref(transport): extract http_send_envelope helper
jpnurmi Feb 12, 2026
f17f8f9
test(retry): remove redundant retry_no_duplicate_rescan test
jpnurmi Feb 12, 2026
e7886a4
ref(curl): use CURLOPT_TIMEOUT_MS for consistency with winhttp and cr…
jpnurmi Feb 12, 2026
ce4ac80
ref(retry): unify startup and poll into a single task
jpnurmi Feb 12, 2026
34d5131
ref(retry): extract sentry__retry_make_path helper
jpnurmi Feb 12, 2026
8015d2c
fix(retry): prevent envelope duplication between retry and cache
jpnurmi Feb 12, 2026
8fcdb4f
ref(database): derive can_cache flag to skip cache dir creation early
jpnurmi Feb 12, 2026
551ad74
ref(retry): change send callback to envelope-based API
jpnurmi Feb 12, 2026
d2f1a07
test(retry): verify cache_keep preserves envelopes on successful send
jpnurmi Feb 12, 2026
3750856
fix(retry): use PRIu64 format specifier for uint64_t
jpnurmi Feb 12, 2026
0cbf407
fix(retry): guard against unsigned underflow in backoff check
jpnurmi Feb 12, 2026
3b2a5d6
fix(retry): prevent startup poll from re-processing same-session enve…
jpnurmi Feb 13, 2026
6608084
fix(retry): flush pending retries on shutdown
jpnurmi Feb 14, 2026
961ec95
ref(retry): use millisecond timestamps for retry filenames
jpnurmi Feb 14, 2026
558c32e
fix(retry): flush pending retries synchronously before shutdown
jpnurmi Feb 14, 2026
be1d7cf
fix(retry): stop retrying on network failure
jpnurmi Feb 14, 2026
fccc565
fix(retry): dump unsent envelopes to retry dir on shutdown timeout
jpnurmi Feb 14, 2026
d831987
test(retry): update expectations for stop-on-failure behavior
jpnurmi Feb 14, 2026
69f4f68
style(retry): fix line length in unit tests
jpnurmi Feb 14, 2026
5562feb
fix(retry): prevent duplicate envelope writes from detached worker
jpnurmi Feb 14, 2026
0664fb3
docs: add changelog entry for HTTP retry feature
jpnurmi Feb 14, 2026
c241adf
test(retry): use sentry__retry_send instead of duplicated eligibility…
jpnurmi Feb 14, 2026
a053ccb
fix(retry): raise backoff cap from 2h to 8h to match crashpad
jpnurmi Feb 14, 2026
71ee17a
refactor(retry): introduce retry_item_t to avoid re-parsing filenames
jpnurmi Feb 15, 2026
dfe7c1f
feat(retry): add debug and warning output for HTTP retries
jpnurmi Feb 15, 2026
5d80da6
refactor(cache): add cache_path to sentry_run_t and centralize cache …
jpnurmi Feb 15, 2026
06cca66
fix(transport): use connect-only timeouts for curl and winhttp
jpnurmi Feb 15, 2026
fd6c577
fix(retry): decrement total count when removing corrupt envelope files
jpnurmi Feb 15, 2026
173de80
fix(retry): only warn about exhausted retries on network failure
jpnurmi Feb 15, 2026
0482bf4
docs(retry): add doc comments to sentry_retry.h declarations
jpnurmi Feb 15, 2026
39f36ec
feat(transport): add sentry_transport_retry()
jpnurmi Feb 15, 2026
7b30688
refactor(retry): store retry envelopes in cache/ directory
jpnurmi Feb 15, 2026
e6c8db4
fix(retry): own cache_path to prevent use-after-free on detached thread
jpnurmi Feb 16, 2026
c520575
fix(retry): don't consume shutdown timeout with bgworker flush
jpnurmi Feb 16, 2026
79d1732
fix(retry): flush in-flight retries before shutdown
jpnurmi Feb 16, 2026
344dcc8
refactor(retry): replace http_retries count with boolean http_retry
jpnurmi Feb 16, 2026
5cdcc47
fix(transport): use explicit WinHTTP send/receive timeouts
jpnurmi Feb 16, 2026
9136500
fix(retry): deduplicate poll tasks on concurrent envelope failures
jpnurmi Feb 16, 2026
9b14dc2
fix(retry): set sealed flag before dumping queued envelopes
jpnurmi Feb 16, 2026
e75f595
fix(retry): prevent retry flush from consuming shutdown timeout
jpnurmi Feb 16, 2026
136fabf
fix(retry): zero-initialize retry struct after malloc
jpnurmi Feb 16, 2026
1a0d99b
fix(retry): skip flush task after seal to prevent duplicate sends
jpnurmi Feb 16, 2026
ed28b85
refactor(database): remove unused sentry__run_write_cache
jpnurmi Feb 16, 2026
62999c3
fix(retry): make trigger one-shot to prevent rapid retry exhaustion
jpnurmi Feb 17, 2026
70b1040
fix(core): check http_retry option instead of transport capability
jpnurmi Feb 17, 2026
794df4b
fix(retry): prevent UB from negative count in backoff shift
jpnurmi Feb 17, 2026
83475e4
fix(options): normalize http_retry with !! to match other boolean set…
jpnurmi Feb 17, 2026
8aae746
revert(database): restore original variable names and whitespace in w…
jpnurmi Feb 23, 2026
791d598
docs: clarify sentry_transport_retry behavior and limitations
jpnurmi Feb 24, 2026
a2efe25
docs(retry): document retry behavior for network failures vs HTTP res…
jpnurmi Feb 24, 2026
4b1e7d7
fix(retry): only clear startup_time when envelope is written
jpnurmi Feb 24, 2026
6064f1d
fix(retry): check for NULL from sentry__path_clone
jpnurmi Feb 24, 2026
45ee886
fix(retry): apply backoff when system clock moves backward
jpnurmi Feb 24, 2026
ec59d5e
fix(retry): increase SENTRY_RETRY_ATTEMPTS to 6 to match Crashpad
jpnurmi Feb 24, 2026
00bef01
fix(retry): avoid retry flush consuming entire shutdown timeout
jpnurmi Feb 24, 2026
d96beb1
fix(retry): warn on failed retry envelope rename
jpnurmi Feb 24, 2026
985b3da
fix(retry): check for NULL from sentry__path_clone in retry send
jpnurmi Feb 24, 2026
87532a3
fix(retry): fix data race on startup_time between threads
jpnurmi Feb 24, 2026
754a65d
fix(retry): clear retry_func when retry fails to initialize
jpnurmi Feb 24, 2026
0e64115
fix(retry): persist non-event envelopes to the retry cache
jpnurmi Feb 25, 2026
9ab3e11
fix(retry): close race between poll task and enqueue on scheduled flag
jpnurmi Feb 25, 2026
5a10044
refactor(database): add retry_count to write_envelope, add sentry__ru…
jpnurmi Feb 25, 2026
f35817d
refactor(database): make sentry_run_t refcounted
jpnurmi Feb 25, 2026
123a7be
refactor(cache): strip retry prefix in move_cache and simplify handle…
jpnurmi Feb 25, 2026
acfd148
fix(cache): use cache_name instead of src_name for UUID in move_cache
jpnurmi Feb 25, 2026
019e0e9
fix(retry): prevent duplicate cache writes during shutdown race
jpnurmi Feb 26, 2026
0e83c47
fix(cache): replace length heuristic with proper filename parsing in …
jpnurmi Feb 26, 2026
5f8e452
docs: fix retry count 5 → 6 in sentry_transport_retry docs
jpnurmi Feb 26, 2026
69ffc09
fix(retry): prevent poll task from re-arming after shutdown
jpnurmi Feb 26, 2026
e50dcf5
fix(winhttp): cancel in-flight request before shutdown to unblock worker
jpnurmi Feb 26, 2026
db19679
fix(winhttp): fix double-close race on client->request between cancel…
jpnurmi Feb 26, 2026
898308d
fix(winhttp): use on_timeout callback to unblock worker instead of ca…
jpnurmi Feb 26, 2026
7db3274
fix(winhttp): remove unnecessary local request snapshot in winhttp_se…
jpnurmi Feb 26, 2026
5b32779
fix(sync): don't force running=0 before on_timeout callback
jpnurmi Feb 26, 2026
9c8d803
fix(test): disable transport retry in unit tests to fix valgrind flak…
jpnurmi Feb 26, 2026
a91842a
fix(retry): clear sealed_envelope after match to prevent address-reus…
jpnurmi Feb 26, 2026
c17329a
ref: consolidate filename formatting into make_cache_path
jpnurmi Mar 4, 2026
3461f52
Adapt to sentry__session_new() change
jpnurmi Mar 9, 2026
36568c1
ref: cache envelopes only on failed HTTP send
jpnurmi Mar 10, 2026
1d85dd8
Clarify bgworker shutdown timeout comment
jpnurmi Mar 10, 2026
168a807
Cache envelopes only on failed HTTP send
jpnurmi Mar 11, 2026
c0f920d
Remove unused sentry__transport_can_retry
jpnurmi Mar 11, 2026
bb06d6f
fix: incref options in cleanup task to prevent use-after-free
jpnurmi Mar 11, 2026
8840444
Update sentry docs URL for network failure handling
jpnurmi Mar 17, 2026
93d884a
Replace pointer comparison with envelope tag in retry seal check
jpnurmi Mar 17, 2026
93db900
Add TODO for retry jitter and shorter poll interval
jpnurmi Mar 17, 2026
8f104d2
Initialize tag for raw envelopes loaded from disk
cursoragent Mar 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
# Changelog

## Unreleased:
## Unreleased

**Features**:

- Add HTTP retry with exponential backoff. ([#1520](https://github.com/getsentry/sentry-native/pull/1520))

**Fixes**:

Expand Down
7 changes: 7 additions & 0 deletions examples/example.c
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,9 @@ main(int argc, char **argv)
sentry_options_set_cache_max_age(options, 5 * 24 * 60 * 60); // 5 days
sentry_options_set_cache_max_items(options, 5);
}
if (has_arg(argc, argv, "no-http-retry")) {
sentry_options_set_http_retry(options, false);
}

if (has_arg(argc, argv, "enable-metrics")) {
sentry_options_set_enable_metrics(options, true);
Expand Down Expand Up @@ -940,6 +943,10 @@ main(int argc, char **argv)
sentry_reinstall_backend();
}

if (has_arg(argc, argv, "flush")) {
sentry_flush(10000);
}

if (has_arg(argc, argv, "sleep")) {
sleep_s(10);
}
Expand Down
41 changes: 36 additions & 5 deletions include/sentry.h
Original file line number Diff line number Diff line change
Expand Up @@ -946,6 +946,24 @@ SENTRY_API void sentry_transport_set_shutdown_func(
sentry_transport_t *transport,
int (*shutdown_func)(uint64_t timeout, void *state));

/**
* Retries sending all pending envelopes in the transport's retry queue,
* e.g. when coming back online. Only applicable for HTTP transports.
*
* Note: The SDK automatically retries failed envelopes on next application
* startup. This function allows manual triggering of pending retries at
* runtime. Each envelope is retried up to 6 times. If all attempts are
* exhausted during intermittent connectivity, events will be discarded
* (or moved to cache if enabled via sentry_options_set_cache_keep).
*
* Warning: This function has no rate limiting - it will immediately
* attempt to send all pending envelopes. Calling this repeatedly during
* extended network outages may exhaust retry attempts that might have
* succeeded with the SDK's built-in exponential backoff.
*/
SENTRY_EXPERIMENTAL_API void sentry_transport_retry(
sentry_transport_t *transport);

/**
* Generic way to free transport.
*/
Expand Down Expand Up @@ -1476,12 +1494,13 @@ SENTRY_API int sentry_options_get_symbolize_stacktraces(
const sentry_options_t *opts);

/**
* Enables or disables storing envelopes in a persistent cache.
* Enables or disables storing failed envelopes in a persistent cache.
*
* When enabled, envelopes that fail to send are written to a `cache/`
* subdirectory within the database directory. The cache is cleared on startup
* based on the cache_max_items, cache_max_size, and cache_max_age options.
*
* When enabled, envelopes are written to a `cache/` subdirectory within the
* database directory and retained regardless of send success or failure.
* The cache is cleared on startup based on the cache_max_items, cache_max_size,
* and cache_max_age options.
* Only applicable for HTTP transports.
*
* Disabled by default.
*/
Expand Down Expand Up @@ -2258,6 +2277,18 @@ SENTRY_EXPERIMENTAL_API void sentry_options_set_enable_logs(
SENTRY_EXPERIMENTAL_API int sentry_options_get_enable_logs(
const sentry_options_t *opts);

/**
* Enables or disables HTTP retry with exponential backoff for network failures.
*
* Only applicable for HTTP transports.
*
* Enabled by default.
*/
SENTRY_EXPERIMENTAL_API void sentry_options_set_http_retry(
sentry_options_t *opts, int enabled);
SENTRY_EXPERIMENTAL_API int sentry_options_get_http_retry(
const sentry_options_t *opts);

/**
* Enables or disables custom attributes parsing for structured logging.
*
Expand Down
2 changes: 2 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ sentry_target_sources_cwd(sentry
sentry_process.h
sentry_ratelimiter.c
sentry_ratelimiter.h
sentry_retry.c
sentry_retry.h
sentry_ringbuffer.c
sentry_ringbuffer.h
sentry_sampling_context.h
Expand Down
8 changes: 2 additions & 6 deletions src/backends/sentry_backend_crashpad.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -565,11 +565,9 @@ process_completed_reports(

SENTRY_DEBUGF("caching %zu completed reports", reports.size());

sentry_path_t *cache_dir
= sentry__path_join_str(options->database_path, "cache");
if (!cache_dir || sentry__path_create_dir_all(cache_dir) != 0) {
sentry_path_t *cache_dir = options->run->cache_path;
if (sentry__path_create_dir_all(cache_dir) != 0) {
SENTRY_WARN("failed to create cache dir");
sentry__path_free(cache_dir);
return;
}

Expand All @@ -593,8 +591,6 @@ process_completed_reports(
sentry__path_free(out_path);
sentry_envelope_free(envelope);
}

sentry__path_free(cache_dir);
}

static int
Expand Down
6 changes: 4 additions & 2 deletions src/sentry_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -292,8 +292,10 @@ sentry_init(sentry_options_t *options)
backend->prune_database_func(backend);
}

if (options->cache_keep) {
sentry__cleanup_cache(options);
if (options->cache_keep || options->http_retry) {
if (!sentry__transport_submit_cleanup(options->transport, options)) {
sentry__cleanup_cache(options);
}
}

if (options->auto_session_tracking) {
Expand Down
Loading
Loading