From d43345968574cdd360ab87877725b26804d91de9 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Wed, 18 Mar 2026 19:34:07 +0100 Subject: [PATCH 1/2] feat(native): offline caching The native daemon uses the same HTTP transport, so it gets offline caching for free as long as the cache_keep option is propagated via shared memory. Also, disable http_retry in the daemon since it is a single-shot process where retry backoff makes no sense. Co-Authored-By: Claude Opus 4.6 (1M context) --- CHANGELOG.md | 1 + src/backends/native/sentry_crash_context.h | 1 + src/backends/native/sentry_crash_daemon.c | 2 ++ src/backends/sentry_backend_native.c | 1 + tests/test_integration_native.py | 26 ++++++++++++++++++++++ 5 files changed, 31 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e23b571e5..e55ac9700 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ **Features**: - Add HTTP retry with exponential backoff. ([#1520](https://github.com/getsentry/sentry-native/pull/1520)) +- Add offline caching support to the new experimental `native` backend. ([#1585](https://github.com/getsentry/sentry-native/pull/1585)) **Fixes**: diff --git a/src/backends/native/sentry_crash_context.h b/src/backends/native/sentry_crash_context.h index f8b1a27e4..d45749949 100644 --- a/src/backends/native/sentry_crash_context.h +++ b/src/backends/native/sentry_crash_context.h @@ -260,6 +260,7 @@ typedef struct { int crash_reporting_mode; // sentry_crash_reporting_mode_t bool debug_enabled; // Debug logging enabled in parent process bool attach_screenshot; // Screenshot attachment enabled in parent process + bool cache_keep; // Platform-specific crash context #if defined(SENTRY_PLATFORM_LINUX) || defined(SENTRY_PLATFORM_ANDROID) diff --git a/src/backends/native/sentry_crash_daemon.c b/src/backends/native/sentry_crash_daemon.c index d45ed7bd0..f2458f58d 100644 --- a/src/backends/native/sentry_crash_daemon.c +++ b/src/backends/native/sentry_crash_daemon.c @@ -2966,6 +2966,8 @@ sentry__crash_daemon_main(pid_t app_pid, uint64_t app_tid, HANDLE event_handle, // Use debug logging and screenshot settings from parent process sentry_options_set_debug(options, ipc->shmem->debug_enabled); options->attach_screenshot = ipc->shmem->attach_screenshot; + options->cache_keep = ipc->shmem->cache_keep; + options->http_retry = false; // Set custom logger that writes to file if (log_file) { diff --git a/src/backends/sentry_backend_native.c b/src/backends/sentry_backend_native.c index 1f13c1c18..d0dc8a5bd 100644 --- a/src/backends/sentry_backend_native.c +++ b/src/backends/sentry_backend_native.c @@ -176,6 +176,7 @@ native_backend_startup( // Pass debug logging setting to daemon ctx->debug_enabled = options->debug; ctx->attach_screenshot = options->attach_screenshot; + ctx->cache_keep = options->cache_keep; // Set up event and breadcrumb paths sentry_path_t *run_path = options->run->run_path; diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 065c4b359..61f4b0dc0 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -626,3 +626,29 @@ def test_crash_mode_native_with_minidump(cmake, httpserver): # Should have debug_meta assert "debug_meta" in event + + +@pytest.mark.parametrize("cache_keep", [True, False]) +def test_native_cache_keep(cmake, cache_keep): + tmp_path = cmake(["sentry_example"], {"SENTRY_BACKEND": "native"}) + db_dir = tmp_path / ".sentry-native" + cache_dir = db_dir / "cache" + unreachable_dsn = "http://uiaeosnrtdy@127.0.0.1:19999/123456" + env = dict(os.environ, SENTRY_DSN=unreachable_dsn) + + # crash -> daemon sends via HTTP -> unreachable -> cache + run_crash( + tmp_path, + "sentry_example", + ["log", "stdout", "crash"] + (["cache-keep"] if cache_keep else []), + env=env, + ) + + # Wait for crash to be processed + time.sleep(2) + + cache_files = list(cache_dir.glob("*.envelope")) + if cache_keep: + assert len(cache_files) == 1 + else: + assert len(cache_files) == 0 From 01bb4652eb75a3d32f54d98e2d116084272f59f2 Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Thu, 19 Mar 2026 12:45:07 +0100 Subject: [PATCH 2/2] Fix flaky test_native_cache_keep by polling for cache file Replace fixed 2s sleep with wait_for_file polling (up to 10s) for the cache_keep=True case. On slow CI machines the daemon may not finish writing the cache file within 2 seconds. Co-Authored-By: Claude Opus 4.6 (1M context) --- tests/assertions.py | 3 ++- tests/test_integration_native.py | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/assertions.py b/tests/assertions.py index 8e9c1169e..6af26af76 100644 --- a/tests/assertions.py +++ b/tests/assertions.py @@ -559,11 +559,12 @@ def assert_failed_proxy_auth_request(stdout): def wait_for_file(path, timeout=10.0, poll_interval=0.1): + import glob import time deadline = time.time() + timeout while time.time() < deadline: - if path.exists(): + if glob.glob(str(path)): return True time.sleep(poll_interval) return False diff --git a/tests/test_integration_native.py b/tests/test_integration_native.py index 61f4b0dc0..cc0cd58b8 100644 --- a/tests/test_integration_native.py +++ b/tests/test_integration_native.py @@ -19,6 +19,7 @@ from .assertions import ( assert_meta, assert_session, + wait_for_file, ) from .conditions import has_native, is_kcov, is_asan @@ -644,11 +645,9 @@ def test_native_cache_keep(cmake, cache_keep): env=env, ) - # Wait for crash to be processed - time.sleep(2) - - cache_files = list(cache_dir.glob("*.envelope")) if cache_keep: - assert len(cache_files) == 1 + assert wait_for_file(cache_dir / "*.envelope") + assert len(list(cache_dir.glob("*.envelope"))) == 1 else: - assert len(cache_files) == 0 + time.sleep(2) + assert len(list(cache_dir.glob("*.envelope"))) == 0