Skip to content

perf(core): Reuse shared executor for transaction timeouts (SDK-1347)#5646

Draft
runningcode wants to merge 2 commits into
mainfrom
no/sdk-1347-tracer-timeout-executor
Draft

perf(core): Reuse shared executor for transaction timeouts (SDK-1347)#5646
runningcode wants to merge 2 commits into
mainfrom
no/sdk-1347-tracer-timeout-executor

Conversation

@runningcode

Copy link
Copy Markdown
Contributor

📜 Description

SentryTracer created a dedicated java.util.Timer — i.e. a new thread — for every transaction that had an idle or deadline timeout. This change schedules those timeouts on the SDK's existing shared SentryExecutorService (a single ScheduledThreadPoolExecutor) instead, so no per-transaction thread is created.

  • Idle/deadline TimerTasks become Future<?>s scheduled via ISentryExecutorService.schedule(...).
  • The shared executor is created with setRemoveOnCancelPolicy(true) so timeouts that are cancelled early (the common case — a transaction finishing before its idle/deadline) are evicted from the work queue immediately instead of lingering until their scheduled time.
  • Reschedule-on-activity, cancel-on-finish, and the fallback that finishes immediately if scheduling is rejected are all preserved.

💡 Motivation and Context

Part of the Reduce SDK init time [Android] effort — specifically SDK-1347 (reduce the number of threads the SDK creates) and the thread/executor audit in JAVA-570. A per-transaction Timer thread is the SDK's largest source of thread churn at runtime; apps with many auto-instrumented transactions (activity/navigation/HTTP) paid a thread create+teardown each time. The shared ScheduledThreadPoolExecutor is alive for the whole transaction lifetime, so it is a safe host for these timeouts.

In production the shared executor is activated during Sentry.init, before any transaction is created. Tests that previously relied on the always-on per-transaction Timer now provide a real executor (or disable the transaction timeouts when isolating an unrelated scheduled task).

💚 How did you test it?

SentryTracerTest updated to assert the new future/scheduler state (incl. reschedule-on-child-finish and the closed-executor fallback); full :sentry:test, :sentry-android-core:testDebugUnitTest, and :sentry-android-navigation:testDebugUnitTest are green.

📝 Checklist

  • I added GH Issue ID & Linear ID
  • 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

Further thread consolidation candidates from the JAVA-570 audit: DefaultAndroidEventProcessor's throwaway init-path executor, and folding the remaining new Timer(true) sites (DefaultCompositePerformanceCollector, RateLimiter, LifecycleWatcher) onto the shared scheduler.

SDK-1347
JAVA-570

SentryTracer created a dedicated java.util.Timer (a new thread) per
transaction that had an idle or deadline timeout. For apps with many
transactions (screen loads, HTTP spans) this churned threads and added
CPU overhead.

Schedule the idle/deadline timeouts on the SDK's shared
SentryExecutorService (a single ScheduledThreadPoolExecutor) instead, so
no per-transaction thread is created. The executor is set with
removeOnCancelPolicy(true) so timeouts that are cancelled early (the
common case) are evicted from the queue immediately rather than lingering.

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

linear-code Bot commented Jun 25, 2026

Copy link
Copy Markdown

SDK-1347

@sentry

sentry Bot commented Jun 25, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

🔗 App Name App ID Version Configuration
SDK Size io.sentry.tests.size 8.46.0 (1) release

⚙️ sentry-android Build Distribution Settings

@github-actions

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 337.35 ms 398.32 ms 60.97 ms
Size 0 B 0 B 0 B

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
18c0bc2 306.73 ms 349.77 ms 43.03 ms
0eaac1e 316.82 ms 357.34 ms 40.52 ms
d15471f 303.49 ms 439.08 ms 135.59 ms
fc5ccaf 276.52 ms 370.46 ms 93.93 ms
e2dce0b 308.96 ms 360.10 ms 51.14 ms
5b1a06b 352.27 ms 413.70 ms 61.43 ms
37ec571 366.04 ms 424.28 ms 58.23 ms
9fbb112 361.43 ms 427.57 ms 66.14 ms
bbc35bb 324.88 ms 425.73 ms 100.85 ms
ff8eea4 313.42 ms 337.08 ms 23.66 ms

App size

Revision Plain With Sentry Diff
18c0bc2 1.58 MiB 2.13 MiB 557.33 KiB
0eaac1e 1.58 MiB 2.19 MiB 619.17 KiB
d15471f 1.58 MiB 2.13 MiB 559.54 KiB
fc5ccaf 1.58 MiB 2.13 MiB 557.54 KiB
e2dce0b 0 B 0 B 0 B
5b1a06b 0 B 0 B 0 B
37ec571 0 B 0 B 0 B
9fbb112 1.58 MiB 2.11 MiB 539.18 KiB
bbc35bb 1.58 MiB 2.12 MiB 553.01 KiB
ff8eea4 1.58 MiB 2.28 MiB 718.64 KiB

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.

1 participant