|
172 | 172 | * @author Christian Fredriksson |
173 | 173 | * @author Timofey Barabanov |
174 | 174 | * @author Janek Lasocki-Biczysko |
| 175 | + * @author Su Ko |
175 | 176 | */ |
176 | 177 | public class KafkaMessageListenerContainer<K, V> // NOSONAR line count |
177 | 178 | extends AbstractMessageListenerContainer<K, V> implements ConsumerPauseResumeEventPublisher { |
@@ -843,6 +844,8 @@ private final class ListenerConsumer implements SchedulingAwareRunnable, Consume |
843 | 844 |
|
844 | 845 | private @Nullable ConsumerRecords<K, V> remainingRecords; |
845 | 846 |
|
| 847 | + private @Nullable ConsumerRecords<K, V> lastRecords; |
| 848 | + |
846 | 849 | private boolean pauseForPending; |
847 | 850 |
|
848 | 851 | private boolean firstPoll; |
@@ -1532,7 +1535,7 @@ private void doProcessCommits() { |
1532 | 1535 | private void invokeIfHaveRecords(@Nullable ConsumerRecords<K, V> records) { |
1533 | 1536 | if (records != null && records.count() > 0) { |
1534 | 1537 | this.receivedSome = true; |
1535 | | - savePositionsIfNeeded(records); |
| 1538 | + saveLastRecordsIfNeeded(records); |
1536 | 1539 | notIdle(); |
1537 | 1540 | notIdlePartitions(records.partitions()); |
1538 | 1541 | invokeListener(records); |
@@ -1604,50 +1607,59 @@ private void notIdle() { |
1604 | 1607 | } |
1605 | 1608 | } |
1606 | 1609 |
|
1607 | | - private void savePositionsIfNeeded(ConsumerRecords<K, V> records) { |
| 1610 | + private void saveLastRecordsIfNeeded(ConsumerRecords<K, V> records) { |
1608 | 1611 | if (this.fixTxOffsets) { |
1609 | | - this.savedPositions.clear(); |
1610 | | - records.partitions().forEach(tp -> this.savedPositions.put(tp, this.consumer.position(tp))); |
| 1612 | + this.lastRecords = records; |
1611 | 1613 | } |
1612 | 1614 | } |
1613 | 1615 |
|
1614 | 1616 | private void fixTxOffsetsIfNeeded() { |
1615 | | - if (this.fixTxOffsets) { |
1616 | | - try { |
1617 | | - Map<TopicPartition, OffsetAndMetadata> toFix = new HashMap<>(); |
1618 | | - this.lastCommits.forEach((tp, oamd) -> { |
1619 | | - long position = this.consumer.position(tp); |
1620 | | - Long saved = this.savedPositions.get(tp); |
1621 | | - if (saved != null && saved != position) { |
1622 | | - this.logger.debug(() -> "Skipping TX offset correction - seek(s) have been performed; " |
1623 | | - + "saved: " + this.savedPositions + ", " |
1624 | | - + "committed: " + oamd + ", " |
1625 | | - + "current: " + tp + "@" + position); |
1626 | | - return; |
1627 | | - } |
1628 | | - if (position > oamd.offset()) { |
1629 | | - toFix.put(tp, createOffsetAndMetadata(position)); |
1630 | | - } |
1631 | | - }); |
1632 | | - if (!toFix.isEmpty()) { |
1633 | | - this.logger.debug(() -> "Fixing TX offsets: " + toFix); |
1634 | | - if (this.kafkaTxManager == null) { |
1635 | | - commitOffsets(toFix); |
1636 | | - } |
1637 | | - else { |
1638 | | - Objects.requireNonNull(this.transactionTemplate).executeWithoutResult( |
1639 | | - status -> doSendOffsets(getTxProducer(), toFix)); |
1640 | | - } |
1641 | | - } |
1642 | | - } |
1643 | | - catch (Exception e) { |
1644 | | - this.logger.error(e, () -> "Failed to correct transactional offset(s): " |
1645 | | - + ListenerConsumer.this.lastCommits); |
| 1617 | + if (!this.fixTxOffsets) { |
| 1618 | + return; |
| 1619 | + } |
| 1620 | + |
| 1621 | + try { |
| 1622 | + if (this.lastRecords == null) { |
| 1623 | + this.logger.trace(() -> "No previous records available for TX offset fix."); |
| 1624 | + return; |
1646 | 1625 | } |
1647 | | - finally { |
1648 | | - ListenerConsumer.this.lastCommits.clear(); |
| 1626 | + |
| 1627 | + Map<TopicPartition, OffsetAndMetadata> nextOffsets = this.lastRecords.nextOffsets(); |
| 1628 | + Map<TopicPartition, OffsetAndMetadata> toFix = new HashMap<>(); |
| 1629 | + |
| 1630 | + nextOffsets.forEach((tp, nextOffset) -> { |
| 1631 | + OffsetAndMetadata committed = this.lastCommits.get(tp); |
| 1632 | + |
| 1633 | + if (committed == null) { |
| 1634 | + this.logger.debug(() -> "No committed offset for " + tp + "; skipping TX offset fix."); |
| 1635 | + return; |
| 1636 | + } |
| 1637 | + |
| 1638 | + if (!this.lastRecords.records(tp).isEmpty() && nextOffset.offset() > committed.offset()) { |
| 1639 | + toFix.put(tp, nextOffset); |
| 1640 | + } |
| 1641 | + }); |
| 1642 | + |
| 1643 | + if (!toFix.isEmpty()) { |
| 1644 | + this.logger.debug(() -> |
| 1645 | + String.format("Fixing TX offsets for %d partitions: %s", toFix.size(), toFix)); |
| 1646 | + if (this.kafkaTxManager == null) { |
| 1647 | + commitOffsets(toFix); |
| 1648 | + } |
| 1649 | + else { |
| 1650 | + Objects.requireNonNull(this.transactionTemplate) |
| 1651 | + .executeWithoutResult(status -> doSendOffsets(getTxProducer(), toFix)); |
| 1652 | + } |
1649 | 1653 | } |
1650 | 1654 | } |
| 1655 | + catch (Exception e) { |
| 1656 | + this.logger.error(e, () -> "Failed to correct transactional offset(s): " |
| 1657 | + + ListenerConsumer.this.lastCommits); |
| 1658 | + } |
| 1659 | + finally { |
| 1660 | + ListenerConsumer.this.lastCommits.clear(); |
| 1661 | + this.lastRecords = null; |
| 1662 | + } |
1651 | 1663 | } |
1652 | 1664 |
|
1653 | 1665 | private @Nullable ConsumerRecords<K, V> doPoll() { |
|
0 commit comments