Skip to content

[#2602] defer prefetch until connection is started - #2605

Draft
mattrpav wants to merge 2 commits into
apache:mainfrom
mattrpav:amq-gh-2602-defer-prefetch-until-started
Draft

mattrpav wants to merge 2 commits into
apache:mainfrom
mattrpav:amq-gh-2602-defer-prefetch-until-started

Conversation

@mattrpav

@mattrpav mattrpav commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

Per the Jakarta specification, a connection that hasn't been started should not receive dispatched messages. Currently, ActiveMQ will dispatch messages for prefetch before the connection has been started.

The Jakarta specification has a test where consumers are mixed and one is not started on purpose. Currently, ActiveMQ would fail that test as messages are dispatched to a connection that has not been started.

This change provides configurable option to have prefetch wait until the connection has been explicitly started.

…t starts

The broker has no notion of connection started state: it dispatches into any
registered consumer's prefetch, and a never-started client just holds those
messages in its dispatch channel. A competing consumer on a never-started
connection therefore steals round-robined queue messages that no one will
ever deliver (seen as the TCK core20 queueReceiveTests hang).

With deferPrefetchUntilStarted enabled on the connection factory, queue
consumers created before their connection has ever started register with
prefetch zero, which the broker treats as no push credit. Connection.start()
restores the configured prefetch through a ConsumerControl, and the broker's
existing processConsumerControl path re-credits the subscription and wakes
the destination. Pull sends and the async-consumer prefetch guard treat a
deferred consumer by its configured value.

A deferred consumer carries prefetch zero on the wire but sends no
MessagePull, so it must not take the pull-consumer branch of
receive(timeout)/receiveNoWait, which waits for the broker to signal the
pull timeout that never comes. The pull-mode decision goes through
isPullConsumer(), which excludes deferred consumers, so they use client-side
timeouts like any push consumer (TCK core/queueConnection
connNotStartedQueueTest). Pinned by UnstartedConnectionQueueDispatchTest.

The flag is copied to the connection in configureConnection(), so factory
subclasses that construct their own connection type inherit it.

Opt-in (default false) to preserve the pre-start prefetch warmup behavior
some applications rely on.
JmsTool creates competing queue consumers on connections it never starts,
which parked round-robined messages in a held dispatch channel and hung
core20/jmsconsumertests queueReceiveTests and core/queueConnection
connNotStartedQueueTest. Once the shared-subscription TCK changes land,
the queueReceiveTests entry they add to ts.jtx is no longer needed.
@mattrpav mattrpav added this to the Jakarta 3.1/JMS 2.0 support milestone Sep 22, 2026
@mattrpav mattrpav self-assigned this Sep 22, 2026
@mattrpav
mattrpav marked this pull request as draft September 22, 2026 19:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant