use of org.apache.kafka.common.errors.WakeupException in project kafka by apache.
the class AbstractCoordinatorTest method testWakeupAfterJoinGroupSent.
@Test
public void testWakeupAfterJoinGroupSent() throws Exception {
setupCoordinator();
mockClient.prepareResponse(groupCoordinatorResponse(node, Errors.NONE));
mockClient.prepareResponse(new MockClient.RequestMatcher() {
private int invocations = 0;
@Override
public boolean matches(AbstractRequest body) {
invocations++;
boolean isJoinGroupRequest = body instanceof JoinGroupRequest;
if (isJoinGroupRequest && invocations == 1)
// simulate wakeup before the request returns
throw new WakeupException();
return isJoinGroupRequest;
}
}, joinGroupFollowerResponse(1, memberId, leaderId, Errors.NONE));
mockClient.prepareResponse(syncGroupResponse(Errors.NONE));
AtomicBoolean heartbeatReceived = prepareFirstHeartbeat();
try {
coordinator.ensureActiveGroup();
fail("Should have woken up from ensureActiveGroup()");
} catch (WakeupException ignored) {
}
assertEquals(1, coordinator.onJoinPrepareInvokes);
assertEquals(0, coordinator.onJoinCompleteInvokes);
assertFalse(heartbeatReceived.get());
coordinator.ensureActiveGroup();
assertEquals(1, coordinator.onJoinPrepareInvokes);
assertEquals(1, coordinator.onJoinCompleteInvokes);
awaitFirstHeartbeat(heartbeatReceived);
}
use of org.apache.kafka.common.errors.WakeupException in project kafka by apache.
the class TransactionalMessageCopier method runEventLoop.
public static void runEventLoop(Namespace parsedArgs) {
final String transactionalId = parsedArgs.getString("transactionalId");
final String outputTopic = parsedArgs.getString("outputTopic");
String consumerGroup = parsedArgs.getString("consumerGroup");
final KafkaProducer<String, String> producer = createProducer(parsedArgs);
final KafkaConsumer<String, String> consumer = createConsumer(parsedArgs);
final AtomicLong remainingMessages = new AtomicLong(parsedArgs.getInt("maxMessages") == -1 ? Long.MAX_VALUE : parsedArgs.getInt("maxMessages"));
boolean groupMode = parsedArgs.getBoolean("groupMode");
String topicName = parsedArgs.getString("inputTopic");
final AtomicLong numMessagesProcessedSinceLastRebalance = new AtomicLong(0);
final AtomicLong totalMessageProcessed = new AtomicLong(0);
if (groupMode) {
consumer.subscribe(Collections.singleton(topicName), new ConsumerRebalanceListener() {
@Override
public void onPartitionsRevoked(Collection<TopicPartition> partitions) {
}
@Override
public void onPartitionsAssigned(Collection<TopicPartition> partitions) {
remainingMessages.set(partitions.stream().mapToLong(partition -> messagesRemaining(consumer, partition)).sum());
numMessagesProcessedSinceLastRebalance.set(0);
// We use message cap for remaining here as the remainingMessages are not set yet.
System.out.println(statusAsJson(totalMessageProcessed.get(), numMessagesProcessedSinceLastRebalance.get(), remainingMessages.get(), transactionalId, "RebalanceComplete"));
}
});
} else {
TopicPartition inputPartition = new TopicPartition(topicName, parsedArgs.getInt("inputPartition"));
consumer.assign(singleton(inputPartition));
remainingMessages.set(Math.min(messagesRemaining(consumer, inputPartition), remainingMessages.get()));
}
final boolean enableRandomAborts = parsedArgs.getBoolean("enableRandomAborts");
producer.initTransactions();
final AtomicBoolean isShuttingDown = new AtomicBoolean(false);
Exit.addShutdownHook("transactional-message-copier-shutdown-hook", () -> {
isShuttingDown.set(true);
consumer.wakeup();
System.out.println(shutDownString(totalMessageProcessed.get(), numMessagesProcessedSinceLastRebalance.get(), remainingMessages.get(), transactionalId));
});
final boolean useGroupMetadata = parsedArgs.getBoolean("useGroupMetadata");
try {
Random random = new Random();
while (!isShuttingDown.get() && remainingMessages.get() > 0) {
System.out.println(statusAsJson(totalMessageProcessed.get(), numMessagesProcessedSinceLastRebalance.get(), remainingMessages.get(), transactionalId, "ProcessLoop"));
ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(200));
if (records.count() > 0) {
try {
producer.beginTransaction();
for (ConsumerRecord<String, String> record : records) {
producer.send(producerRecordFromConsumerRecord(outputTopic, record));
}
long messagesSentWithinCurrentTxn = records.count();
ConsumerGroupMetadata groupMetadata = useGroupMetadata ? consumer.groupMetadata() : new ConsumerGroupMetadata(consumerGroup);
producer.sendOffsetsToTransaction(consumerPositions(consumer), groupMetadata);
if (enableRandomAborts && random.nextInt() % 3 == 0) {
abortTransactionAndResetPosition(producer, consumer);
} else {
producer.commitTransaction();
remainingMessages.getAndAdd(-messagesSentWithinCurrentTxn);
numMessagesProcessedSinceLastRebalance.getAndAdd(messagesSentWithinCurrentTxn);
totalMessageProcessed.getAndAdd(messagesSentWithinCurrentTxn);
}
} catch (ProducerFencedException e) {
throw new KafkaException(String.format("The transactional.id %s has been claimed by another process", transactionalId), e);
} catch (KafkaException e) {
log.debug("Aborting transaction after catching exception", e);
abortTransactionAndResetPosition(producer, consumer);
}
}
}
} catch (WakeupException e) {
if (!isShuttingDown.get()) {
// as part of shutdown.
throw e;
}
} finally {
Utils.closeQuietly(producer, "producer");
Utils.closeQuietly(consumer, "consumer");
}
}
use of org.apache.kafka.common.errors.WakeupException in project kafka by apache.
the class WorkerSinkTaskTest method testWakeupNotThrownDuringShutdown.
@Test
public void testWakeupNotThrownDuringShutdown() throws Exception {
createTask(initialState);
expectInitializeTask();
expectTaskGetTopic(true);
expectPollInitialAssignment();
expectConsumerPoll(1);
expectConversionAndTransformation(1);
sinkTask.put(EasyMock.anyObject());
EasyMock.expectLastCall();
EasyMock.expect(consumer.poll(Duration.ofMillis(EasyMock.anyLong()))).andAnswer(() -> {
// stop the task during its second iteration
workerTask.stop();
return new ConsumerRecords<>(Collections.emptyMap());
});
consumer.wakeup();
EasyMock.expectLastCall();
sinkTask.put(EasyMock.eq(Collections.emptyList()));
EasyMock.expectLastCall();
EasyMock.expect(consumer.assignment()).andReturn(INITIAL_ASSIGNMENT).times(1);
final Map<TopicPartition, OffsetAndMetadata> offsets = new HashMap<>();
offsets.put(TOPIC_PARTITION, new OffsetAndMetadata(FIRST_OFFSET + 1));
offsets.put(TOPIC_PARTITION2, new OffsetAndMetadata(FIRST_OFFSET));
sinkTask.preCommit(offsets);
EasyMock.expectLastCall().andReturn(offsets);
sinkTask.close(EasyMock.anyObject());
PowerMock.expectLastCall();
// fail the first time
consumer.commitSync(EasyMock.eq(offsets));
EasyMock.expectLastCall().andThrow(new WakeupException());
// and succeed the second time
consumer.commitSync(EasyMock.eq(offsets));
EasyMock.expectLastCall();
PowerMock.replayAll();
workerTask.initialize(TASK_CONFIG);
workerTask.initializeAndStart();
workerTask.execute();
assertEquals(0, workerTask.commitFailures());
PowerMock.verifyAll();
}
use of org.apache.kafka.common.errors.WakeupException in project kafka by apache.
the class WorkerSinkTaskTest method testWakeupInCommitSyncCausesRetry.
@Test
public void testWakeupInCommitSyncCausesRetry() throws Exception {
createTask(initialState);
expectInitializeTask();
expectTaskGetTopic(true);
expectPollInitialAssignment();
expectConsumerPoll(1);
expectConversionAndTransformation(1);
sinkTask.put(EasyMock.anyObject());
EasyMock.expectLastCall();
final Map<TopicPartition, OffsetAndMetadata> offsets = new HashMap<>();
offsets.put(TOPIC_PARTITION, new OffsetAndMetadata(FIRST_OFFSET + 1));
offsets.put(TOPIC_PARTITION2, new OffsetAndMetadata(FIRST_OFFSET));
sinkTask.preCommit(offsets);
EasyMock.expectLastCall().andReturn(offsets);
// first one raises wakeup
consumer.commitSync(EasyMock.<Map<TopicPartition, OffsetAndMetadata>>anyObject());
EasyMock.expectLastCall().andThrow(new WakeupException());
// we should retry and complete the commit
consumer.commitSync(EasyMock.<Map<TopicPartition, OffsetAndMetadata>>anyObject());
EasyMock.expectLastCall();
sinkTask.close(INITIAL_ASSIGNMENT);
EasyMock.expectLastCall();
INITIAL_ASSIGNMENT.forEach(tp -> EasyMock.expect(consumer.position(tp)).andReturn(FIRST_OFFSET));
sinkTask.open(INITIAL_ASSIGNMENT);
EasyMock.expectLastCall();
EasyMock.expect(consumer.assignment()).andReturn(INITIAL_ASSIGNMENT).times(5);
EasyMock.expect(consumer.poll(Duration.ofMillis(EasyMock.anyLong()))).andAnswer(() -> {
rebalanceListener.getValue().onPartitionsRevoked(INITIAL_ASSIGNMENT);
rebalanceListener.getValue().onPartitionsAssigned(INITIAL_ASSIGNMENT);
return ConsumerRecords.empty();
});
INITIAL_ASSIGNMENT.forEach(tp -> {
consumer.resume(Collections.singleton(tp));
EasyMock.expectLastCall();
});
statusListener.onResume(taskId);
EasyMock.expectLastCall();
PowerMock.replayAll();
workerTask.initialize(TASK_CONFIG);
time.sleep(30000L);
workerTask.initializeAndStart();
time.sleep(30000L);
// poll for initial assignment
workerTask.iteration();
time.sleep(30000L);
// first record delivered
workerTask.iteration();
// now rebalance with the wakeup triggered
workerTask.iteration();
time.sleep(30000L);
assertSinkMetricValue("partition-count", 2);
assertSinkMetricValue("sink-record-read-total", 1.0);
assertSinkMetricValue("sink-record-send-total", 1.0);
assertSinkMetricValue("sink-record-active-count", 0.0);
assertSinkMetricValue("sink-record-active-count-max", 1.0);
assertSinkMetricValue("sink-record-active-count-avg", 0.33333);
assertSinkMetricValue("offset-commit-seq-no", 1.0);
assertSinkMetricValue("offset-commit-completion-total", 1.0);
assertSinkMetricValue("offset-commit-skip-total", 0.0);
assertTaskMetricValue("status", "running");
assertTaskMetricValue("running-ratio", 1.0);
assertTaskMetricValue("pause-ratio", 0.0);
assertTaskMetricValue("batch-size-max", 1.0);
assertTaskMetricValue("batch-size-avg", 1.0);
assertTaskMetricValue("offset-commit-max-time-ms", 0.0);
assertTaskMetricValue("offset-commit-avg-time-ms", 0.0);
assertTaskMetricValue("offset-commit-failure-percentage", 0.0);
assertTaskMetricValue("offset-commit-success-percentage", 1.0);
PowerMock.verifyAll();
}
use of org.apache.kafka.common.errors.WakeupException in project wikidata-query-rdf by wikimedia.
the class KafkaPoller method fetch.
/**
* Fetch changes from Kafka.
* @param lastNextStartTime where last fetch ended up.
* @return Set of changes.
* @throws RetryableException
*/
@SuppressWarnings({ "checkstyle:npathcomplexity", "checkstyle:cyclomaticcomplexity" })
private Batch fetch(Instant lastNextStartTime) throws RetryableException {
Map<String, Change> changesByTitle = new LinkedHashMap<>();
ConsumerRecords<String, ChangeEvent> records;
Instant nextInstant = Instant.EPOCH;
AtomicLongMap<String> topicCounts = AtomicLongMap.create();
Map<TopicPartition, OffsetAndMetadata> batchOffsets = new HashMap<>();
while (true) {
commitPendindOffsets();
try (Context timerContext = pollingTimer.time()) {
// TODO: make timeout configurable? Wait for a bit so we catch bursts of messages?
records = consumer.poll(1000);
} catch (InterruptException | WakeupException e) {
throw new RetryableException("Error fetching recent changes", e);
}
int count = records.count();
log.debug("Fetched {} records from Kafka", count);
changesCounter.inc(count);
if (count == 0) {
// If we got nothing from Kafka, get out of the loop and return what we have
break;
}
boolean foundSomething = false;
for (ConsumerRecord<String, ChangeEvent> record : records) {
ChangeEvent event = record.value();
String topic = record.topic();
batchOffsets.put(new TopicPartition(record.topic(), record.partition()), new OffsetAndMetadata(record.offset()));
log.trace("Got event t:{} o:{}", record.topic(), record.offset());
if (!event.domain().equals(uris.getHost())) {
// wrong domain, ignore
continue;
}
// check namespace
if (!uris.isEntityNamespace(event.namespace())) {
continue;
}
if (!(event instanceof RevisionCreateEvent)) {
log.info("Got non revision create event class:{}, domain:{}, t:{}, revision:{}", event.getClass().getSimpleName(), event.title(), event.domain(), event.revision());
}
// Now we have event that we want to process
foundSomething = true;
topicCounts.getAndIncrement(record.topic());
// very chaotic, jumping back and forth.
if (topic.endsWith(reportingTopic)) {
nextInstant = Utils.max(nextInstant, Instant.ofEpochMilli(record.timestamp()));
}
// Using offset here as RC id since we do not have real RC id (this not being RC poller) but
// the offset serves the same function in Kafka and is also useful for debugging.
Change change = makeChange(event, record.offset());
Change dupe = changesByTitle.put(change.entityId(), change);
// This is not a big deal since deletes are relatively rare.
if (dupe != null && change.revision() > Change.NO_REVISION && (dupe.revision() > change.revision() || dupe.revision() == Change.NO_REVISION)) {
// need to remove so that order will be correct
changesByTitle.remove(change.entityId());
changesByTitle.put(change.entityId(), dupe);
}
}
log.debug("{} records left after filtering", changesByTitle.size());
if (changesByTitle.size() >= batchSize) {
// We have enough for the batch
break;
}
if (changesByTitle.size() > 0 && !foundSomething) {
log.info("Did not find anything useful in this batch, returning existing data");
// wait for more.
break;
}
// TODO: if we already have something and we've spent more than X seconds in the loop,
// we probably should return without waiting for more
}
// If we didn't get anything useful in the reporting topic, keep the old value
if (nextInstant.equals(Instant.EPOCH)) {
nextInstant = lastNextStartTime;
}
final ImmutableList<Change> changes = ImmutableList.copyOf(changesByTitle.values());
log.info("Found {} changes", changes.size());
if (log.isDebugEnabled()) {
topicCounts.asMap().forEach((k, v) -> log.debug("Topic {}: {} records", k, v));
}
long advanced = ChronoUnit.MILLIS.between(lastNextStartTime, nextInstant);
// be sure we got the whole second
return new Batch(changes, advanced, nextInstant.minusSeconds(1).toString(), nextInstant, batchOffsets);
}
Aggregations