[
https://issues.apache.org/jira/browse/CAMEL-26667?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen reassigned CAMEL-26667:
-----------------------------------
Assignee: Claus Ibsen
> camel-chaos - findings from the hands-on round of stage 1: report accuracy,
> feedback on rules, live rule state, new faults
> --------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-26667
> URL: https://issues.apache.org/jira/browse/CAMEL-26667
> Project: Camel
> Issue Type: Improvement
> Components: camel-jbang
> Reporter: Claus Ibsen
> Assignee: Claus Ibsen
> Priority: Major
> Fix For: 4.24.0
>
>
> h2. Hands-on round 1 of camel-chaos (stage 1): findings and ideas
> Stage 1 of camel-chaos (CAMEL-26330) was tried for real with {{camel run
> --chaos}}, driven from the Camel CLI ({{camel cmd chaos-*}}, {{camel get
> chaos}}) and from the MCP tools ({{camel_runtime_chaos_*}}), against a small
> app: a timer route {{orders}} with redelivery (2 attempts, 100ms) and a dead
> letter channel to {{seda:dlq}}, an HTTP call to a local {{platform-http}}
> route {{stock}}, a {{direct}} route with named steps, a {{seda}} hop to
> {{shipping}} and a file poller.
> Every hook works as documented: HTTP {{status=503}} faults with redelivery
> and the DLC, {{step=}} error and delay, {{at=poll drop}}, {{route=}}
> {{corrupt}} at the entry, {{stall}} and {{lifecycle action=stop}},
> expectations, the shutdown report, and the parser refuses wrong rules with
> clear reasons that say what to use instead. This issue collects what felt
> wrong or missing while using it, as input before stage 2 (CAMEL-26607) and
> the chaos screen (CAMEL-26608).
> h3. Usability problems
> # *An expectation can be met while every message went to the DLQ.*
> {{expect=orders:20}} with {{endpoint=http://...stock* error status=503}}
> (every send fails) is *met*: a handled exchange counts as completed (as
> documented). The honest check was {{expect=shipping:20}} on the downstream
> route (FAIL: "route shipping completed 0 of 20 exchanges within 8s: 20
> lost"). Users and AI agents will write the first form.
> # *No expectation on the error handler itself.* "N messages should end in the
> DLQ" works only when a route consumes the DLQ ({{expect=dlq:5}} with
> {{seda:dlq}} -> route {{dlq}}). It cannot be said for a DLC to {{log:}},
> {{mock:}} or {{kafka:dlq}}, and the most common resilience check, "no message
> reaches the DLQ" (at most 0), cannot be said at all. And when such an
> expectation is unmet, the report says "5 lost", while the 5 messages were in
> fact recovered by redelivery: the wording is the opposite of what happened.
> The per-route {{handled}} count is already in the report, so see idea 1.
> # *A message lost inside the dead letter channel is not reported.*
> {{component=seda error until=6}} also failed the send to {{seda:dlq}}; with
> {{deadLetterHandleNewException}} (the default) that exchange counts as
> handled, but never reached the DLQ ({{orders handled 11}}, {{dlq completed
> 10}}, a WARN from DeadLetterChannel). The report says {{lost: 0}} for it. The
> docs warn about {{endpoint=*}} and {{component=}} hitting the DLC, but chaos
> knows the send is the DLC send (the failure endpoint) and could report it
> ("r6 failed the send to the dead letter channel of route orders: 1 message
> gone"), or skip DLC sends unless asked for ({{at=dlq}}).
> # *A rule that matches nothing gives no feedback.*
> {{endpoint=htp://localhost:8765/stock*}} (typo) and {{step=callStok}} were
> accepted with only an id, and fired 0 times. The expectation
> {{expect=shiping:10}} (no such route) logs a WARN in the application log, but
> the CLI and the MCP result only return {{\{"id":"r3"\}}}, so an AI agent
> never sees it. Suggest: {{chaos-add}} / {{chaos-change}} return warnings:
> "matches no endpoint / step / route of the context now", "expects route
> shiping, which does not exist", and the MCP tools pass them through.
> # *{{chaos-clear}} does not start a fresh experiment.* After clear the old
> FAIL verdict, {{lost}} and the history stay until the application restarts.
> Suggest {{chaos-clear --reset}} (or {{chaos-reset}}) that also resets the
> report, the route counts and the verdict (a new "session", optionally a new
> seed).
> # *A removed rule with a pending expectation keeps the report not final for
> good.* Removing the typo rule {{r3}} (pending expectation) made the report
> "FAIL (not final: an expectation is pending)" for the rest of the run. As
> documented, but in practice a removed rule's expectation should become
> {{cancelled}} (not counted, not pending).
> # *"skipped" has no reason.* {{route=shipping corrupt mode=charset}} was
> skipped 79 of 79 times: the body after the HTTP call is a stream cache, which
> {{charset}} does not change ({{mode=truncate}} worked, the body became
> {{in-s}}). Neither the CLI table, the JSON, nor the dev console say why, so
> it looks like it worked. Suggest a count per skip reason ({{skipped:
> \{"stream body (charset)": 79\}}}), and a hint in the table.
> # *Minor:* the JSON report lists the versions of one id as 3, 1, 2; a rule on
> {{direct:}} lists {{hooks: ["send","poll"]}} although direct has no polls
> (the hooks the target offers, not what applies).
> h3. Live rule state (for the TUI chaos screen, CAMEL-26608)
> Today a running rule shows {{matches}}, {{fired}} (per hook in JSON),
> {{skipped}}, {{ends in}}, and a pending expectation {{completed}} / {{time
> left}}. For "fail 5%" that is {{117 matches, 6 fired}}. What a person (and
> the chaos screen) needs to see while it runs:
> * the observed rate next to the target ({{5.1% of 5%}}), and for {{every:N}}
> the next match that fires;
> * when it last fired, and a short ring buffer of the last faults: time,
> exchange id, route, endpoint/step, fault (so a fault can be matched to a DLQ
> entry, a trace or the message history);
> * the effect of the rule, not only its faults: of the exchanges it faulted,
> how many were recovered by redelivery, handled by the DLC, failed, still in
> flight. In the run, {{r7}} (stop {{stock}} for 3s) sent 5 orders to the DLQ,
> because 2 x 100ms redelivery is too short for a 3s outage: today the report
> shows {{orders handled +5}} but not that r7 caused it;
> * a rate over time (faults per second over the last minute) for a sparkline;
> * mark the exchange: a faulted exchange gets a property (e.g.
> {{CamelChaosRule=r1}}) so the tracer, message history, the DLQ route and
> {{camel get history}} show "faulted by chaos rule r1".
> h3. Ideas for new rules and features
> # *Expectations on the error handler:* {{expect=orders:handled<=0}}
> (redelivery must recover everything), {{expect=orders:handled=5}},
> {{expect=orders:redeliveries<=30}}, {{expect=orders:failed=0}}; the count
> kinds already exist per route.
> # *Error after the send* ({{at=reply}} or {{after=true}}): the producer is
> called, then the send fails, as a timeout after the remote side did the work;
> the classic duplicate test for idempotent consumers and retries (a payment
> that is retried after it went through).
> # *Duplicate:* {{endpoint=kafka:orders* duplicate}} sends the message twice
> (at-least-once delivery), to test the idempotent consumer.
> # *Burst / outage windows:* {{rate=burst:5s/30s}} fails everything for 5s
> every 30s, closer to a real outage than an even rate, and what circuit
> breakers react to.
> # *Slow then fail:* {{delay duration=30s}} combined with an error ({{error
> after=30s}}), a timeout that happens after a long wait, which tests the
> timeouts of the route.
> # *Reply corrupt* at the send: change the reply of the endpoint ({{corrupt
> at=reply mode=truncate}}), not only the request.
> h3. From reading the code (each traced end to end; 2 and 3 also confirmed on
> the running app)
> # *A route is counted once per pass, not once per exchange.*
> {{ChaosRoutePolicyFactory}} counts in {{onExchangeDone}}, and the route
> policy advice runs each time the route processor runs. An exchange that
> enters a {{direct}} route 3 times (a {{loop(3).to("direct:sub")}}, or a
> caller that redelivers {{to("direct:sub")}} while {{sub}} has
> {{noErrorHandler()}}) counts 3 completed or failed exchanges in {{sub}},
> completes {{expect=sub:30}} with 10 messages, and makes a {{route=sub
> corrupt}} rule fire and use up its {{until=}} count 3 times. chaos.adoc says
> "each counted once when the route is done with it". Fix: count once per
> exchange and route (a marker on the exchange), or document "per pass".
> # *{{exception=java.lang.InterruptedException}} bypasses all error handling.*
> {{AbstractExchange.setException}} marks the exchange interrupted, so Camel
> stops routing it: no {{onException}}, no redelivery, no DLC. Confirmed:
> {{step=enrich error exception=java.lang.InterruptedException until=3}} gave 3
> failed exchanges in {{orders}}, 0 redeliveries, nothing in the DLQ, only a
> WARN of the timer consumer. Fix: reject it (and its subclasses) in the
> parser, or wrap it.
> # *More rules that can never fire are accepted silently* (adds to problem 4):
> {{component=http:localhost}} (a {{component=}} pattern is matched against the
> scheme only, so a {{:}} never matches; confirmed, 0 matches), {{rate=every:10
> until=5}} (fires 0 times), and {{message=}} with an exception class that has
> only a no-argument constructor (the message is dropped). Fix: reject the
> first two, reject or warn on the third.
> # *Rules from {{camel.chaos.rules}} start their clock when the context
> initializes, not when it has started.* {{addedAt}} is set in
> {{onContextInitializing}}, so {{until=}}, {{within=}} and the lifecycle times
> include the startup. {{expect=orders:100 within=1m}} with routes that take
> 20s to start (a slow broker connect, the supervising route controller) leaves
> 40s. Fix: re-base the times of the startup rules when the context has started.
> # *The report stays not final after a change too* (adds to problem 6): a
> frozen expectation of a changed or replaced rule, or one frozen by a context
> restart, reads as pending, and {{final}} includes the history, so one
> {{chaos-change}} of a rule with an undecided {{expect=}} keeps
> {{final=false}} for the rest of the process. An MCP agent loop that waits for
> {{final}} never ends. Fix: leave frozen expectations out of {{final}}.
> # *The CLI text hides the lifecycle state, and an empty result says nothing.*
> {{camel get chaos}} and {{--report}} do not show the {{lifecycle}} object
> (down, back in 12s, skipped: route was stopped, left, cancelled): a {{stall}}
> row shows only {{active 1 1 0}}, and the reason only appears with {{--json}}.
> With no rules, or without camel-chaos on the classpath, {{camel get chaos}}
> prints nothing and exits 0. Fix: a LIFECYCLE column (state plus back-in or
> reason), and a "no chaos rules" / "camel-chaos is not on the classpath" line.
> # *Doc nit:* chaos.adoc says {{\"}}, {{\'}} and {{\\}} are escapes inside
> quotes, but only the quote that opened the value and {{\\}} are:
> {{message="it\'s"}} keeps the backslash.
> More ideas from the code read, on top of the list above: a jittered delay
> from the seeded sequence ({{step=enrich delay duration=100ms..2s rate=0.3}});
> a start offset ({{endpoint=kafka:orders* error from=+30s until=1m}}, "the
> broker dies after warm-up", which also avoids the startup clock problem); a
> flapping route ({{route=poller stall duration=10s repeat=3 every=1m}}, for
> reconnect, back-off and supervision); more corrupt modes ({{mode=empty}},
> {{mode=remove-header header=JMSCorrelationID}}, {{truncate}} for String and
> byte[] bodies, which today are skipped). {{SendHook.afterSend}} is already
> there and unused, so the "error after the send" and "corrupt the reply" ideas
> fit the SPI as it is; {{duplicate}} needs a "send again" result in the
> SendHook SPI, and "slow then fail" needs a delay and an error to combine
> (today the first rule that fires decides).
> h3. Not in this issue
> Route-entry errors, a "sends/steps of route X" filter, {{when=}} conditions
> and loose AI prompts are stage 2 (CAMEL-26607). Claus's test prompt from the
> design (2026-10-27) "the order route should have chaos, by failing 5% or when
> a message origin country is spain" was tried through MCP: {{route=orders
> error rate=0.05}} is refused (pointing to endpoint=/step=), the 5% works as
> {{endpoint=direct:inventory error rate=0.05}} (6 of 117), and
> {{when="$\{header.country\} == 'ES'"}} is refused with "not supported yet".
> So the AI needed to look up the first send of the route itself: that is the
> case for the stage 2 filter.
> _Claude Code on behalf of Claus Ibsen_
--
This message was sent by Atlassian Jira
(v10.3.26#10030026)