Skip to content
Open
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 @@ -2229,7 +2229,7 @@ private void recordRatio(final long now, final WindowedSum windowedSum, final Se
if (runOnceLatencyWindow > 0.0) {
final double latencyWindow =
windowedSum.measure(metricsConfig, now);
ratioSensor.record(latencyWindow / runOnceLatencyWindow);
ratioSensor.record(latencyWindow / runOnceLatencyWindow, now);
} else {
ratioSensor.record(0.0, now);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import static org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.TOTAL_SUFFIX;
import static org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.addAvgAndMaxToSensor;
import static org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.addInvocationRateAndCountToSensor;
import static org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.addInvocationRateToSensor;
import static org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.addRateOfSumAndSumMetricsToSensor;
import static org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.addSumMetricToSensor;
import static org.apache.kafka.streams.processor.internals.metrics.StreamsMetricsImpl.addValueMetricToSensor;

Expand Down Expand Up @@ -189,7 +189,7 @@ public static Sensor restoreSensor(final String threadId,
final String taskId,
final StreamsMetricsImpl streamsMetrics,
final Sensor... parentSensor) {
return invocationRateAndTotalSensor(
return rateAndTotalSensor(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I assume you changed the code so that it matches with what we have in docs/operations/monitoring.md ? So no need to update the doc? Should we mention this somewhere so that users understand that the change in number is due to an improvement in how we measure it, rather than a change in their application?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Don't think we need to call it out -- that is what the release changelog is for, and KAFKA-20877 will be listed.

That's actually the main reason, why I did not do this a MINOR PR, but filed a Jira, to highlight the fix.

threadId,
taskId,
RESTORE,
Expand All @@ -205,7 +205,7 @@ public static Sensor updateSensor(final String threadId,
final String taskId,
final StreamsMetricsImpl streamsMetrics,
final Sensor... parentSensor) {
return invocationRateAndTotalSensor(
return rateAndTotalSensor(
threadId,
taskId,
UPDATE,
Expand Down Expand Up @@ -250,7 +250,7 @@ public static Sensor recordLatenessSensor(final String threadId,
public static Sensor droppedRecordsSensor(final String threadId,
final String taskId,
final StreamsMetricsImpl streamsMetrics) {
return invocationRateAndTotalSensor(
return rateAndTotalSensor(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This changes dropped-records-rate too: almost all call sites record 1 per record, but InMemoryWindowStore:158 and InMemorySessionStore:163 record a whole batch (expiredRecordSensor.record(expiredRecords, ...))

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Oh. So this metric (dropped-records-rate) is actually broken for InMemoryWindowStore and InMemorySessionStore, too. Great catch.

Guess we just need to update the Jira and PR description, as this PR does fix the issue, too?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Actually, it seems the issue for InMemoryWindowStore and InMemorySessionStore was only introduced recently on trunk only: d2328ff

So we don't even need to update the Jira description as there is no issue in 4.3 and older releases.

threadId,
taskId,
DROPPED_RECORDS,
Expand Down Expand Up @@ -281,19 +281,20 @@ private static Sensor invocationRateAndCountSensor(final String threadId,
return sensor;
}

private static Sensor invocationRateAndTotalSensor(final String threadId,
final String taskId,
final String operation,
final String descriptionOfRate,
final String descriptionOfTotal,
final RecordingLevel recordingLevel,
final StreamsMetricsImpl streamsMetrics,
final Sensor... parentSensors) {
private static Sensor rateAndTotalSensor(
final String threadId,
final String taskId,
final String operation,
final String descriptionOfRate,
final String descriptionOfTotal,
final RecordingLevel recordingLevel,
final StreamsMetricsImpl streamsMetrics,
final Sensor... parentSensors
) {
final Sensor sensor = streamsMetrics.taskLevelSensor(threadId, taskId, operation, recordingLevel, parentSensors);
final Map<String, String> tags = streamsMetrics.taskLevelTagMap(threadId, taskId);

addInvocationRateToSensor(sensor, TASK_LEVEL_GROUP, tags, operation, descriptionOfRate);
addSumMetricToSensor(sensor, TASK_LEVEL_GROUP, tags, operation, true, descriptionOfTotal);
addRateOfSumAndSumMetricsToSensor(sensor, TASK_LEVEL_GROUP, tags, operation, descriptionOfRate, descriptionOfTotal);
return sensor;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public void put(final Bytes rawBaseKey,
final S segment = segments.getOrCreateSegmentIfLive(segmentId, internalProcessorContext, observedStreamTime);

if (segment == null) {
expiredRecordSensor.record(1.0d, internalProcessorContext.currentSystemTimeMs());
expiredRecordSensor.record();
LOG.warn("Skipping record for expired segment.");
} else {
synchronized (position) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ public void put(final Bytes key,
final long segmentId = segments.segmentId(timestamp);
final S segment = segments.getOrCreateSegmentIfLive(segmentId, internalProcessorContext, observedStreamTime);
if (segment == null) {
expiredRecordSensor.record(1.0d, internalProcessorContext.currentSystemTimeMs());
expiredRecordSensor.record();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This is the only expired-record drop that logs nothing — the sibling in AbstractDualSchemaRocksDBSegmentedBytesStore follows the same record() with LOG.warn("Skipping record for expired segment."). Worth adding here.

} else {
synchronized (position) {
// Transactional puts stage their position too, so READ_COMMITTED never sees a position ahead of the data.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ public void init(final StateStoreContext stateStoreContext,
}
removeExpiredSegments();
if (expiredRecords > 0) {
if (expiredRecordSensor != null && context != null) {
expiredRecordSensor.record(expiredRecords, context.currentSystemTimeMs());
if (expiredRecordSensor != null) {
expiredRecordSensor.record(expiredRecords);
}
LOG.warn("Skipping {} records for expired segments.", expiredRecords);
}
Expand Down Expand Up @@ -205,8 +205,8 @@ public void put(final Windowed<Bytes> sessionKey, final byte[] aggregate) {
if (windowEndTimestamp <= observedStreamTime - retentionPeriod) {
// The provided context is not required to implement InternalProcessorContext,
// If it doesn't, we can't record this metric (in fact, we wouldn't have even initialized it).
if (expiredRecordSensor != null && context != null) {
expiredRecordSensor.record(1.0d, context.currentSystemTimeMs());
if (expiredRecordSensor != null) {
expiredRecordSensor.record();
}
LOG.warn("Skipping record for expired segment.");
} else if (transactionBuffer != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ public void init(final StateStoreContext stateStoreContext,
}
removeExpiredSegments();
if (expiredRecords > 0) {
expiredRecordSensor.record(expiredRecords, internalProcessorContext.currentSystemTimeMs());
expiredRecordSensor.record(expiredRecords);
LOG.warn("Skipping {} records for expired segments.", expiredRecords);
}
}
Expand Down Expand Up @@ -195,7 +195,7 @@ public void put(final Bytes key, final byte[] value, final long windowStartTimes

synchronized (position) {
if (windowStartTimestamp <= observedStreamTime - retentionPeriod) {
expiredRecordSensor.record(1.0d, internalProcessorContext.currentSystemTimeMs());
expiredRecordSensor.record();
LOG.warn("Skipping record for expired segment.");
} else if (transactionBuffer != null) {
if (value != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ public long put(final Bytes key, final byte[] value, final long timestamp) {

synchronized (position) {
if (timestamp < observedStreamTime - gracePeriod) {
expiredRecordSensor.record(1.0d, internalProcessorContext.currentSystemTimeMs());
expiredRecordSensor.record();
LOG.warn("Skipping record for expired put.");
StoreQueryUtils.updatePosition(position, internalProcessorContext);
return PUT_RETURN_CODE_NOT_PUT;
Expand Down Expand Up @@ -160,7 +160,7 @@ public VersionedRecord<byte[]> delete(final Bytes key, final long timestamp) {

synchronized (position) {
if (timestamp < observedStreamTime - gracePeriod) {
expiredRecordSensor.record(1.0d, internalProcessorContext.currentSystemTimeMs());
expiredRecordSensor.record();
LOG.warn("Skipping record for expired delete.");
return null;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@
import static org.hamcrest.Matchers.empty;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.isA;
import static org.hamcrest.Matchers.not;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
Expand Down Expand Up @@ -487,12 +486,26 @@ public void shouldRecordRestoredRecords() {
task.recordRestoration(time, 25L, false);

assertThat(totalMetric.metricValue(), equalTo(25.0));
assertThat(rateMetric.metricValue(), not(0.0));
// the rate measures updated records per second, not update batches per second; with no time
// elapsed the rate window is (metrics.num.samples - 1) * metrics.sample.window.ms == 30s
assertEquals(
25.0 / 30.0,
((Number) rateMetric.metricValue()).doubleValue(),
0.0001d,
"update-rate must measure updated records per second, not update batches per second; "
+ "counting batches would give 1/30 == 0.03333 (KAFKA-20877)"
);

task.recordRestoration(time, 50L, false);

assertThat(totalMetric.metricValue(), equalTo(75.0));
assertThat(rateMetric.metricValue(), not(0.0));
assertEquals(
75.0 / 30.0,
((Number) rateMetric.metricValue()).doubleValue(),
0.0001d,
"update-rate must measure updated records per second, not update batches per second; "
+ "counting batches would give 2/30 == 0.06666 (KAFKA-20877)"
);
}

private KafkaMetric getMetric(final String operation,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -893,13 +893,27 @@ public void shouldRecordRestoredRecords() {
task.recordRestoration(time, 25L, false);

assertThat(totalMetric.metricValue(), equalTo(25.0));
assertThat(rateMetric.metricValue(), not(0.0));
// the rate measures restored records per second, not restore batches per second; with no time
// elapsed the rate window is (metrics.num.samples - 1) * metrics.sample.window.ms == 30s
assertEquals(
25.0 / 30.0,
((Number) rateMetric.metricValue()).doubleValue(),
0.0001d,
"restore-rate must measure restored records per second, not restore batches per second; "
+ "counting batches would give 1/30 == 0.03333 (KAFKA-20877)"
);
assertThat(remainMetric.metricValue(), equalTo(75.0));

task.recordRestoration(time, 50L, false);

assertThat(totalMetric.metricValue(), equalTo(75.0));
assertThat(rateMetric.metricValue(), not(0.0));
assertEquals(
75.0 / 30.0,
((Number) rateMetric.metricValue()).doubleValue(),
0.0001d,
"restore-rate must measure restored records per second, not restore batches per second; "
+ "counting batches would give 2/30 == 0.06666 (KAFKA-20877)"
);
assertThat(remainMetric.metricValue(), equalTo(25.0));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -240,21 +240,12 @@ public void shouldGetDroppedRecordsSensor() {
try (final MockedStatic<StreamsMetricsImpl> streamsMetricsStaticMock = mockStatic(StreamsMetricsImpl.class)) {
final Sensor sensor = TaskMetrics.droppedRecordsSensor(THREAD_ID, TASK_ID, streamsMetrics);
streamsMetricsStaticMock.verify(
() -> StreamsMetricsImpl.addInvocationRateToSensor(
() -> StreamsMetricsImpl.addRateOfSumAndSumMetricsToSensor(
expectedSensor,
TASK_LEVEL_GROUP,
tagMap,
operation,
rateDescription
)
);
streamsMetricsStaticMock.verify(
() -> StreamsMetricsImpl.addSumMetricToSensor(
expectedSensor,
TASK_LEVEL_GROUP,
tagMap,
operation,
true,
rateDescription,
totalDescription
)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@
import static org.hamcrest.Matchers.hasEntry;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -1586,8 +1585,6 @@ public void shouldMeasureExpiredRecords() {
TestUtils.tempDirectory(),
new StreamsConfig(streamsConfig)
);
final Time time = Time.SYSTEM;
context.setSystemTimeMs(time.milliseconds());
bytesStore.init(context, bytesStore);

// write a record to advance stream time, with a high enough timestamp
Expand Down Expand Up @@ -1622,7 +1619,16 @@ public void shouldMeasureExpiredRecords() {
)
));
assertEquals(1.0, dropTotal.metricValue());
assertNotEquals(0.0, dropRate.metricValue());
// exactly one record was dropped, over the rate's default un-elapsed sampling window of
// (metrics.num.samples - 1) * metrics.sample.window.ms == 30s. The delta is generous because the
// window also grows by however long the store work takes between recording and reading the
// metric; it still separates one dropped record from none (0.0) and from two (0.06666).
assertEquals(
1.0 / 30.0,
((Number) dropRate.metricValue()).doubleValue(),
0.005d,
"dropped-records-rate should reflect the single dropped record over the ~30s sampling window"
);

bytesStore.close();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@
import static org.hamcrest.Matchers.hasEntry;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -962,8 +961,6 @@ public void shouldMeasureExpiredRecords(final SegmentedBytesStore.KeySchema sche
TestUtils.tempDirectory(),
new StreamsConfig(streamsConfig)
);
final Time time = Time.SYSTEM;
context.setSystemTimeMs(time.milliseconds());
bytesStore.init(context, bytesStore);

// write a record to advance stream time, with a high enough timestamp
Expand Down Expand Up @@ -998,7 +995,16 @@ public void shouldMeasureExpiredRecords(final SegmentedBytesStore.KeySchema sche
)
));
assertEquals(1.0, dropTotal.metricValue());
assertNotEquals(0.0, dropRate.metricValue());
// exactly one record was dropped, over the rate's default un-elapsed sampling window of
// (metrics.num.samples - 1) * metrics.sample.window.ms == 30s. The delta is generous because the
// window also grows by however long the store work takes between recording and reading the
// metric; it still separates one dropped record from none (0.0) and from two (0.06666).
assertEquals(
1.0 / 30.0,
((Number) dropRate.metricValue()).doubleValue(),
0.005d,
"dropped-records-rate should reflect the single dropped record over the ~30s sampling window"
);

bytesStore.close();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand Down Expand Up @@ -894,9 +893,7 @@ public void shouldMeasureExpiredRecords() {
new StreamsConfig(streamsConfig),
recordCollector
);
final Time time = Time.SYSTEM;
context.setTime(1L);
context.setSystemTimeMs(time.milliseconds());
sessionStore.init(context, sessionStore);

// Advance stream time by inserting record with large enough timestamp that records with timestamp 0 are expired
Expand Down Expand Up @@ -931,7 +928,16 @@ public void shouldMeasureExpiredRecords() {
)
));
assertEquals(1.0, dropTotal.metricValue());
assertNotEquals(0.0, dropRate.metricValue());
// exactly one record was dropped, over the rate's default un-elapsed sampling window of
// (metrics.num.samples - 1) * metrics.sample.window.ms == 30s. The delta is generous because the
// window also grows by however long the store work takes between recording and reading the
// metric; it still separates one dropped record from none (0.0) and from two (0.06666).
assertEquals(
1.0 / 30.0,
((Number) dropRate.metricValue()).doubleValue(),
0.005d,
"dropped-records-rate should reflect the single dropped record over the ~30s sampling window"
);

sessionStore.close();
}
Expand Down
Loading
Loading