allthingssecurity opened a new pull request, #28754:
URL: https://github.com/apache/camel/pull/28754

   # Description
   
   [CAMEL-26418](https://issues.apache.org/jira/browse/CAMEL-26418) (related to 
CAMEL-26330): the second part of
   `camel-chaos` from `design/chaos.adoc`, on top of the module core merged in 
#28716. It also takes in the six
   suggestions from the review of #28716.
   
   The core can fail, delay and drop sends, and fail and delay steps. This PR 
adds the rest of the first stage of the
   design:
   
   - faults at the polls of the scheduled poll consumers of the routes 
(`at=poll`: `error`, `delay`, `drop`), for a file,
     FTP or SQL route that should cope with a failing source;
   - `route=` with `corrupt` at the entry of a route, and `corrupt` at sends 
(`mode=headers`, `charset` or `truncate`);
   - `error status=<code>` (with `header.*` and `body=`) at sends to `http`, 
`https`, `undertow` and `vertx-http`. It
     raises the `HttpOperationFailedException` of camel-http-base, or returns 
the response when the endpoint does not
     throw on failure;
   - counts per route in the report (completed, failed, kept by the consumer, 
handled, redeliveries);
   - expectations (`expect=orders:100 within=2m`: met, unmet or pending) and a 
verdict with `lost`.
   
   ## The suggestions from #28716
   
   1. **Clear parse errors.** These cases now get a message that names what to 
use instead, each with a case in
      `ChaosRuleParserTest`:
      - `route=orders error`: route= does not reach the sends and steps, so use 
endpoint=, component= or step=;
      - `step=s1 drop` and `step=s1 corrupt`: the hooks that offer these faults;
      - `stall` and `lifecycle`: not supported yet, followed by the list of 
faults;
      - `action=` and `when=`: not supported yet;
      - `status=` for netty-http: not supported yet, followed by the supported 
components;
      - a misspelt target (`rout=orders error`): this parameter is not a target;
      - `expect=ord*:5`: one route id, not a pattern.
   2. **A version per report entry.** A rule is version 1 when it is added. 
`changeRule` gives the new rule the next
      version. The JSON entry has `version`, and the text shows `r2 v2 active: 
...`. This is documented in the Rules
      section. Test: `ChaosReportTest.theEntriesOfAChangedRuleDifferByVersion`.
   3. **Docs under "Where the faults are raised".** `endpoint=*` also fails the 
send to the dead letter channel, and
      `step=*` also matches the steps inside `onException` and `doCatch`. 
`ChaosStepAndErrorHandlerTest` pins both:
      - `endpoint=mock:*` fires 4 times for one exchange: the try, 2 
redeliveries and the send to the dead letter channel;
      - step rules fire at a step inside `onException` and at a step inside 
`doCatch`.
   4. **WARN in camel-main when the JAR is missing.** camel-main does not 
depend on camel-chaos for this. When
      `camel.chaos.*` options are set (from properties or Java) and chaos would 
be switched on (`enabled=true`, or
      `enabled` unset with profile `dev`; never with `prod`), camel-main looks 
up the plugin class
      `org.apache.camel.component.chaos.ChaosContextServicePlugin` by name. If 
it is missing, it logs one WARN that the
      options are ignored. Without profile `dev` or `enabled=true` chaos stays 
off anyway, and with the JAR the plugin
      says so itself. `camel run --dep` puts its JARs on the classpath while it 
creates the context, before this check
      runs. `MainChaosWithoutJarTest` (camel-main) covers seven cases:
      - `enabled=true`: WARN;
      - profile `dev`: WARN;
      - `enabled=true` from Java: WARN;
      - rules without `dev` or `enabled`: no WARN;
      - `enabled=false`: no WARN;
      - profile `prod`: no WARN;
      - no options: no WARN.
   
      `ChaosMainTest` checks that there is no WARN when the JAR is present.
   5. **Ended rules and the pattern.**
      - At each add, change or remove, the snapshot that the hooks read leaves 
out the rules that ended and the
        expectations that are decided. They stay in the report.
      - The pending expectation of an ended rule stays and keeps counting.
      - Until then, the hooks check `live(now)` before matching.
      - The `endpoint=` pattern is normalised once per rule. The URI and the 
`component=` scheme are matched in place,
        with no new strings.
      - `ChaosPatternsTest` compares the new matcher with the old substring 
matcher on 200,000 seeded random cases.
   6. **`ChaosStressTest`** now waits with `await().atMost(10, SECONDS)` 
instead of its own spin loop.
   
   ## Where this differs from the design
   
   - **Polls.** The design swaps the consumer's poll strategy while a rule is 
active. Chaos instead wraps it once per route
     consumer, before the consumer starts. The poll thread reads the plain 
field `pollStrategy`, so a swap during a poll
     could pair the `begin` of one strategy with the `rollback` of the other. 
The wrapper costs one volatile read while
     no poll rule fires. A poll `error` is thrown from `begin`. The wrapper 
passes the consumer's strategy the retry
     counter of a poll that failed by itself (0, 1, ...), so a strategy that 
retries up to a limit still stops.
     `pollEnrich` and `ConsumerTemplate` consumers belong to no route, and 
chaos does not reach them.
   - **Counts per route** come from a route policy (`onExchangeDone`), not from 
an event notifier, so each exchange counts
     once per route it goes through. Camel does not call route policies while 
the context stops. So exchanges that finish
     while the context drains are not counted, and expectations that are still 
