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

   ## Description
   
   [CAMEL-25660](https://issues.apache.org/jira/browse/CAMEL-25660): with 
`enableDeadLettering=true` in PEEK_LOCK mode, 
`ServiceBusConsumer.ConsumerOnCompletion.onFailure` dead-letters a failed 
message with a reason and description built from the route's exception. Two 
problems, the second the more serious.
   
   ### The call was unguarded
   
   ```java
   try {
       ...
       messageContext.deadLetter(deadLetterOptions);
       ...
   } finally {
       pendingExchanges.decrementAndGet();
   }
   ```
   
   No `catch`. When the broker rejects the request, the exception escapes 
`onFailure` into the Azure SDK's processor callback, where nothing reports it 
through the consumer's `ExceptionHandler`. The message stays locked, the lock 
expires, Service Bus redelivers it, the route fails the same way — until 
`maxDeliveryCount` is reached and the broker dead-letters it with its own 
`MaxDeliveryCountExceeded` reason. So the diagnostics `enableDeadLettering` 
exists to provide are exactly what gets lost, and the exchange is processed 
several more times than intended.
   
   ### Neither field was bounded
   
   To be clear, the *content* is what Azure asks for. [The dead-letter queue 
documentation](https://learn.microsoft.com/en-us/azure/service-bus-messaging/service-bus-dead-letter-queues)
 says:
   
   > We recommend that you include the type of the exception in the 
`DeadLetterReason` and the stack trace of the exception in the 
`DeadLetterDescription` as it makes it easier to troubleshoot the cause of the 
problem resulting in messages being dead-lettered. It might result in some 
messages exceeding the 256 KB quota limit for the Standard tier of Azure 
Service Bus.
   
   The component followed the recommendation but not the caveat. A full Camel 
stack trace, with error-handler and processor frames, is routinely several 
thousand characters, and `cause.getMessage()` is itself unbounded — a database 
error quoting the statement, a validation report. 
`com.azure.messaging.servicebus.models.DeadLetterOptions` does no client-side 
length validation (checked against azure-messaging-servicebus 7.17.16), so the 
broker is what rejects it, landing on the problem above.
   
   ## Change
   
   - Cap the reason at 1024 characters and the description at 8192, via 
`StringHelper.limitLength` with a trailing `...` so a reader of the dead-letter 
queue can tell the text was cut rather than that the trace ended there. The 
head of a stack trace carries the cause; the tail is error-handler frames.
   - Guard the dead-letter call and report a rejection through the consumer's 
exception handler.
   
   `onComplete` has the same unguarded shape around 
`messageContext.complete()`. I left it alone — it is a different failure (a 
lost lock rather than an oversized payload) and wants its own decision. Happy 
to fold it in if reviewers prefer.
   
   ## Tests
   
   Two tests added to `ServiceBusConsumerTest`, using the 
`handoverCompletions()` seam the existing dead-letter tests already use:
   
   - `synchronizationBoundsTheDeadLetterReasonAndDescription` — an exception 
with a 4096-character message and 512 stack frames; asserts both fields are 
within their caps and end with the ellipsis.
   - `synchronizationReportsARejectedDeadLetterInsteadOfThrowing` — 
`messageContext.deadLetter(any())` is stubbed to throw; asserts `onFailure` 
does not propagate it and the exception handler receives it.
   
   Revert-checked, each half separately:
   
   | reverted | result |
   |---|---|
   | the bounding | `synchronizationBoundsTheDeadLetterReasonAndDescription` 
fails on all 3 surefire runs |
   | the guard | `synchronizationReportsARejectedDeadLetterInsteadOfThrowing` 
errors on all 3 runs with `IllegalStateException: Dead-lettering rejected` 
propagating out of `onFailure` |
   
   | Module | Tests |
   |---|---|
   | camel-azure-servicebus | 76 (8 skipped) |
   
   No option metadata changed, so nothing needed regenerating. I did not run 
the full reactor; relying on CI for that.
   
   ## Docs
   
   Upgrade guide 4.24: the two caps, the ellipsis marker, and why — including 
that a rejected request used to leave the message to be redelivered.
   
   ## What is not verified
   
   Not reproduced against a live namespace: the exact payload size at which the 
broker rejects the request has not been measured, and Azure documents a 
message-size quota rather than a per-field limit. The caps are therefore a 
conservative choice rather than a protocol constant. The unguarded-call half 
does not depend on any of that — it is verified in Camel's own code and by the 
revert run above.
   
   An earlier version of CAMEL-25660 cited a 4096-character limit on these 
fields, from the .NET `DeadLetterMessageAsync` API reference. That is not 
corroborated by the dead-letter page or the quotas page, so I retracted it on 
the issue; the documented constraint is the 256 KB message quota quoted above.
   
   🤖 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