Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,6 @@ private void runOnce() {
maybeCheckpointTasks(checkpointStartTimeMs);

final long waitStartTimeMs = time.milliseconds();

waitIfAllChangelogsCompletelyRead();

final long endTimeMs = time.milliseconds();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.utils.Time;
import org.apache.kafka.streams.processor.StateStore;
import org.apache.kafka.streams.processor.TaskId;

Expand Down Expand Up @@ -188,6 +189,11 @@ public void clearTaskTimeout() {
throw new UnsupportedOperationException("This task is read-only");
}

@Override
public void maybeRecordRestored(final Time time, final long numRecords) {
throw new UnsupportedOperationException("This task is read-only");
}

@Override
public boolean commitNeeded() {
throw new UnsupportedOperationException("This task is read-only");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,15 @@
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.metrics.Sensor;
import org.apache.kafka.common.utils.Time;
import org.apache.kafka.streams.StreamsConfig;
import org.apache.kafka.streams.StreamsMetrics;
import org.apache.kafka.streams.errors.StreamsException;
import org.apache.kafka.streams.errors.TaskCorruptedException;
import org.apache.kafka.streams.errors.TaskMigratedException;
import org.apache.kafka.streams.processor.TaskId;
import org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl;
import org.apache.kafka.streams.processor.internals.metrics.TaskMetrics;
import org.apache.kafka.streams.processor.internals.metrics.ThreadMetrics;
import org.apache.kafka.streams.TopologyConfig.TaskConfig;
import org.apache.kafka.streams.state.internals.ThreadCache;
Expand All @@ -36,12 +38,15 @@
import java.util.Optional;
import java.util.Set;

import static org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.maybeRecordSensor;

/**
* A StandbyTask
*/
public class StandbyTask extends AbstractTask implements Task {
private final boolean eosEnabled;
private final Sensor closeTaskSensor;
private final Sensor updateSensor;
private final StreamsMetricsImpl streamsMetrics;

@SuppressWarnings("rawtypes")
Expand Down Expand Up @@ -81,6 +86,7 @@ public class StandbyTask extends AbstractTask implements Task {
processorContext.transitionToStandby(cache);

closeTaskSensor = ThreadMetrics.closeTaskSensor(Thread.currentThread().getName(), streamsMetrics);
updateSensor = TaskMetrics.updateSensor(Thread.currentThread().getName(), id.toString(), streamsMetrics);
this.eosEnabled = config.eosEnabled;
}

Expand All @@ -89,6 +95,11 @@ public boolean isActive() {
return false;
}

@Override
public void maybeRecordRestored(final Time time, final long numRecords) {
maybeRecordSensor(numRecords, time, updateSensor);
}

/**
* @throws TaskCorruptedException if the state cannot be reused (with EOS) and needs to be reset)
* @throws StreamsException fatal error, should close the thread
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -457,15 +457,9 @@ public long restore(final Map<TaskId, Task> tasks) {
// small batches; this can be optimized in the future, e.g. wait longer for larger batches.
final TaskId taskId = changelogs.get(partition).stateManager.taskId();
try {
final Task task = tasks.get(taskId);
final ChangelogMetadata changelogMetadata = changelogs.get(partition);
final int restored = restoreChangelog(changelogMetadata);
if (restored > 0 || changelogMetadata.state().equals(ChangelogState.COMPLETED)) {
final Task task = tasks.get(taskId);
if (task != null) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we remove this null check?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pondered on the code and I think it should not be null ever? Please correct me if I'm wrong.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mjsax LMK what do you think? I may lack some background here.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was just wondering. It seemed to be unrelated to this PR, and so I just assumed there must be a reason for having the null check.

task.clearTaskTimeout();
}
}
totalRestored += restored;
totalRestored += restoreChangelog(task, changelogMetadata);
} catch (final TimeoutException timeoutException) {
tasks.get(taskId).maybeInitTaskTimeoutOrThrow(
time.milliseconds(),
Expand Down Expand Up @@ -642,7 +636,7 @@ private void bufferChangelogRecords(final ChangelogMetadata changelogMetadata, f
*
* @return number of records restored
*/
private int restoreChangelog(final ChangelogMetadata changelogMetadata) {
private int restoreChangelog(final Task task, final ChangelogMetadata changelogMetadata) {
final ProcessorStateManager stateManager = changelogMetadata.stateManager;
final StateStoreMetadata storeMetadata = changelogMetadata.storeMetadata;
final TopicPartition partition = storeMetadata.changelogPartition();
Expand All @@ -662,6 +656,8 @@ private int restoreChangelog(final ChangelogMetadata changelogMetadata) {
changelogMetadata.bufferedRecords.clear();
}

task.maybeRecordRestored(time, records.size());

final Long currentOffset = storeMetadata.offset();
log.trace("Restored {} records from changelog {} to store {}, end offset is {}, current offset is {}",
numRecords, partition, storeName, recordEndOffset(changelogMetadata.restoreEndOffset), currentOffset);
Expand Down Expand Up @@ -694,6 +690,10 @@ private int restoreChangelog(final ChangelogMetadata changelogMetadata) {
}
}

if (numRecords > 0 || changelogMetadata.state().equals(ChangelogState.COMPLETED)) {
task.clearTaskTimeout();
}

return numRecords;
}

Expand Down Expand Up @@ -857,7 +857,7 @@ private void initializeChangelogs(final Map<TaskId, Task> tasks,
}
}

// try initialize limit offsets for standby tasks for the first time
// try initializing limit offsets for standby tasks for the first time
if (!committedOffsets.isEmpty()) {
updateLimitOffsetsForStandbyChangelogs(committedOffsets);
}
Expand All @@ -875,7 +875,7 @@ private void initializeChangelogs(final Map<TaskId, Task> tasks,
}

// prepare newly added partitions of the restore consumer by setting their starting position
prepareChangelogs(newPartitionsToRestore);
prepareChangelogs(tasks, newPartitionsToRestore);
}

private void addChangelogsToRestoreConsumer(final Set<TopicPartition> partitions) {
Expand Down Expand Up @@ -930,7 +930,8 @@ private void resumeChangelogsFromRestoreConsumer(final Collection<TopicPartition
log.debug("Resumed partitions {} from the restore consumer", partitions);
}

private void prepareChangelogs(final Set<ChangelogMetadata> newPartitionsToRestore) {
private void prepareChangelogs(final Map<TaskId, Task> tasks,
final Set<ChangelogMetadata> newPartitionsToRestore) {
// separate those who do not have the current offset loaded from checkpoint
final Set<TopicPartition> newPartitionsWithoutStartOffset = new HashSet<>();

Expand Down Expand Up @@ -986,6 +987,14 @@ private void prepareChangelogs(final Set<ChangelogMetadata> newPartitionsToResto
} catch (final Exception e) {
throw new StreamsException("State restore listener failed on batch restored", e);
}

final TaskId taskId = changelogs.get(partition).stateManager.taskId();
final StreamTask task = (StreamTask) tasks.get(taskId);
// if the log is truncated between when we get the log end offset and when we get the
// consumer position, then it's possible that the difference become negative and there's actually
// no records to restore; in this case we just initialize the sensor to zero
final long recordsToRestore = Math.max(changelogMetadata.restoreEndOffset - startOffset, 0L);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to apply max() ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not very compelling reasons, I just want to make sure we do not start with a negative number, but I cannot think of a case that it could be negative.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a comment to cover the very edge case when it could become negative.

task.initRemainingRecordsToRestore(time, recordsToRestore);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@
import java.util.stream.Collectors;

import static java.util.Collections.singleton;
import static org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.maybeRecordSensor;
import static org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.maybeMeasureLatency;

/**
Expand Down Expand Up @@ -92,6 +93,8 @@ public class StreamTask extends AbstractTask implements ProcessorNodePunctuator,
private final Sensor closeTaskSensor;
private final Sensor processRatioSensor;
private final Sensor processLatencySensor;
private final Sensor restoreSensor;
private final Sensor restoreRemainingSensor;
private final Sensor punctuateLatencySensor;
private final Sensor bufferedRecordsSensor;
private final Map<String, Sensor> e2eLatencySensors = new HashMap<>();
Expand Down Expand Up @@ -144,6 +147,8 @@ public StreamTask(final TaskId id,
this.streamsMetrics = streamsMetrics;
closeTaskSensor = ThreadMetrics.closeTaskSensor(threadId, streamsMetrics);
final String taskId = id.toString();
restoreSensor = TaskMetrics.restoreSensor(threadId, taskId, streamsMetrics);
restoreRemainingSensor = TaskMetrics.restoreRemainingRecordsSensor(threadId, taskId, streamsMetrics);
processRatioSensor = TaskMetrics.activeProcessRatioSensor(threadId, taskId, streamsMetrics);
processLatencySensor = TaskMetrics.processLatencySensor(threadId, taskId, streamsMetrics);
punctuateLatencySensor = TaskMetrics.punctuateSensor(threadId, taskId, streamsMetrics);
Expand Down Expand Up @@ -213,6 +218,16 @@ public boolean isActive() {
return true;
}

@Override
public void maybeRecordRestored(final Time time, final long numRecords) {
maybeRecordSensor(numRecords, time, restoreSensor);
maybeRecordSensor(-1 * numRecords, time, restoreRemainingSensor);
}

public void initRemainingRecordsToRestore(final Time time, final long numRecords) {
maybeRecordSensor(numRecords, time, restoreRemainingSensor);
}

/**
* @throws TaskCorruptedException if the state cannot be reused (with EOS) and needs to be reset
* @throws LockException could happen when multi-threads within the single instance, could retry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.OffsetAndMetadata;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.utils.Time;
import org.apache.kafka.streams.errors.TaskCorruptedException;
import org.apache.kafka.streams.errors.LockException;
import org.apache.kafka.streams.errors.StreamsException;
Expand Down Expand Up @@ -202,6 +203,8 @@ void maybeInitTaskTimeoutOrThrow(final long currentWallClockMs,

void clearTaskTimeout();

void maybeRecordRestored(final Time time, final long numRecords);

// task status inquiry

TaskId id();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,8 @@ public static Sensor e2ELatencySensor(final String threadId,
final String taskId,
final String processorNodeId,
final StreamsMetricsImpl streamsMetrics) {
final String sensorName = processorNodeId + "-" + RECORD_E2E_LATENCY;
final Sensor sensor = streamsMetrics.nodeLevelSensor(threadId, taskId, processorNodeId, sensorName, RecordingLevel.INFO);
final String sensorSuffix = processorNodeId + "-" + RECORD_E2E_LATENCY;
final Sensor sensor = streamsMetrics.nodeLevelSensor(threadId, taskId, processorNodeId, sensorSuffix, RecordingLevel.INFO);
final Map<String, String> tagMap = streamsMetrics.nodeLevelTagMap(threadId, taskId, processorNodeId);
addAvgAndMinAndMaxToSensor(
sensor,
Expand All @@ -185,8 +185,8 @@ public static Sensor emitFinalLatencySensor(final String threadId,
final String taskId,
final String processorNodeId,
final StreamsMetricsImpl streamsMetrics) {
final String sensorName = processorNodeId + "-" + EMIT_FINAL_LATENCY;
final Sensor sensor = streamsMetrics.nodeLevelSensor(threadId, taskId, processorNodeId, sensorName, RecordingLevel.DEBUG);
final String sensorSuffix = processorNodeId + "-" + EMIT_FINAL_LATENCY;
final Sensor sensor = streamsMetrics.nodeLevelSensor(threadId, taskId, processorNodeId, sensorSuffix, RecordingLevel.DEBUG);
final Map<String, String> tagMap = streamsMetrics.nodeLevelTagMap(threadId, taskId, processorNodeId);
addAvgAndMaxToSensor(
sensor,
Expand All @@ -203,8 +203,8 @@ public static Sensor emittedRecordsSensor(final String threadId,
final String taskId,
final String processorNodeId,
final StreamsMetricsImpl streamsMetrics) {
final String sensorName = processorNodeId + "-" + EMITTED_RECORDS;
final Sensor sensor = streamsMetrics.nodeLevelSensor(threadId, taskId, processorNodeId, sensorName, RecordingLevel.DEBUG);
final String sensorSuffix = processorNodeId + "-" + EMITTED_RECORDS;
final Sensor sensor = streamsMetrics.nodeLevelSensor(threadId, taskId, processorNodeId, sensorSuffix, RecordingLevel.DEBUG);
final Map<String, String> tagMap = streamsMetrics.nodeLevelTagMap(threadId, taskId, processorNodeId);
addRateOfSumAndSumMetricsToSensor(
sensor,
Expand All @@ -219,18 +219,19 @@ public static Sensor emittedRecordsSensor(final String threadId,

private static Sensor throughputParentSensor(final String threadId,
final String taskId,
final String metricNamePrefix,
final String operation,
final String descriptionOfRate,
final String descriptionOfCount,
final RecordingLevel recordingLevel,
final StreamsMetricsImpl streamsMetrics) {
final Sensor sensor = streamsMetrics.taskLevelSensor(threadId, taskId, metricNamePrefix, recordingLevel);
// use operation name as sensor suffix and metric prefix
final Sensor sensor = streamsMetrics.taskLevelSensor(threadId, taskId, operation, recordingLevel);
final Map<String, String> parentTagMap = streamsMetrics.nodeLevelTagMap(threadId, taskId, ROLLUP_VALUE);
addInvocationRateAndCountToSensor(
sensor,
PROCESSOR_NODE_LEVEL_GROUP,
parentTagMap,
metricNamePrefix,
operation,
descriptionOfRate,
descriptionOfCount
);
Expand All @@ -240,20 +241,21 @@ private static Sensor throughputParentSensor(final String threadId,
private static Sensor throughputSensor(final String threadId,
final String taskId,
final String processorNodeId,
final String metricNamePrefix,
final String operationName,
final String descriptionOfRate,
final String descriptionOfCount,
final RecordingLevel recordingLevel,
final StreamsMetricsImpl streamsMetrics,
final Sensor... parentSensors) {
// use operation name as sensor suffix and metric name prefix
final Sensor sensor =
streamsMetrics.nodeLevelSensor(threadId, taskId, processorNodeId, metricNamePrefix, recordingLevel, parentSensors);
streamsMetrics.nodeLevelSensor(threadId, taskId, processorNodeId, operationName, recordingLevel, parentSensors);
final Map<String, String> tagMap = streamsMetrics.nodeLevelTagMap(threadId, taskId, processorNodeId);
addInvocationRateAndCountToSensor(
sensor,
PROCESSOR_NODE_LEVEL_GROUP,
tagMap,
metricNamePrefix,
operationName,
descriptionOfRate,
descriptionOfCount
);
Expand Down
Loading