Skip to content

Conversation

@jpnurmi
Copy link
Collaborator

@jpnurmi jpnurmi commented Jan 23, 2026

Summary

Adds offline caching support for the inproc and breakpad backends, allowing envelopes to be persisted locally for debugging and offline scenarios. When enabled, envelopes are retained in a cache/ subdirectory within the database path, regardless of whether the send succeeds or fails.

On startup, the cache is pruned based on the configured limits (max items, age, size), removing entries from oldest to newest until constraints are satisfied. The pruning algorithm is based on Crashpad's prune_crash_reports.cc.

API

New options:

  • sentry_options_set_cache_keep(opts, int enabled) - Enables/disables offline caching
  • sentry_options_set_cache_max_items(opts, size_t items) - Maximum number of cache entries (default: 30)
  • sentry_options_set_cache_max_size(opts, size_t bytes) - Maximum cache directory size (no default max size)
  • sentry_options_set_cache_max_age(opts, time_t seconds) - Maximum age for cache entries (no default max age)

Usage

sentry_options_set_cache_keep(options, true);
sentry_options_set_cache_max_items(options, 10);              // 10 items
sentry_options_set_cache_max_size(options, 8 * 1024 * 1024);  // 8 MB
sentry_options_set_cache_max_age(options, 30 * 24 * 60 * 60); // 30 days

Comparison

Option Native Cocoa/iOS Java/Android
Enable/disable cache_keep (default: off) Always on Always on
Max items cache_max_items (30) maxCacheItems (30) maxCacheItems (30)
Max size cache_max_size (0) - -
Max age cache_max_age (0) - (90 days, hardcoded) -

Limitations

Related

Test Plan

  • Unit tests for age-based and size-based cache pruning logic (test_cache.c)
  • Integration tests for end-to-end caching behavior (test_integration_cache.py)

Co-authored-by: @JoshuaMoelans
Closes: #1461

JoshuaMoelans and others added 16 commits January 23, 2026 14:21
Before "revert separate caching folder implementation"

#1461
Cast cache_max_age to time_t to avoid implicit signedness conversion
between uint64_t and time_t.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Add explicit static_cast<time_t>() for cache_max_age to fix
-Wsign-conversion warning on Windows with clang-cl.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@jpnurmi jpnurmi changed the title WIP: offline caching + tests for inproc & breakpad feat: offline caching (inproc & breakpad) Jan 26, 2026
@github-actions
Copy link

github-actions bot commented Jan 26, 2026

Messages
📖 Do not forget to update Sentry-docs with your feature once the pull request gets approved.

Generated by 🚫 dangerJS against 96a8cd7

jpnurmi and others added 3 commits January 27, 2026 13:57
For consistency with time-related operations throughout the codebase.

This adds a time.h dependency to the public header, but it's a
lightweight standard C header available since C89.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@jpnurmi jpnurmi marked this pull request as ready for review February 3, 2026 11:02
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
jpnurmi and others added 2 commits February 3, 2026 13:13
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Move size accumulation into else branch so age-pruned files don't count
toward size limit. Remove the size subtraction on prune which caused
bin-packing behavior (keeping older smaller files while removing newer
larger ones).

Add test for size-based pruning order.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
jpnurmi and others added 2 commits February 4, 2026 10:29
- Check CreateFileW return value before calling SetFileTime/CloseHandle
- Change TEST_CHECK to TEST_ASSERT since mtime setup is a precondition

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
cache_dir is NULL when cache_keep is false, and sentry__path_free
handles NULL safely.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

jpnurmi and others added 2 commits February 4, 2026 11:31
Crashpad's AgePruneCondition and DatabaseSizePruneCondition only accept
days and KB respectively, causing precision loss for sub-day ages and
sub-KB sizes. Replace them with MaxAgePruneCondition (seconds) and
MaxSizePruneCondition (bytes) that handle zero as "no limit" internally.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
jpnurmi and others added 6 commits February 4, 2026 16:50
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Re-order cache_max_items before max_size/max_age for visibility as the
most relevant default limit.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…disabled

When cache_keep is false, the crashpad database should be pruned with
the original upstream defaults (2 days, 8 MB) rather than the cache_max
options which default to 0 (disabled). This preserves the existing
pruning behavior for users who have not opted into offline caching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
data->db->CleanDatabase(60 * 60 * 24 * 2);
// large. When offline caching is enabled, the cache_max_* options are
// used instead.
time_t max_age = 2 * 24 * 60 * 60; // 2 days
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe extracting these constants somewhere would make sense (instead of keeping them as crashpad-only defaults in a function)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are just some legacy defaults that were used by the crashpad backend before offline caching was introduced. These defaults are specific to the crashpad backend and are not used anywhere else. The idea is to retain full backwards compatibility by using the old legacy defaults unless the user opts in for offline caching. I updated the comments - I hope it's clear now :)

jpnurmi and others added 3 commits February 9, 2026 12:37
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
jpnurmi and others added 2 commits February 9, 2026 14:41
Avoid short-circuit evaluation skipping stateful conditions by combining
MaxItemsPruneCondition, MaxSizePruneCondition, and MaxAgePruneCondition
into a single CachePruneCondition that evaluates all checks unconditionally.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@jpnurmi
Copy link
Collaborator Author

jpnurmi commented Feb 11, 2026

Hey @JoshuaMoelans, we decided to merge these two

Don't hesitate to drop comments when you're back, and I'll follow up if there's anything.

@jpnurmi jpnurmi merged commit b5c53dd into master Feb 11, 2026
45 checks passed
@jpnurmi jpnurmi deleted the jpnurmi/feat/offline-caching branch February 11, 2026 13:13
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.

3 participants