use of org.apache.kafka.clients.consumer.InvalidOffsetException in project apache-kafka-on-k8s by banzaicloud.
the class StoreChangelogReaderTest method shouldRecoverFromInvalidOffsetExceptionAndFinishRestore.
@Test
public void shouldRecoverFromInvalidOffsetExceptionAndFinishRestore() {
final int messages = 10;
setupConsumer(messages, topicPartition);
consumer.setException(new InvalidOffsetException("Try Again!") {
@Override
public Set<TopicPartition> partitions() {
return Collections.singleton(topicPartition);
}
});
changelogReader.register(new StateRestorer(topicPartition, restoreListener, null, Long.MAX_VALUE, true, "storeName"));
EasyMock.expect(active.restoringTaskFor(topicPartition)).andReturn(task);
EasyMock.replay(active);
// first restore call "fails" but we should not die with an exception
assertEquals(0, changelogReader.restore(active).size());
// retry restore should succeed
assertEquals(1, changelogReader.restore(active).size());
assertThat(callback.restored.size(), equalTo(messages));
}
use of org.apache.kafka.clients.consumer.InvalidOffsetException in project apache-kafka-on-k8s by banzaicloud.
the class GlobalStreamThreadTest method shouldDieOnInvalidOffsetException.
@Test
public void shouldDieOnInvalidOffsetException() throws Exception {
initializeConsumer();
globalStreamThread.start();
TestUtils.waitForCondition(new TestCondition() {
@Override
public boolean conditionMet() {
return globalStreamThread.state() == RUNNING;
}
}, 10 * 1000, "Thread never started.");
mockConsumer.updateEndOffsets(Collections.singletonMap(topicPartition, 1L));
mockConsumer.addRecord(new ConsumerRecord<>(GLOBAL_STORE_TOPIC_NAME, 0, 0L, "K1".getBytes(), "V1".getBytes()));
TestUtils.waitForCondition(new TestCondition() {
@Override
public boolean conditionMet() {
return mockConsumer.position(topicPartition) == 1L;
}
}, 10 * 1000, "Input record never consumed");
mockConsumer.setException(new InvalidOffsetException("Try Again!") {
@Override
public Set<TopicPartition> partitions() {
return Collections.singleton(topicPartition);
}
});
// feed first record for recovery
mockConsumer.addRecord(new ConsumerRecord<>(GLOBAL_STORE_TOPIC_NAME, 0, 0L, "K1".getBytes(), "V1".getBytes()));
TestUtils.waitForCondition(new TestCondition() {
@Override
public boolean conditionMet() {
return globalStreamThread.state() == DEAD;
}
}, 10 * 1000, "GlobalStreamThread should have died.");
}
use of org.apache.kafka.clients.consumer.InvalidOffsetException in project apache-kafka-on-k8s by banzaicloud.
the class StreamThread method maybeUpdateStandbyTasks.
private void maybeUpdateStandbyTasks(final long now) {
if (state == State.RUNNING && taskManager.hasStandbyRunningTasks()) {
if (processStandbyRecords) {
if (!standbyRecords.isEmpty()) {
final Map<TopicPartition, List<ConsumerRecord<byte[], byte[]>>> remainingStandbyRecords = new HashMap<>();
for (final Map.Entry<TopicPartition, List<ConsumerRecord<byte[], byte[]>>> entry : standbyRecords.entrySet()) {
final TopicPartition partition = entry.getKey();
List<ConsumerRecord<byte[], byte[]>> remaining = entry.getValue();
if (remaining != null) {
final StandbyTask task = taskManager.standbyTask(partition);
if (task.isClosed()) {
log.warn("Standby task {} is already closed, probably because it got unexpectly migrated to another thread already. " + "Notifying the thread to trigger a new rebalance immediately.", task.id());
throw new TaskMigratedException(task);
}
remaining = task.update(partition, remaining);
if (remaining != null) {
remainingStandbyRecords.put(partition, remaining);
} else {
restoreConsumer.resume(singleton(partition));
}
}
}
standbyRecords = remainingStandbyRecords;
log.debug("Updated standby tasks {} in {}ms", taskManager.standbyTaskIds(), time.milliseconds() - now);
}
processStandbyRecords = false;
}
try {
final ConsumerRecords<byte[], byte[]> records = restoreConsumer.poll(0);
if (!records.isEmpty()) {
for (final TopicPartition partition : records.partitions()) {
final StandbyTask task = taskManager.standbyTask(partition);
if (task == null) {
throw new StreamsException(logPrefix + "Missing standby task for partition " + partition);
}
if (task.isClosed()) {
log.warn("Standby task {} is already closed, probably because it got unexpectedly migrated to another thread already. " + "Notifying the thread to trigger a new rebalance immediately.", task.id());
throw new TaskMigratedException(task);
}
final List<ConsumerRecord<byte[], byte[]>> remaining = task.update(partition, records.records(partition));
if (remaining != null) {
restoreConsumer.pause(singleton(partition));
standbyRecords.put(partition, remaining);
}
}
}
} catch (final InvalidOffsetException recoverableException) {
log.warn("Updating StandbyTasks failed. Deleting StandbyTasks stores to recreate from scratch.", recoverableException);
final Set<TopicPartition> partitions = recoverableException.partitions();
for (final TopicPartition partition : partitions) {
final StandbyTask task = taskManager.standbyTask(partition);
if (task.isClosed()) {
log.warn("Standby task {} is already closed, probably because it got unexpectly migrated to another thread already. " + "Notifying the thread to trigger a new rebalance immediately.", task.id());
throw new TaskMigratedException(task);
}
log.info("Reinitializing StandbyTask {}", task);
task.reinitializeStateStoresForPartitions(recoverableException.partitions());
}
restoreConsumer.seekToBeginning(partitions);
}
}
}
use of org.apache.kafka.clients.consumer.InvalidOffsetException in project apache-kafka-on-k8s by banzaicloud.
the class GlobalStateManagerImpl method restoreState.
private void restoreState(final StateRestoreCallback stateRestoreCallback, final List<TopicPartition> topicPartitions, final Map<TopicPartition, Long> highWatermarks, final String storeName) {
for (final TopicPartition topicPartition : topicPartitions) {
globalConsumer.assign(Collections.singletonList(topicPartition));
final Long checkpoint = checkpointableOffsets.get(topicPartition);
if (checkpoint != null) {
globalConsumer.seek(topicPartition, checkpoint);
} else {
globalConsumer.seekToBeginning(Collections.singletonList(topicPartition));
}
long offset = globalConsumer.position(topicPartition);
final Long highWatermark = highWatermarks.get(topicPartition);
BatchingStateRestoreCallback stateRestoreAdapter = (BatchingStateRestoreCallback) ((stateRestoreCallback instanceof BatchingStateRestoreCallback) ? stateRestoreCallback : new WrappedBatchingStateRestoreCallback(stateRestoreCallback));
stateRestoreListener.onRestoreStart(topicPartition, storeName, offset, highWatermark);
long restoreCount = 0L;
while (offset < highWatermark) {
try {
final ConsumerRecords<byte[], byte[]> records = globalConsumer.poll(100);
final List<KeyValue<byte[], byte[]>> restoreRecords = new ArrayList<>();
for (ConsumerRecord<byte[], byte[]> record : records) {
if (record.key() != null) {
restoreRecords.add(KeyValue.pair(record.key(), record.value()));
}
offset = globalConsumer.position(topicPartition);
}
stateRestoreAdapter.restoreAll(restoreRecords);
stateRestoreListener.onBatchRestored(topicPartition, storeName, offset, restoreRecords.size());
restoreCount += restoreRecords.size();
} catch (final InvalidOffsetException recoverableException) {
log.warn("Restoring GlobalStore {} failed due to: {}. Deleting global store to recreate from scratch.", storeName, recoverableException.getMessage());
reinitializeStateStoresForPartitions(recoverableException.partitions(), processorContext);
stateRestoreListener.onRestoreStart(topicPartition, storeName, offset, highWatermark);
restoreCount = 0L;
}
}
stateRestoreListener.onRestoreEnd(topicPartition, storeName, restoreCount);
checkpointableOffsets.put(topicPartition, offset);
}
}
use of org.apache.kafka.clients.consumer.InvalidOffsetException in project apache-kafka-on-k8s by banzaicloud.
the class StreamThreadTest method shouldRecoverFromInvalidOffsetExceptionOnRestoreAndFinishRestore.
@Test
public void shouldRecoverFromInvalidOffsetExceptionOnRestoreAndFinishRestore() throws Exception {
internalStreamsBuilder.stream(Collections.singleton("topic"), consumed).groupByKey().count(Materialized.<Object, Long, KeyValueStore<Bytes, byte[]>>as("count"));
final StreamThread thread = createStreamThread("cliendId", config, false);
final MockConsumer<byte[], byte[]> mockConsumer = (MockConsumer<byte[], byte[]>) thread.consumer;
final MockConsumer<byte[], byte[]> mockRestoreConsumer = (MockConsumer<byte[], byte[]>) thread.restoreConsumer;
final TopicPartition topicPartition = new TopicPartition("topic", 0);
final Set<TopicPartition> topicPartitionSet = Collections.singleton(topicPartition);
final Map<TaskId, Set<TopicPartition>> activeTasks = new HashMap<>();
activeTasks.put(new TaskId(0, 0), topicPartitionSet);
thread.taskManager().setAssignmentMetadata(activeTasks, Collections.<TaskId, Set<TopicPartition>>emptyMap());
mockConsumer.updatePartitions("topic", new ArrayList<PartitionInfo>() {
{
add(new PartitionInfo("topic", 0, null, new Node[0], new Node[0]));
}
});
mockConsumer.updateBeginningOffsets(Collections.singletonMap(topicPartition, 0L));
mockRestoreConsumer.updatePartitions("stream-thread-test-count-changelog", new ArrayList<PartitionInfo>() {
{
add(new PartitionInfo("stream-thread-test-count-changelog", 0, null, new Node[0], new Node[0]));
}
});
final TopicPartition changelogPartition = new TopicPartition("stream-thread-test-count-changelog", 0);
final Set<TopicPartition> changelogPartitionSet = Collections.singleton(changelogPartition);
mockRestoreConsumer.updateBeginningOffsets(Collections.singletonMap(changelogPartition, 0L));
mockRestoreConsumer.updateEndOffsets(Collections.singletonMap(changelogPartition, 2L));
mockConsumer.schedulePollTask(new Runnable() {
@Override
public void run() {
thread.setState(StreamThread.State.PARTITIONS_REVOKED);
thread.rebalanceListener.onPartitionsAssigned(topicPartitionSet);
}
});
try {
thread.start();
TestUtils.waitForCondition(new TestCondition() {
@Override
public boolean conditionMet() {
return mockRestoreConsumer.assignment().size() == 1;
}
}, "Never restore first record");
mockRestoreConsumer.addRecord(new ConsumerRecord<>("stream-thread-test-count-changelog", 0, 0L, "K1".getBytes(), "V1".getBytes()));
TestUtils.waitForCondition(new TestCondition() {
@Override
public boolean conditionMet() {
return mockRestoreConsumer.position(changelogPartition) == 1L;
}
}, "Never restore first record");
mockRestoreConsumer.setException(new InvalidOffsetException("Try Again!") {
@Override
public Set<TopicPartition> partitions() {
return changelogPartitionSet;
}
});
mockRestoreConsumer.addRecord(new ConsumerRecord<>("stream-thread-test-count-changelog", 0, 0L, "K1".getBytes(), "V1".getBytes()));
mockRestoreConsumer.addRecord(new ConsumerRecord<>("stream-thread-test-count-changelog", 0, 1L, "K2".getBytes(), "V2".getBytes()));
TestUtils.waitForCondition(new TestCondition() {
@Override
public boolean conditionMet() {
mockRestoreConsumer.assign(changelogPartitionSet);
return mockRestoreConsumer.position(changelogPartition) == 2L;
}
}, "Never finished restore");
} finally {
thread.shutdown();
thread.join(10000);
}
}
Aggregations