Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import org.apache.kafka.common.PartitionInfo;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.errors.WakeupException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.Duration;
import java.util.ArrayList;
Expand Down Expand Up @@ -61,6 +63,7 @@ public class MockConsumer<K, V> implements Consumer<K, V> {
private KafkaException exception;
private AtomicBoolean wakeup;
private boolean closed;
private static final Logger log = LoggerFactory.getLogger(MockConsumer.class);

public MockConsumer(OffsetResetStrategy offsetResetStrategy) {
this.subscriptions = new SubscriptionState(offsetResetStrategy);
Expand Down Expand Up @@ -188,6 +191,12 @@ public synchronized ConsumerRecords<K, V> poll(final Duration timeout) {
if (!subscriptions.isPaused(entry.getKey())) {
final List<ConsumerRecord<K, V>> recs = entry.getValue();
for (final ConsumerRecord<K, V> rec : recs) {
if (beginningOffsets.get(entry.getKey()) != null && beginningOffsets.get(entry.getKey()) > subscriptions.position(entry.getKey())) {
log.info("poll offset {} less than changelog topic start offset {}, throw OffsetOutOfRangeException!", subscriptions.position(entry.getKey()), beginningOffsets.get(entry.getKey()));
Comment thread
linyli001 marked this conversation as resolved.
Outdated
RuntimeException exception = new OffsetOutOfRangeException(Collections.singletonMap(entry.getKey(), subscriptions.position(entry.getKey())));
Comment thread
linyli001 marked this conversation as resolved.
Outdated
throw exception;
}

if (assignment().contains(entry.getKey()) && rec.offset() >= subscriptions.position(entry.getKey())) {
results.computeIfAbsent(entry.getKey(), partition -> new ArrayList<>()).add(rec);
subscriptions.position(entry.getKey(), rec.offset() + 1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ public Collection<TopicPartition> restore(final RestoringTasks active) {
needsInitializing.remove(partition);
needsRestoring.remove(partition);

final StateRestorer restorer = stateRestorers.get(partition);
restorer.setCheckpointOffset(StateRestorer.NO_CHECKPOINT);
task.reinitializeStateStoresForPartitions(recoverableException.partitions());
}
restoreConsumer.seekToBeginning(partitions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,36 @@ public Set<TopicPartition> partitions() {
assertThat(callback.restored.size(), equalTo(messages));
}

@Test
public void shouldRecoverFromOffsetOutOfRangeExceptionAndRestoreFromStart() {
final int messages = 10;
final int startOffset = 5;
assignPartition(messages, topicPartition);
consumer.updateBeginningOffsets(Collections.singletonMap(topicPartition, (long) startOffset));
consumer.updateEndOffsets(Collections.singletonMap(topicPartition, (long) (messages + startOffset)));

addRecords(messages, topicPartition, startOffset);
consumer.assign(Collections.<TopicPartition>emptyList());

final StateRestorer stateRestorer = new StateRestorer(topicPartition, restoreListener, 1L, Long.MAX_VALUE, true,
"storeName");
Comment thread
linyli001 marked this conversation as resolved.
changelogReader.register(stateRestorer);

EasyMock.expect(active.restoringTaskFor(topicPartition)).andStubReturn(task);
EasyMock.replay(active, task);

// first restore call "fails" since OffsetOutOfRangeException but we should not die with an exception
assertEquals(0, changelogReader.restore(active).size());
//the starting offset for stateRestorer is set to NO_CHECKPOINT
assertThat(stateRestorer.checkpoint(), equalTo(-1L));

//restore the active task again
changelogReader.register(stateRestorer);
//the restored task should return completed partition without Exception.
assertEquals(1, changelogReader.restore(active).size());
//the restored size should be equal to message length.
assertThat(callback.restored.size(), equalTo(messages));
}

@Test
public void shouldRestoreMessagesFromCheckpoint() {
Expand Down