perf: materialize child emitters in ComposingEmitter to avoid per-event Guice resolution - #19942
Draft
rbankar7 wants to merge 3 commits into
Draft
perf: materialize child emitters in ComposingEmitter to avoid per-event Guice resolution#19942rbankar7 wants to merge 3 commits into
rbankar7 wants to merge 3 commits into
Conversation
abhishekrb19
approved these changes
Aug 10, 2026
abhishekrb19
left a comment
Contributor
There was a problem hiding this comment.
Lgtm, good catch! Double-checked SwitchingEmitterModule as well - same eager resolution behavior already applies there.
Contributor
|
@rbankar7 this PR is marked as draft — are there any additional changes you're intending to make here? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #19943
Description
When Druid is configured with
druid.emitter=composing, the composing emitter was re-resolving all of its child emitters from the Guice injector on every emitted event. Because Druid emits a metric per segment scanned, on busy nodes this executes tens of thousands of times per second, and each resolution takes a monitor lock. Lock profiling on affected nodes attributed the large majority of lock-contention samples toLifecycleScope$1.get.Root cause
ComposingEmitterModule#getEmitterbuilt its child-emitter list withLists.transform(...), which returns a lazy Guava view rather than a materialized list:ComposingEmitter.emit(Event)iterates this list once per event:Since
emittersis a lazy view, each iteration re-ran the transform function, which re-resolved every child emitter from the injector. For lifecycle-scoped bindings, that resolution goes throughLifecycleScope#get(), which issynchronizedand so takes a monitor lock on every call — even though, after startup, it does nothing but return an already-cached instance. The child emitters were therefore being looked up (and locked) on the hottest path in the process rather than resolved once at startup.Fixed the lock contention in the composing emitter
The child emitters are now resolved eagerly, exactly once, into an
ImmutableListat construction time, soComposingEmitter.emit()iterates a plain materialized list with no injector lookups and no locking:This is purely an internal change — the same set of child emitters is composed in the same order. The only difference is when they are resolved: once during provisioning instead of on every emitted event. This also aligns
ComposingEmitterModulewith its siblingSwitchingEmitterModule, which already resolves its child emitters eagerly into materialized lists.Release note
Fixed lock contention on the metrics emit path when using the composing emitter (
druid.emitter=composing). Child emitters are now resolved once at startup instead of on every emitted event, which previously caused significant monitor contention on nodes emitting metrics at high volume.Key changed/added classes in this PR
ComposingEmitterModuleThis PR has: