Resolution: This is documented in EMS User's Guide:
----------------------
If both prefetch and
maxRedelivery are set to a non-zero value, then there is a potential to lose prefetched messages if one of the messages exceeds the
maxRedelivery limit. For example,
prefetch=5 and
maxRedelivery=4. The first message is redelivered 4 times, hits the
maxRedelivery
limit and is sent to the undelivered queue (as expected). However, the
other 4 pre-fetched messages are also sent to the undelivered queue and
are not processed by the receiving application. The work around is to
set
prefetch=none, but this can have performance implications on large volume interfaces.
------------------
The EMS server
maintains an exact redelivery count for queues with a positive prefetch
count. The server and clients mark messages as redelivered (and increase
the message delivery count) only after explicitly generated rollback
events. These events include calls to session.recover or
session.rollback, or closed connections. In the event of an application
crash, the exact count is not guaranteed.
Refer to EMS User's Guide regarding property "maxRedelivery":
------------
In the event of an abrupt exit by the client, the maxRedelivery
count can be mistakenly incremented. An abrupt exit prevents the client
from communicating with the server; for example, when the client exits
without closing the connection or when the client application crashes.
If a client application exits abruptly, the EMS server counts all
messages sent to the client as delivered, even if they were not
presented to the application.
--------------
The following example demonstrates how message loss could happen:
- Queue properties: prefetch=5, maxRedelivery=4
- Messages has JMS_TIBCO_PRESERVE_UNDELIVERED set to true
- Consumer uses EXPLICIT_CLIENT acknowledge mode but does not acknowledge the message after receiving it.
If the application is terminated (i.e crtl-C) for 4 times,
though only the first message is received through receive() call by the application for 4 times, all 5 messages are moved to $sys.undelivered.
If the connection is explicitly closed, there is no message loss. All messages are only moved to $sys.undelivered when their maxRedelivery
is exceeded.