|
| 1 | +--- |
| 2 | +alwaysApply: true |
| 3 | +description: Java SDK Offline behaviour |
| 4 | +--- |
| 5 | +# Java SDK Offline behaviour |
| 6 | + |
| 7 | +By default offline caching is enabled for Android but disabled for JVM. |
| 8 | +It can be enabled by setting SentryOptions.cacheDirPath. |
| 9 | + |
| 10 | +For Android, AndroidEnvelopeCache is used. For JVM, if cache path has been configured, EnvelopeCache will be used. |
| 11 | + |
| 12 | +Any error, event, transaction, profile, replay etc. is turned into an envelope and then sent into ITransport.send. |
| 13 | +The default implementation is AsyncHttpTransport. |
| 14 | + |
| 15 | +If an envelope is dropped due to rate limit and has previously been cached (Cached hint) it will be discarded from the IEnvelopeCache. |
| 16 | + |
| 17 | +AsyncHttpTransport.send will enqueue an AsyncHttpTransport.EnvelopeSender task onto an executor. |
| 18 | + |
| 19 | +Any envelope that doesn't have the Cached hint will be stored in IEnvelopeCache by the EventSender task. Previously cached envelopes (Cached hint) will have a noop cache passed to AsyncHttpTransport.EnvelopeSender and thus not cache again. It is also possible cache is disabled in general. |
| 20 | + |
| 21 | +An envelope being sent directly from SDK API like Sentry.captureException will not have the Retryable hint. |
| 22 | + |
| 23 | +In case the SDK is offline, it'll mark the envelope to be retried if it has the Retryable hint. |
| 24 | +If the envelope is not retryable and hasn't been sent to offline cache, it's recorded as lost in a client report. |
| 25 | + |
| 26 | +In case the envelope can't be sent due to an error or network connection problems it'll be marked for retry if it has the Retryable hint. |
| 27 | +If it's not retryable and hasn't been cached, it's recorded as lost in a client report. |
| 28 | + |
| 29 | +In case the envelope is sent successfully, it'll be discarded from cache. |
| 30 | + |
| 31 | +The SDK has multiple mechanisms to deal with envelopes on disk. |
| 32 | +- OutboxSender: Sends events coming from other SDKs like NDK that wrote them to disk. |
| 33 | +- io.sentry.EnvelopeSender: This is the offline cache. |
| 34 | + |
| 35 | +Both of these are set up through an integration (SendCachedEnvelopeIntegration) which is configured to use SendFireAndForgetOutboxSender or SendFireAndForgetEnvelopeSender. |
| 36 | + |
| 37 | +io.sentry.EnvelopeSender is able to pick up files in the cache directory and send them. |
| 38 | +It will trigger sending envelopes in cache dir on init and when the connection status changes (e.g. the SDK comes back online, meaning it has Internet connection again). |
| 39 | + |
| 40 | +## When Envelope Files Are Removed From Cache |
| 41 | + |
| 42 | +Envelope files are removed from the cache directory in the following scenarios: |
| 43 | + |
| 44 | +### 1. Successful Send to Sentry Server |
| 45 | +When `AsyncHttpTransport` successfully sends an envelope to the Sentry server, it calls `envelopeCache.discard(envelope)` to remove the cached file. This happens in `AsyncHttpTransport.EnvelopeSender.flush()` when `result.isSuccess()` is true. |
| 46 | + |
| 47 | +### 2. Rate Limited Previously Cached Envelopes |
| 48 | +If an envelope is dropped due to rate limiting **and** has previously been cached (indicated by the `Cached` hint), it gets discarded immediately via `envelopeCache.discard(envelope)` in `AsyncHttpTransport.send()`. |
| 49 | +In this case the discarded envelope is recorded as lost in client reports. |
| 50 | + |
| 51 | +### 3. Offline Cache Processing (EnvelopeSender) |
| 52 | +When the SDK processes cached envelope files from disk (via `EnvelopeSender`), files are deleted after processing **unless** they are marked for retry. In `EnvelopeSender.processFile()`, the file is deleted with `safeDelete(file)` if `!retryable.isRetry()`. |
| 53 | + |
| 54 | +### 4. Session File Management |
| 55 | +Session-related files (session.json, previous_session.json) are removed during session lifecycle events like session start/end and abnormal exits. |
| 56 | + |
| 57 | +### 5. Cache rotation |
| 58 | +If the number of files in the cache directory has reached the configured limit (SentryOptions.maxCacheItems), the oldest file will be deleted to make room. |
| 59 | +This happens in `CacheStrategy.rotateCacheIfNeeded`. The deleted envelope will be recorded as lost in client reports. |
| 60 | + |
| 61 | +## Retry Mechanism |
| 62 | + |
| 63 | +**Important**: The SDK does NOT implement a traditional "max retry count" mechanism. Instead: |
| 64 | + |
| 65 | +### Infinite Retry Approach |
| 66 | +- **Retryable envelopes**: Stay in cache indefinitely and are retried when conditions improve (network connectivity restored, rate limits expire, etc.) |
| 67 | +- **Non-retryable envelopes**: If they fail to send, they're immediately recorded as lost (not cached for retry) |
| 68 | + |
| 69 | +### When Envelopes Are Permanently Lost (Not Due to Retry Limits) |
| 70 | + |
| 71 | +1. **Queue Overflow**: When the transport executor queue is full - recorded as `DiscardReason.QUEUE_OVERFLOW` |
| 72 | + |
| 73 | +2. **Network Errors (Non-Retryable)**: When an envelope isn't marked as retryable and fails due to network issues - recorded as `DiscardReason.NETWORK_ERROR` |
| 74 | + |
| 75 | +3. **Rate Limiting**: When envelope items are dropped due to active rate limits - recorded as `DiscardReason.RATELIMIT_BACKOFF` |
| 76 | + |
| 77 | +4. **Cache Overflow**: When the cache directory has reached maxCacheItems, old files are deleted - recorded as `DiscardReason.CACHE_OVERFLOW` |
| 78 | + |
| 79 | +### Cache Processing Triggers |
| 80 | +Cached envelopes are processed when: |
| 81 | +- Network connectivity is restored (via connection status observer) |
| 82 | +- SDK initialization occurs |
| 83 | +- Rate limits expire |
| 84 | +- Manual flush operations |
| 85 | + |
| 86 | +### File Deletion Implementation |
| 87 | +The actual file deletion is handled by `EnvelopeCache.discard()` which calls `envelopeFile.delete()` and logs errors if deletion fails. |
0 commit comments