Summary
The JDBC dead-letter queue implementation appears to page claimable sequence heads differently from the JPA implementation, in a way that may skip or distort candidate coverage beyond the first page.
JDBC behavior observed
JdbcSequencedDeadLetterQueue.process(predicate, processingTask) calls findClaimableSequences(10).
PagingJdbcIterable advances pages using page * pageSize, and DefaultDeadLetterStatementFactory.claimableSequencesStatement(...) uses that value as:
while also ordering by:
The resulting SQL shape is effectively:
SELECT *
FROM DeadLetterEntry dl
WHERE dl.processingGroup = ?
AND dl.sequenceIndex >= ?
AND dl.sequenceIndex = (
SELECT MIN(dl2.sequenceIndex)
FROM DeadLetterEntry dl2
WHERE dl2.processingGroup = dl.processingGroup
AND dl2.sequenceIdentifier = dl.sequenceIdentifier
)
AND (
dl.processingStarted IS NULL
OR dl.processingStarted < ?
)
ORDER BY dl.lastTouched ASC
LIMIT ?
Why this looks problematic
sequenceIndex is sequence-local, not globally monotonic across a processing group.
That means paging with sequenceIndex >= offset does not appear consistent with a result set ordered by lastTouched ASC.
For example:
- page 1 uses
sequenceIndex >= 0
- page 2 uses
sequenceIndex >= 10
But page 2 is then not "the next 10 oldest sequence heads by lastTouched".
Instead, it filters out any sequence head whose minimum sequenceIndex is below 10, even if that head was never part of page 1.
Comparison with JPA
The JPA implementation appears to page the ordered result set directly:
select dl
from DeadLetterEntry dl
where dl.processingGroup = :processingGroup
and dl.sequenceIndex = (
select min(dl2.sequenceIndex)
from DeadLetterEntry dl2
where dl2.processingGroup = dl.processingGroup
and dl2.sequenceIdentifier = dl.sequenceIdentifier
)
and (dl.processingStarted is null or dl.processingStarted < :processingStartedLimit)
order by dl.lastTouched asc
This matches the expected semantics much more closely.
Potential risk
This may:
- distort coverage beyond the first page,
- skip valid sequence heads in later pages,
- or make predicate-based processing fairness/throughput worse than expected.
This becomes especially visible when a custom predicate is used and the first page contains many non-processable sequence heads.
Question
Is this intentional in the JDBC implementation, or should JDBC paging be aligned with the JPA behavior for claimable sequence selection?
Summary
The JDBC dead-letter queue implementation appears to page claimable sequence heads differently from the JPA implementation, in a way that may skip or distort candidate coverage beyond the first page.
JDBC behavior observed
JdbcSequencedDeadLetterQueue.process(predicate, processingTask)callsfindClaimableSequences(10).PagingJdbcIterableadvances pages usingpage * pageSize, andDefaultDeadLetterStatementFactory.claimableSequencesStatement(...)uses that value as:sequenceIndex >= ?while also ordering by:
The resulting SQL shape is effectively:
Why this looks problematic
sequenceIndexis sequence-local, not globally monotonic across a processing group.That means paging with
sequenceIndex >= offsetdoes not appear consistent with a result set ordered bylastTouched ASC.For example:
sequenceIndex >= 0sequenceIndex >= 10But page 2 is then not "the next 10 oldest sequence heads by lastTouched".
Instead, it filters out any sequence head whose minimum
sequenceIndexis below 10, even if that head was never part of page 1.Comparison with JPA
The JPA implementation appears to page the ordered result set directly:
This matches the expected semantics much more closely.
Potential risk
This may:
This becomes especially visible when a custom predicate is used and the first page contains many non-processable sequence heads.
Question
Is this intentional in the JDBC implementation, or should JDBC paging be aligned with the JPA behavior for claimable sequence selection?