Skip to content

[pull] main from getsentry:main - #411

Merged
pull[bot] merged 6 commits into
NOUIY:mainfrom
getsentry:main
Jul 30, 2026
Merged

[pull] main from getsentry:main#411
pull[bot] merged 6 commits into
NOUIY:mainfrom
getsentry:main

Conversation

@pull

@pull pull Bot commented Jul 30, 2026

Copy link
Copy Markdown

See Commits and Changes for more details.


Created by pull[bot] (v2.0.0-alpha.4)

Can you help keep this open source service alive? 💖 Please sponsor : )

dependabot Bot and others added 6 commits July 30, 2026 10:26
Bumps the github-actions group with 2 updates: [getsentry/craft/.github/workflows/changelog-preview.yml](https://github.com/getsentry/craft) and [getsentry/craft](https://github.com/getsentry/craft).


Updates `getsentry/craft/.github/workflows/changelog-preview.yml` from 2.27.0 to 2.27.2
- [Release notes](https://github.com/getsentry/craft/releases)
- [Changelog](https://github.com/getsentry/craft/blob/master/CHANGELOG.md)
- [Commits](getsentry/craft@667b5f5...8fd703e)

Updates `getsentry/craft` from 2.27.0 to 2.27.2
- [Release notes](https://github.com/getsentry/craft/releases)
- [Changelog](https://github.com/getsentry/craft/blob/master/CHANGELOG.md)
- [Commits](getsentry/craft@667b5f5...8fd703e)

---
updated-dependencies:
- dependency-name: getsentry/craft/.github/workflows/changelog-preview.yml
  dependency-version: 2.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
- dependency-name: getsentry/craft
  dependency-version: 2.27.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: github-actions
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* ref(android): Remove unused lock from SentryPerformanceProvider

The field was added in #3715 to guard onAppStartDone(), its only user. That
method was removed in the 8.x.x merge (#4033), leaving the lock orphaned — it
has been allocated on every cold start since, in ContentProvider.onCreate,
without ever being acquired.

The ISentryLifecycleToken import stays: it is still used for
AppStartMetrics.staticLock in shutdown().

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* changelog

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
#5867)

* perf(android): Parse app start profiling config without JsonSerializer (JAVA-621)

SentryPerformanceProvider.launchAppStartProfiler ran in ContentProvider.onCreate
— main thread, before Application.onCreate, on every cold start — and built a
full JsonSerializer(SentryOptions.empty()) to read one small config file.

That path only ever reads options.getLogger(), but JsonSerializer's constructor
registers every known deserializer to use exactly one of them. Calling the
deserializer directly cuts the parse from 221 to 33 allocations and ~7.5us to
~3.7us (Pixel 10, androidx Microbenchmark, allocationCount is deterministic).

Malformed input still yields null so callers keep reporting it as a
deserialization failure rather than a read error.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* changelog

* ref(android): Catch only expected exceptions when parsing profiling config

Narrow the deserialization catch from Throwable to Exception. The vendored JSON
reader signals bad input with IOException (MalformedJsonException, EOFException),
IllegalStateException on token type mismatch, and NumberFormatException on an
unparseable number — all Exception subclasses. Catching Throwable additionally
swallowed Error, which is never a recoverable "config file is bad" signal.

This also restores parity with JsonSerializer.deserialize, which catches
Exception, so the replaced behaviour is matched exactly rather than widened.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* ref(android): Trim javadoc on deserializeProfilingConfig

Drop the paragraph enumerating which exception types the vendored JSON reader
throws. That catch (Exception) does not swallow Error is a language-level
given, and listing the reader's internal exception types invites the comment
to drift as that code changes. The rationale a reader cannot infer from the
code — why the deserializer is called directly, and why null is returned
instead of rethrowing — stays.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(clientreport): Stop deserializing discarded logs (JAVA-662)

ClientReportRecorder counted discarded log and metric items by fully
deserializing the envelope payload just to read its size. On the discard
path this runs continuously under sustained rate limiting, and the JSON
reader's error-tolerant recovery throws an exception per token, pinning
CPU cores in a busy-loop (fillInStackTrace dominated the profile).

The item count is already stored in the envelope item header, so read it
from there instead of deserializing. Byte counts still come from the raw
data. This makes the discard path O(1) and allocation/exception-free.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* changelog

* test(clientreport): Cover both onDiscard restore entry points (JAVA-662)

The two tests asserting that restoring counts from an attached client
report does not re-fire onDiscard were named for their setup rather than
for what actually differed between them, which made the pair read as an
accidental duplicate.

Name each for its entry point and share the setup and verifications, so
it is clear the property is being pinned for both recordLostEnvelope and
recordLostEnvelopeItem.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* perf(core): Batch and coalesce scope-persistence disk writes (JAVA-628)

Scope persistence wrote to disk on every scope mutation, which dominated
SDK cost during startup: each breadcrumb triggered a synchronous fsync'd
QueueFile append, and every other scope field (contexts, trace, user,
tags, ...) rewrote its whole file on each change even though only the
latest value matters.

Coalesce mutations instead of writing eagerly. Each field keeps only its
latest pending value and is flushed once per debounce window; breadcrumbs
are buffered and appended together behind a single fsync (QueueFile gains
an opt-in buffered-write mode plus sync()). This trades a small data-loss
window (~100ms before the process dies) for far fewer writes and fsyncs.

Persistence exists to enrich crash/ANR events on the next launch, and was
already asynchronous, so the widened loss window is acceptable.

* Add changelog entry for #5791

* perf(core): Coalesce scope writes on submit instead of a timer (JAVA-628)

Scope-persistence flushes were debounced 100ms behind a scheduled task.
The debounce was unnecessary: the Sentry executor is single-threaded, so a
submitted flush task already sits in the queue long enough for mutations
arriving behind it to be folded into the same write. That is exactly the
window that matters, since the queue is deepest during startup.

Submit the flush instead of scheduling it. Coalescing now tracks executor
load rather than a fixed delay, which closes the data-loss window the
debounce introduced. Guard against the executor rejecting the task without
throwing once its queue is full, which would otherwise leave the pending
flag set and stop scope persistence for the rest of the process.

Also record why resetCache() deliberately leaves pending mutations alone:
they only ever hold values from the current process, so dropping them
would lose scope state set during init rather than clearing the previous
run's data.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(core): Keep breadcrumbs added during a scope flush (JAVA-628)

Batching split one ordered stream of breadcrumb operations into two
independent pieces of state: a queue of pending adds and a separate
clear flag. Independent state cannot preserve an ordering that the old
FIFO executor queue gave for free.

flushPending consumed the clear flag with a CAS at the top, then spent
the rest of the method serializing and writing each breadcrumb. A clear
plus a subsequent add landing in that window had its clear applied after
the new breadcrumb was already on disk, so the follow-up flush wiped a
breadcrumb that was added after the clear. Breadcrumbs are added from
arbitrary threads while the flush runs on the executor thread, so this
needs no unusual timing.

Enqueue the clear into the breadcrumb queue as a sentinel instead,
mirroring the DELETE_MARKER pattern already used for pendingWrites.
Ordering becomes intrinsic to the queue rather than something the flush
has to reconstruct. This also stops setBreadcrumbs(emptyList()) from
calling clear() on the shared queue, which could discard a breadcrumb
offered concurrently by another thread even with no flush in flight.

* docs(core): Clarify scope-persistence comments (JAVA-628)

"Durable" overstated what the buffered-write mode guarantees. sync()
issues an fsync, but durability also depends on the storage stack, so
describe what the code does — write to disk — rather than promise an
outcome it cannot ensure on its own.

Also name the pendingBreadcrumbs queue for what it holds: requests to
add and clear, not breadcrumbs already buffered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor(core): Fold the scope flush into one method (JAVA-628)

flushPending existed only so the @testonly flush() could do the disk
write without the latch bookkeeping that belongs to the queued task.
Nothing in production called flush(), and its only test asserted the
behaviour of flush() itself, so the second entry point was buying two
near-identical methods and a way to corrupt hasPendingFlush.

Drop flush() and merge the pair: flush() is now the queued task and
writePending() the write it performs. Clear hasPendingFlush in a finally
so an unexpected throw cannot leave the flag set, which would stop scope
persistence for the rest of the process.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(core): Drop redundant buffered-write QueueFile test (JAVA-628)

bufferedWritesSurviveReopenAfterSync was testAddOneElement with a second
element and a sync() call: same write, close, reopen, read-it-back shape,
and testAddAndRemoveElements already covers round-tripping far more
thoroughly.

The sync() it appeared to exercise was not actually load-bearing. close()
flushes through to the OS whichever mode the file was opened in, so the
assertions would hold even if sync() did nothing — the name promised
durability semantics the test could not verify in-process. Verifying that
would take a killed process, not a reopen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs(core): Record that the vendored Tape fork has diverged (JAVA-628)

Square archived Tape on 2024-10-25, so the upstream link in each file
header points at a snapshot that will never receive fixes. Our copy has
also drifted from it: corruption recovery, a bounded ring size, and
optional buffered writes.

Say both things where a reader will look — the file headers, the class
javadoc, and THIRD_PARTY_NOTICES.md — so nobody diffs against upstream
expecting it to explain our behaviour, or files a bug there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* refactor(core): Iterate pendingWrites directly when flushing (JAVA-628)

writePending() copied the key set into an ArrayList before draining it,
which allocates on every flush for no benefit: ConcurrentHashMap's
iterator is weakly consistent, so walking it while removing entries is
already safe.

Removal still goes through the map, not the iterator, because that is an
atomic get-and-remove — we never drop a value stored by a mutation racing
with the loop. Keys added after iteration starts can be missed, which is
fine since flush() re-checks and queues another write.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…s on API 35+ (#5841)

* fix(android): Drop inflated app start for background-spawned processes on API 35+

When the OS spawns the process for background work (FCM push, job, service,
broadcast, etc.) and the user opens the app later, the app start stayed anchored
at background process creation, inflating the reported cold start by the whole
idle gap.

On API 35+ we now use ApplicationStartInfo.getReason() to detect background
process starts and mark them as not launched in foreground, so the first created
activity re-classifies them as a warm start anchored at activity creation.

* docs: fix changelog PR link

* docs: trim code comments

* docs: trim changelog entry

* Update Changelog

* Reverse isBackgroundStartReason to isForegroundStartReason

* Add extra test

---------

Co-authored-by: Markus Hintersteiner <markus.hintersteiner@sentry.io>
@pull pull Bot locked and limited conversation to collaborators Jul 30, 2026
@pull pull Bot added the ⤵️ pull label Jul 30, 2026
@pull
pull Bot merged commit 6de3962 into NOUIY:main Jul 30, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants