getsentry / getsentry/sentry-java
OutboxSender sends attachments as standalone envelopes
- 主要语言
- Kotlin
- 星标
- 1.4k
- 派生
- 478
- 平均合并
- 2 天 22 小时
- 30 天内合并 PR
- 69
描述
## What's happening?
When `OutboxSender` (sentry-java/Android) processes envelopes written to disk by sentry-native (e.g. after an NDK crash), it iterates each envelope item individually. Event and Transaction items go through `captureEvent`/`captureTransaction`, but any other item type — including Attachment — is wrapped in a brand-new single-item `SentryEnvelope` and sent standalone via `captureEnvelope`, instead of being sent together with the event that produced it.
## Why is it happening? (root cause)
This is `OutboxSender.processEnvelope` in `sentry/src/main/java/io/sentry/OutboxSender.java`. The loop dispatches Event/Transaction items via their dedicated capture calls, and falls through to a generic `else` branch for every other item type that wraps that single item in its own envelope and sends it immediately (around `OutboxSender.java#L124`, current `captureEnvelope(newEnvelope, hint)` call). There is no step that buffers non-event items and reassembles them into one outgoing envelope keyed by `event_id` before sending.
## Possible solutions (proposal, not yet decided)
* Buffer Attachment-type items encountered while iterating `envelope.getItems()`, converting each `SentryEnvelopeItem` into an `Attachment` (filename/content-type from the item header, bytes from `item.getData()`).
* When the Event or Transaction item is found, call `hint.addAttachments(buffered)` before `captureEvent`/`captureTransaction`, instead of sending each attachment through its own `captureEnvelope` call. `Hint` already supports `addAttachments(List)`, so this doesn't require a new envelope format.
* Handle item ordering (attachments may appear before or after the event item within the envelope) and the edge case of attachment-only envelopes (no event/transaction present), which must still be sent standalone.
* Add an explicit size check before merging: if attaching would push the combined envelope over the max envelope size, fall back to sending that attachment standalone (or drop it) rather than risking the whole event getting rejected.
* Only merge Attachment-type items — leave other non-event item types (session, user feedback, etc.) on their current per-item send path to avoid changing unrelated behavior.
* Extend `OutboxSenderTest.kt` to cover reassembly, mixed item ordering, and the no-event fallback.
* Estimated scope: mostly contained to `OutboxSender.java` and its test, no protocol/API changes, no server-side work required — ingestion already accepts combined event+attachment envelopes (used by JS and other SDKs today) and will keep supporting standalone attachments for legacy SDK versions regardless.
## User Impact
* Quota/cost impact: Relay has no filter stage for standalone attachments (only `processing/errors/` has one), so when an event is dropped by an inbound filter, its standalone attachment still gets ingested and billed — landing as an orphan the customer can't even see, since there's no event to attach it to. Riot Games hit this concretely, ingesting roughly 10TB/day of attachments with ~90% having no associated event, and a live investigation (`INGEST-1167`) found Android/Java is currently the SDK with the highest volume of standalone attachments. Ingest is shipping a server-side stopgap (discard orphaned attachments at the filter stage), but fixing `OutboxSender` removes the root cause on the Android/Java side rather than relying on ingest to keep mopping up after it.
* Mainly affects Android apps using sentry-native (NDK crash reporting) and hybrid SDKs (Unity, Flutter, React Native, etc.) layered on top of sentry-android, since they're the ones writing envelopes to the outbox folder that `OutboxSender` later fans out.
## History of the issue
The design is incidental, not deliberate. The original envelope-pickup code (`getsentry/sentry-android#89`, `getsentry/sentry-android#145`, 2019) only handled Event items; every other item type was simply logged as ignored, with a `// TODO: Handle attachments and other types` comment left in place. In January 2021, `getsentry/sentry-java#1158` ("OutboxSender supports all envelope item types") closed that TODO by adding the current catch-all branch: wrap any non-event item in its own envelope and send it immediately. That was a pragmatic fix to stop dropping items, not a considered protocol decision — there's no historical discussion of envelope size limits, item ordering, or ingestion semantics tied to it.
## Other useful context
This surfaced from [a cross-SDK discussion]() (also [here]()) about disallowing standalone attachments at the protocol/spec level (develop-docs). Ingestion will keep accepting standalone attachments indefinitely to support already-shipped legacy SDK versions, so this fix is about sentry-java emitting the preferred combined-envelope format going forward, not a breaking or urgent change. If the spec formally disallows standalone attachments before this is fixed, `OutboxSender`'s native-outbox path should get an explicit documented exception until this issue is resolved.
Action taken on behalf of Adam Brown.
贡献指南
评估
这个 Issue 还没有评估数据。