Skip to content

perf: Share one executor between log and metrics batch processors (JAVA-653)#5818

Draft
runningcode wants to merge 2 commits into
mainfrom
no/perf/share-batch-processor-executor
Draft

perf: Share one executor between log and metrics batch processors (JAVA-653)#5818
runningcode wants to merge 2 commits into
mainfrom
no/perf/share-batch-processor-executor

Conversation

@runningcode

@runningcode runningcode commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

📜 Description

LoggerBatchProcessor and MetricsBatchProcessor each constructed their own SentryExecutorService, so an app with both logs and metrics enabled ran two dedicated threads for structurally identical work (a 5-second flush loop that batches events and hands them to the transport).

SentryClient now owns a single SentryExecutorService, created only when logs or metrics are enabled, and exposes it via getBatchProcessorExecutorService(). The default factories inject it into both processors (through the existing 3-arg LoggerBatchProcessor constructor and a new matching one on MetricsBatchProcessor). The pluggable factory interfaces are unchanged.

The shutdown paths already close the executor from each processor's close(); since the executor is now shared, the second close is a safe no-op, and the restart branch's submit(...) is guarded with the same RejectedExecutionException fallback already used in Scopes.close() for the main executor.

💡 Motivation and Context

Part of reducing the number of threads created by the SDK: JAVA-653.

Saves one thread for every app that has both logs and metrics enabled. The executor is created only when at least one of logs or metrics is enabled, and its worker thread is spawned only when the first flush is scheduled.

Note for reviewers

Custom ILoggerBatchProcessorFactory / IMetricsBatchProcessorFactory implementations that call the public 2-arg processor constructors still get an independent executor (unchanged behavior). Only the default factories opt into the shared one.

💚 How did you test it?

New SentryClientTest case asserting both processors receive the same executor instance (and that it is the client's shared one), plus the existing LoggerBatchProcessorTest, MetricsBatchProcessorTest, SentryClientTest, LoggerApiTest, and MetricsApiTest suites.

📝 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

Related PRs in this effort: RateLimiter (#5814), LifecycleWatcher (#5819), performance collector (#5816), HostnameCache (#5817).

🤖 Generated with Claude Code

@linear-code

linear-code Bot commented Jul 22, 2026

Copy link
Copy Markdown

JAVA-653

@sentry

sentry Bot commented Jul 22, 2026

Copy link
Copy Markdown

📲 Install Builds

Android

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

⚙️ sentry-android Build Distribution Settings

@github-actions

github-actions Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

Performance metrics 🚀

  Plain With Sentry Diff
Startup time 427.22 ms 526.69 ms 99.48 ms
Size 0 B 0 B 0 B

Baseline results on branch: main

Startup times

Revision Plain With Sentry Diff
eb95ded 317.51 ms 369.08 ms 51.57 ms
2124a46 319.19 ms 415.04 ms 95.85 ms
c3ee041 310.64 ms 361.90 ms 51.26 ms
dcc6bbf 382.58 ms 462.13 ms 79.54 ms
7a19fee 315.46 ms 368.62 ms 53.16 ms
9054d65 330.94 ms 403.24 ms 72.30 ms
d500866 326.13 ms 378.70 ms 52.58 ms
ad8da22 365.86 ms 427.00 ms 61.14 ms
ed33deb 337.52 ms 484.06 ms 146.54 ms
d15471f 310.26 ms 377.04 ms 66.78 ms

App size

Revision Plain With Sentry Diff
eb95ded 0 B 0 B 0 B
2124a46 1.58 MiB 2.12 MiB 551.51 KiB
c3ee041 0 B 0 B 0 B
dcc6bbf 1.58 MiB 2.12 MiB 553.10 KiB
7a19fee 0 B 0 B 0 B
9054d65 1.58 MiB 2.29 MiB 723.38 KiB
d500866 0 B 0 B 0 B
ad8da22 1.58 MiB 2.29 MiB 719.83 KiB
ed33deb 1.58 MiB 2.13 MiB 559.52 KiB
d15471f 1.58 MiB 2.13 MiB 559.54 KiB

runningcode and others added 2 commits July 22, 2026 17:54
…VA-653)

The log and metrics batch processors each created their own
SentryExecutorService, so an app using both spawned two threads for
work that is identical in shape (a 5s flush loop that hands envelopes
to the transport). SentryClient now owns a single executor, created
only when logs or metrics are enabled, and injects it into both
processors via the default factories. The restart shutdown path
tolerates the shared executor already being closed by the sibling
processor.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@runningcode
runningcode force-pushed the no/perf/share-batch-processor-executor branch from bb143cc to 875b0ea Compare July 22, 2026 15:55
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