[ 
https://issues.apache.org/jira/browse/CAMEL-24887?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18124442#comment-18124442
 ] 

Claus Ibsen commented on CAMEL-24887:
-------------------------------------

_Analysis and implementation plan by Claude Code on behalf of davsclaus. 
Torsten, the issue is yours. This is written so that you or your agent can pick 
it up directly._

h3. Decision

The group MBean should count an exchange *once per group*, as the CamelContext 
MBean already counts it once per context. We are not adding {{getAggregated*}} 
or "transaction" methods. The 4.22.1 behaviour is a regression from 
CAMEL-24590, so please change the issue type to *Bug*.

Why:
* The original design was 1 per exchange per group. {{ManagedRouteGroupTest}} 
from CAMEL-22310 ({{dfb7d5cb9b1c}}) asserted {{ExchangesTotal == 1}} for both 
groups, although group "first" has 3 member routes on the exchange's path and 
"second" has 2. CAMEL-24590 ({{7c231133c27e}}, backported to 4.22.x as 
{{b9e1da150b80}}) fixed the wiring (one shared {{ManagedRouteGroup}} per group, 
which is correct and stays). It also changed those assertions to 3 and 2, and 
nothing deduplicates the counts.
* The same problem was solved for the context before: 
{{ManagedCamelContext.completedExchange/failedExchange/processExchange}} only 
count when {{uow.routeStackLevel(includeRouteTemplates, includeKamelets) <= 1}} 
(CAMEL-18557, comment: "if an exchange is routed via 3 routes we should only 
count this as 1 instead of 3").
* Summing over the member routes also double counts the processing times. X1 
calling X2 via {{direct:}} means X1's time already includes X2's, so the 
group's total, mean and max processing times are wrong too, not just the 
counts. That is why an "aggregated" variant is not worth keeping.
* An exchange property marker is not needed. It would add a property to every 
exchange, and with "first callback wins" the innermost route (which completes 
first) would be the one recorded, so the processing time would be wrong.

h3. Rule

A route event (process, completed, failed) counts for group G only when the 
route raising it is the *outermost route of group G on the exchange's route 
stack*. In other words, the number of routes of group G on the stack is {{<= 
1}}. Examples:
* X1 -> X2 (both in G): counted once, when X1 runs.
* X1 -> Y (not in G) -> X2: counted once. Only X1 counts, because X1 is still 
on the stack while X2 runs.
* Y (not in G) -> X2: counted once, when X2 runs.
* wireTap, seda, multicast with parallel processing or other copies into a 
member route: the copy is a new exchange with its own UnitOfWork, so it counts 
separately. The context MBean behaves the same way.
* The rule has to be the same in {{processExchange}}, {{completedExchange}} and 
{{failedExchange}}, otherwise the inflight counter drifts.

h3. Implementation

*1. camel-api: a filtered route stack level*

{{core/camel-api/src/main/java/org/apache/camel/spi/UnitOfWork.java}}: add next 
to {{routeStackLevel(boolean, boolean)}}:
{code:java}
/**
 * Gets the number of routes on the route stack that this {@link UnitOfWork} 
currently is being routed through,
 * which match the given filter.
 * <p/>
 * For example to know whether the current route is the outermost route of a 
route group on the stack.
 *
 * @param  filter the filter
 * @return        the number of matching routes on the route stack
 * @since 4.23
 */
default int routeStackLevel(Predicate<Route> filter) {
    return 0;
}
{code}
* The {{default}} keeps third-party {{UnitOfWork}} implementations compiling. 
It returns 0, so those implementations keep the current, summed behaviour. In 
Camel only {{DefaultUnitOfWork}} and {{MDCUnitOfWork extends 
DefaultUnitOfWork}} implement it. Please search camel-quarkus and 
camel-spring-boot to confirm.
* Use the {{@since}} of the release the change goes into. If it misses 4.23.0, 
which is cut on Friday 2026-10-09, it becomes {{4.24}}.

{{core/camel-base-engine/src/main/java/org/apache/camel/impl/engine/DefaultUnitOfWork.java}}:
 implement it under the existing {{lock}}, counting {{route}} plus 
{{routeStack}}, like {{routeStackLevel(boolean, boolean)}} at line ~430 does.

*2. camel-management: deduplicate in ManagedRouteGroup*

{{core/camel-management/src/main/java/org/apache/camel/management/mbean/ManagedRouteGroup.java}}:
 override the three counter methods, following {{ManagedCamelContext}} lines 
106-190:
{code:java}
@Override
public void completedExchange(Exchange exchange, long time) {
    if (isOutermostRouteOfGroup(exchange)) {
        super.completedExchange(exchange, time);
    }
}
// same for failedExchange(Exchange) and processExchange(Exchange, String)

private boolean isOutermostRouteOfGroup(Exchange exchange) {
    UnitOfWork uow = exchange.getUnitOfWork();
    // no unit of work: count, as ManagedCamelContext does
    return uow == null || uow.routeStackLevel(r -> group.equals(r.getGroup())) 
<= 1;
}
{code}
* Do not change {{CompositePerformanceCounter}} or the caching in 
{{JmxManagementLifecycleStrategy}}. The shared instance from CAMEL-24590 is 
right.
* Check first that the route counter's callbacks fire while the route is still 
on the UnitOfWork stack. {{ManagedCamelContext}}'s {{level <= 1}} relies on 
this too, so a breakpoint on the X2 completion should show both X1 and X2 on 
the stack.
* Unlike the context MBean, no {{includeRouteTemplates}}/{{includeKamelets}} 
filtering: membership is the group name.

*3. Tests (camel-management, JUnit style as in the existing ManagedRouteGroup 
tests)*
* {{ManagedRouteGroupTest}}: restore the original expectation, {{ExchangesTotal 
== 1}} for both "first" and "second", and drop the "aggregate" comments added 
by CAMEL-24590.
* {{ManagedRouteGroupFailuresHandledTest}}: {{trigger}} is the outermost route 
and the {{onException}} sends to {{step1 -> step2}} under it. Expect 
{{ExchangesCompleted == 1}} (currently 3), and {{FailuresHandled == 1}} plus 
the timestamp unchanged. This keeps the CAMEL-24590 bug fixed.
* New test (for example {{ManagedRouteGroupExchangeCountTest}}) covering the 
reporter's case and the rule:
** X1 -> X2, send 1 message: completed 1, total 1, inflight 0. Send 3: 
completed 3.
** X1 -> Y (no group) -> X2: 1.
** a route outside the group calling X2 directly: 1.
** a failure in X2 that is not handled: group {{ExchangesFailed == 1}}, 
{{ExchangesCompleted == 0}}, inflight 0.
** a failure handled in a *nested* member (onException in X2, continuing in 
X1): the group still records 1 failure handled. That is, the handled flag is 
still on the exchange when X1 completes. If it is not, decide whether that is 
acceptable. The context MBean has the same semantics, so assert that the group 
and the context agree when all routes are in one group.
** two groups on one path (X1 in G1 -> Z1 in G2): each group counts 1.
** {{wireTap("direct:x2")}}: G counts 2, consistent with the context MBean.
* A unit test in camel-base-engine for 
{{DefaultUnitOfWork.routeStackLevel(Predicate)}}.

*4. Docs*
* {{docs/user-manual/modules/ROOT/pages/route-group.adoc}}, "Monitoring Route 
Groups": add one sentence. Each exchange is counted once per group, even when 
it is routed through several routes of the group, as the CamelContext MBean 
counts it once. The per-route numbers are on the route MBeans.
* Upgrade guide, on {{main}}:
** {{camel-4x-upgrade-guide-4_22.adoc}}, 4.22.2 section: the group exchange 
counters and processing times count each exchange once per group again, as in 
4.22.0. In 4.22.1 they counted it once per member route.
** the upgrade guide of the release the fix goes into ({{4_23}} or {{4_24}}): 
the same note, for users coming from 4.22.1.
* No other code needs changing: {{RouteGroupDevConsole}} and {{camel get 
group}} read the MBean, so they pick up the fix.

*5. Backport*
* {{camel-4.22.x}}: yes. It is a human-reported regression introduced by a 
4.22.x backport. The API addition is a {{default}} method, so it is additive. 
Add {{port/camel-4.22.x}} to the PR.
* {{camel-4.18.x}}: no. CAMEL-24590 is not on that branch ({{git log 
origin/camel-4.18.x --grep CAMEL-24590}} is empty), so 4.18 still has the old 
single-route wiring.
* fixVersions: 4.22.2 plus 4.23.0 or 4.24.0, depending on when it is merged.

*6. Build*
{{cd core/camel-api && mvn -DskipTests install}}, then 
{{core/camel-base-engine}}, then {{cd core/camel-management && mvn install}}. 
Run the {{ManagedRouteGroup*}} and {{ManagedCamelContext*}} tests, and the 
camel-console tests for the route group console.

h3. Answer for the reporter

{{getExchangesCompleted()}} goes back to 1 per message for the group (the 
4.22.0 numbers). The per-route counts stay available on each route's 
{{ManagedRouteMBean}}.


> ManagedRouteGroupMBean exchange stats changed
> ---------------------------------------------
>
>                 Key: CAMEL-24887
>                 URL: https://issues.apache.org/jira/browse/CAMEL-24887
>             Project: Camel
>          Issue Type: Improvement
>          Components: camel-jmx
>    Affects Versions: 4.22.1
>            Reporter: Raymond
>            Assignee: Torsten Mielke
>            Priority: Minor
>
> In 4.22.1 the exchange stats in the {{ManagedRouteGroupMBean}} changed.
> As an example I have created a route group X with two routes X1 and X2. Now I 
> would like to get how many "transactions" the group processed. I used:
> {{ManagedRouteGroupMBean managedRouteGroup = 
> managedContext.getManagedRouteGroup("X");}}
> {{managedRouteGroup.getExchangesCompleted()}}
> Now when I processed 1 message/transaction I would like to get the number 1, 
> etc. This worked until 4.22.0, but this recently changed.
> In 4.22.0 the result was 1
> In 4.22.1 the result is 2
> The second is the aggregated processed exchanges of all routes in the group. 
> This is useful too, but I would still like to have the old result. This can 
> be in a separated method:
> {{managedRouteGroup.getExchangesCompleted()                --> 1}}
> {{managedRouteGroup.getAggegatedExchangesCompleted()      --> 2}}
> or
> {{managedRouteGroup.getTransactionsCompleted()            --> 1}}
> {{managedRouteGroup.getExchangesCompleted()               --> 2}}
> This may have changed after this issue: 
> https://issues.apache.org/jira/browse/CAMEL-24590



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to