rbankar7 opened a new pull request, #19942:
URL: https://github.com/apache/druid/pull/19942

   
   
   ### 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 to `LifecycleScope$1.get`.
   
   #### Root cause
   
   `ComposingEmitterModule#getEmitter` built its child-emitter list with 
`Lists.transform(...)`, which returns a **lazy** Guava view rather than a 
materialized list:
   
   ```java
   List<Emitter> emitters = Lists.transform(
       config.getEmitters(),
       s -> injector.getInstance(Key.get(Emitter.class, Names.named(s)))
   );
   return new ComposingEmitter(emitters);
   ```
   
   `ComposingEmitter.emit(Event)` iterates this list once per event:
   
   ```java
   public void emit(Event event) {
     for (Emitter e : emitters) {
       e.emit(event);
     }
   }
   ```
   
   Since `emitters` is 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 through `LifecycleScope#get()`, 
which is `synchronized` and 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 
`ImmutableList` at construction time, so `ComposingEmitter.emit()` iterates a 
plain materialized list with no injector lookups and no locking:
   
   ```java
   List<Emitter> emitters = config.getEmitters()
                                  .stream()
                                  .map(s -> 
injector.getInstance(Key.get(Emitter.class, Names.named(s))))
                                  .collect(ImmutableList.toImmutableList());
   return new ComposingEmitter(emitters);
   ```
   
   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 
`ComposingEmitterModule` with its sibling `SwitchingEmitterModule`, 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.
   
   <hr>
   
   ##### Key changed/added classes in this PR
    * `ComposingEmitterModule`
   
   <hr>
   
   This PR has:
   
   - [ ] been self-reviewed.
      - [ ] using the [concurrency 
checklist](https://github.com/apache/druid/blob/master/dev/code-review/concurrency.md)
 (Remove this item if the PR doesn't have any relation to concurrency.)
   - [ ] added documentation for new or modified features or behaviors.
   - [ ] a release note entry in the PR description.
   - [ ] added Javadocs for most classes and all non-trivial methods. Linked 
related entities via Javadoc links.
   - [ ] added or updated version, license, or notice information in 
[licenses.yaml](https://github.com/apache/druid/blob/master/dev/license.md)
   - [ ] added comments explaining the "why" and the intent of the code 
wherever would not be obvious for an unfamiliar reader.
   - [ ] added unit tests or modified existing tests to cover new code paths, 
ensuring the threshold for [code 
coverage](https://github.com/apache/druid/blob/master/dev/code-review/code-coverage.md)
 is met.
   - [ ] added integration tests.
   - [ ] been tested in a test Druid cluster.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to