Failed client-Java listener delivery is retried, not lost - #6832
Open
TIVMOF wants to merge 14 commits into
Open
Conversation
…t lost Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # components/engine/engine-java/src/test/java/org/eclipse/dirigible/engine/java/listener/ListenerClassConsumerTest.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR:
Problem
The client-Java listener path — which carries all intent glue — had no retry, no redelivery and no dead letter.
ListenerClassConsumer.dispatchcaught the handler's exception and never rethrew it, so the broker saw every failed delivery as a success, acknowledged the message and dropped the event permanently.That also stranded handlers whose correctness depends on a second delivery.
Posting.java.templatedocuments and implements an idempotent-and-resumable contract: a redelivery of a half-written post clears the partial rows and rebuilds the set. The code is correct, but its one precondition was redelivery, so the repair could never run — a journal header with 2 of 5 lines stayed unbalanced forever.Change
Rethrow after reporting in
ListenerClassConsumer.dispatch. This is the half that actually fixes it:dispatcher.onError(...)still runs first, then the failure leaves the listener where the broker can act on it.Bound the retries by configuring a redelivery policy on the subscription. Worth being precise about what this does — the rethrow alone already yields redelivery under ActiveMQ's built-in defaults; this makes the Java path use the same budget as the JavaScript path (1s initial, 5s delay, exponential backoff, max 3) rather than a different one.
Rather than copy that policy into
engine-java, it moves ontoActiveMQConnectionArtifactsFactory— the collaborator both paths already inject — withListenerManagerdelegating. The retry budget is now one number in one place, andengine-javanever names an ActiveMQ type.What this deliberately does not change
The session stays
AUTO_ACKNOWLEDGE. Both paths call the samecreateSession(false, AUTO_ACKNOWLEDGE)—ListenerManager:83andListenerClassConsumer:269— so the session mode cannot be what separated the working path from the broken one. For an asynchronous listener, ActiveMQ acknowledges only afteronMessagereturns, which is exactly why the JS path retries on the same session. Making sessions transacted would have changed error and threading semantics for every listener, including the ones already working, for no benefit.The text-read failure path still returns and drops the message. A body that can't be read is likely poison and arguably belongs in the DLQ, but that's out of scope here.
Testing
Three new tests, verified red-first: with both changes stripped they fail with
Expected java.lang.Exception to be thrown, but nothing was thrown— precisely the swallow — while the pre-existing tests kept passing. They assert the failure escapesonMessage, that the original exception survives as the cause rather than being flattened to a message, thatonErrorstill runs for the failed attempt, and that the subscription configures its redelivery policy.Green after restore:
ListenerClassConsumerTest5/5, plusListenersManagerTestandAsynchronousMessageListenerTestconfirming theListenerManagerrefactor left the JavaScript path intact.🤖 Generated with Claude Code