Skip to content

Commit 6fc8adc

Browse files
authored
Add rules file for documenting SDK offline behaviour (#4572)
#skip-changelog ## 📜 Description <!--- Describe your changes in detail --> Add rules file for documenting SDK offline behaviour ## 💡 Motivation and Context <!--- Why is this change required? What problem does it solve? --> <!--- If it fixes an open issue, please link to the issue here. --> Should help speed up AI reasoning about the SDK offline/retry behaviour. ## 💚 How did you test it? ## 📝 Checklist <!--- Put an `x` in the boxes that apply --> - [ ] I added tests to verify the changes. - [ ] No new PII added or SDK only sends newly added PII if `sendDefaultPII` is enabled. - [ ] I updated the docs if needed. - [ ] I updated the wizard if needed. - [ ] Review from the native team if needed. - [ ] No breaking change or entry added to the changelog. - [ ] No breaking change for hybrid SDKs or communicated to hybrid SDKs. ## 🔮 Next steps
1 parent fbe7c10 commit 6fc8adc

File tree

1 file changed

+87
-0
lines changed

1 file changed

+87
-0
lines changed

.cursor/rules/offline.mdc

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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

Comments
 (0)