undecided at the stop stay pending. Only
     the redeliveries come from an event notifier, which asks for no exchange 
events while there are no rules.
   - **`status=` for netty-http** is not supported yet. Its exception needs a 
Netty `HttpContent`, which I could not build
     and test without camel-netty-http on the test classpath.
   - **`corrupt`**: `charset` reads a local file as text, but skips streams, 
readers and remote files, since reading them
     would consume them. `truncate` takes an `InputStream` or a stream cache.
   
   camel-http-base (for `status=`, built by reflection) and camel-file (for the 
file consumer tests) are test
   dependencies only. No option changes, so no generated metadata. The catalog 
copy of the docs page is byte-identical.
   Size: 38 files, +4,350 / -217: about 1,700 lines of main code and 2,360 of 
tests. The earlier 3b on the unmerged
   core was +3,338; the review points added about 1,000, mostly tests.
   
   ## Cost
   
   The per-send cost is measured with the same method as #28716. It is the 
median ns per call of 1M calls of the send
   hook, with three rules that match nothing, against a normalised endpoint URI 
with `//` and a query
   (`mock://orders?retainFirst=0`). Before is the merged 3a; after is this PR:
   
   | | before | after |
   |---|---|---|
   | 3 rules that match nothing | 165 | 64 |
   | the same, plus 3 rules that ended by count | 353 | 62 |
   
   Most of the gain is in matching: there is no copy of the URI without its 
`//`, and no substring for the query.
   
   Per route call through `ProducerTemplate`, median ns of 3 runs (no JAR / 
chaos off / on without rules / on with three
   rules that match nothing):
   
   | route | before | after |
   |---|---|---|
   | empty route | 439 / 425 / 441 / 487 | 437 / 419 / 452 / 535 |
   | one `to` | 627 / 622 / 649 / 755 | 632 / 640 / 677 / 783 |
   | recipient list of two | 1866 / 1833 / 1898 / 2033 | 1870 / 1856 / 1936 / 
2117 |
   
   The machine load was around 7 during these runs, so differences of 10-20 ns 
are noise.
   
   What this PR adds per exchange:
   - with chaos on and no rules: one volatile read at the begin and at the done 
of each route, in its route policy;
   - with rules: the counts per route (a map lookup and a `LongAdder` per 
exchange and route). That is the 30-80 ns in
     the last column.
   
   The poll strategy reads one volatile field per poll while no poll rule fires 
(plus one more at commit, until an
   error rule first fails a poll of that consumer).
   
   ## Tests
   
   - camel-chaos: 254 tests in 18 classes, no `Thread.sleep`. The new tests are 
in:
     - `ChaosHttpFaultTest`;
     - `ChaosPollFaultsTest` and `ChaosPollStrategyTest`, including a real 
`file:` consumer, the retry counter seen by a
       retrying strategy, concurrent poll runs, and suspend during a delay;
     - `ChaosEntryFaultsTest`;
     - `ChaosRouteCountsTest`;
     - `ChaosConsumersTest`;
     - the expectation, verdict and version tests in `ChaosReportTest`;
     - the cases of the six suggestions listed above.
   - camel-main: `MainChaosWithoutJarTest` (7), 
`ChaosConfigurationPropertiesTest` and `MainNotAutoConfiguredWarningTest`
     pass.
   - Repeats: each camel-chaos test class ran 20 times on its own, one JVM per 
run: 360 runs, 0 failures.
     `MainChaosWithoutJarTest` also ran 20 times, 0 failures.
   - Negative controls: 21 mutants of camel-chaos and 4 of the camel-main check 
(one per behaviour above, from the prod
     switch to the WARN conditions), each caught by at least one test.
   
   ## Open questions
   
   - An expectation stays pending for good when its rule is changed or removed 
first, and then `isFinal()` stays false.
     The design says so, because the messages may still arrive. But a tool that 
changes rules in a loop would never see a
     final report. Should an expectation that was abandoned this way stop 
counting against `isFinal()`, as long as it was
     not abandoned by the context stopping?
   - The WARN for a missing JAR is in camel-main only. Should the Spring Boot 
and Quarkus starters get the same check?
   - PR 4 (CLI/MCP) will show id and version, and change/remove act on the 
current version of an id. Is that the shape
     you want for the tools?
   
   # Target
   
   - [x] I checked that the commit is targeting the correct branch (Camel 4 
uses the `main` branch)
   
   # Tracking
   - [x] If this is a large change, bug fix, or code improvement, I checked 
there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL-26418) 
filed for the change (usually before you start working on it): 
[CAMEL-26418](https://issues.apache.org/jira/browse/CAMEL-26418).
   
   # Apache Camel coding standards and style
   
   - [x] I checked that each commit in the pull request has a meaningful 
subject line and body.
   - [ ] I have run `mvn clean install -DskipTests` locally from root folder 
and I have committed all auto-generated changes.
     (I built the core modules, camel-main and the module with the formatter 
and impsort. The catalog copy of the docs
     page is byte-identical to its source. I did not run the full root build.)
   
   # AI-assisted contributions
   
   - [x] If this PR includes AI-generated code, commits have proper 
co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR 
description identifies the AI tool used.
     This PR was prepared with Claude Code (Claude Opus 5.5). The commit 
carries a `Co-Authored-By` trailer.
   
   _Claude Code on behalf of allthingssecurity_
   
   🤖 Generated with [Claude Code](https://claude.com/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