Croway commented on PR #28729:
URL: https://github.com/apache/camel/pull/28729#issuecomment-6576343651

   The gauge lookup removed here is only one of several registry scans per 
exchange, so this PR alone barely changes the cost. Two more lookups in 
`MicrometerExchangeEventNotifier` are where the time goes:
   
   1. `getOrCreateTimer()` calls `find(name).tags(tags).timer()` on every sent 
and completed event. Each call copies and filters every registered meter. 
`Timer.builder(...).register(registry)` returns the same timer through a map 
lookup.
   2. `notify()` calls `CamelContext.getRoute(routeId)` on every event to check 
for kamelet/template routes. That call copies the route list under 
`routesLock`. `addInflightMeter()` already makes the same decision once per 
route, so the excluded route ids can be kept in a set.
   
   I ran and verified these performance tests: JMH, Camel 4.22.1 + Micrometer 
1.17.2, JDK 21. Routes are `from(direct)` + 3 × `to(log)`, wired like the 
camel-micrometer-prometheus defaults. Each cell shows **4.22.1 / + this PR / + 
this PR + the change below**. The first and last values come from built jars, 
the middle one from an exact copy of this PR's class:
   
   | routes (meters) | µs per exchange, 1 thread | exchanges/s, 4 threads |
   |---|---|---|
   | 1 (23) | 6.6 / 6.4 / **3.9** | – |
   | 10 (131) | 20.6 / 19.3 / **4.3** | 160k / 170k / **551k** |
   | 50 (611) | 90.4 / 89.6 / **4.3** | 40k / 37k / **666k** |
   | 200 (2,411) | 386 / 359 / **4.9** | 10k / 10k / **599k** |
   
   Without the exchange notifier it is ~2.2 µs and ~1.1M exchanges/s. With the 
change, the notifier costs ~2 µs per exchange regardless of route count, and 
allocation at 200 routes drops from 360 KB to 8 KB per exchange. The timers, 
counts and gauge recorded are identical to 4.22.1.
   
   <details>
   <summary>Suggested change (applies on this PR, main and 
camel-4.22.1)</summary>
   
   ```diff
   @@ -18,6 +18,8 @@ package 
org.apache.camel.component.micrometer.eventnotifier;
    import java.util.HashMap;
    import java.util.Map;
   +import java.util.Set;
   +import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.TimeUnit;
   @@ -47,6 +49,9 @@ public class MicrometerExchangeEventNotifier extends 
AbstractMicrometerEventNoti
        private final Map<String, Meter> meterMap = new HashMap<>();
   +    // ids of routes not to meter (created by kamelets/templates), decided 
once when the route is added so the
   +    // exchange events do not need CamelContext.getRoute(id), which copies 
the route list on every call
   +    private final Set<String> skippedRouteIds = 
ConcurrentHashMap.newKeySet();
        private Predicate<Exchange> ignoreExchanges = exchange -> false;
   @@ -143,6 +148,9 @@ public class MicrometerExchangeEventNotifier extends 
AbstractMicrometerEventNoti
        private void addInflightMeter(Route route) {
            boolean skip = (route.isCreatedByKamelet() && !registerKamelets)
                    || (route.isCreatedByRouteTemplate() && !registerTemplates);
   +        if (skip) {
   +            skippedRouteIds.add(route.getRouteId());
   +        }
            if (!skip) {
   @@ -156,6 +164,7 @@ public class MicrometerExchangeEventNotifier extends 
AbstractMicrometerEventNoti
        private void removeInflightMeter(String routeId) {
   +        skippedRouteIds.remove(routeId);
            Meter meter = meterMap.remove(routeId);
   @@ -169,6 +178,7 @@ public class MicrometerExchangeEventNotifier extends 
AbstractMicrometerEventNoti
            meterMap.values().forEach(m -> getMeterRegistry().remove(m));
            meterMap.clear();
   +        skippedRouteIds.clear();
        }
   @@ -182,12 +192,8 @@ public class MicrometerExchangeEventNotifier extends 
AbstractMicrometerEventNoti
                    routeId = ExchangeHelper.getAtRouteId(ee.getExchange());
                }
   -            if (routeId != null) {
   -                Route route = 
ee.getExchange().getContext().getRoute(routeId);
   -                if (route != null) {
   -                    skip = (route.isCreatedByKamelet() && !registerKamelets)
   -                            || (route.isCreatedByRouteTemplate() && 
!registerTemplates);
   -                }
   +            if (routeId != null && skippedRouteIds.contains(routeId)) {
   +                skip = true;
                }
   @@ -231,11 +237,8 @@ public class MicrometerExchangeEventNotifier extends 
AbstractMicrometerEventNoti
        private Timer getOrCreateTimer(final String name, final Tags tags, 
final String description) {
   -        Timer timer = getMeterRegistry().find(name).tags(tags).timer();
   -        if (timer == null) {
   -            timer = 
Timer.builder(name).tags(tags).description(description).register(getMeterRegistry());
   -        }
   -        return timer;
   +        // register() returns the existing timer with a map lookup; find() 
would copy and scan every meter
   +        return 
Timer.builder(name).tags(tags).description(description).register(getMeterRegistry());
        }
   ```
   
   </details>
   
   I also have a test for it, `MicrometerExchangeEventNotifierHotPathTest`:
   - **No registry search per exchange.** This check fails on this PR's head 
and passes with the change.
   - **Kamelet routes not metered,** both at startup and when added at runtime. 
Nothing covered the kamelet exclusion before.
   
   camel-micrometer is green both on this PR's head and on 4.22.1 with the 
change. I can push it as a commit for you to pick, or open a follow-up PR. The 
same per-event `getRoute()` lookup is in camel-opentelemetry-metrics' 
`OpenTelemetryExchangeEventNotifier`.
   
   _Drafted with Claude Code._
   


-- 
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]

Reply via email to