This is an automated email from the ASF dual-hosted git repository.
Jackie-Jiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/pinot.git
The following commit(s) were added to refs/heads/master by this push:
new 274601b020e Add numRemoteAccesses and remoteAccessBytes query stats
(#19756)
274601b020e is described below
commit 274601b020eda36e28c917a3b94728ca37733bce
Author: Xiaotian (Jackie) Jiang <[email protected]>
AuthorDate: Tue Oct 6 17:10:44 2026 -0700
Add numRemoteAccesses and remoteAccessBytes query stats (#19756)
---
.../apache/pinot/common/datatable/DataTable.java | 8 +++-
.../pinot/common/response/BrokerResponse.java | 8 ++++
.../response/broker/BrokerResponseNative.java | 45 +++++++++++++++-------
.../response/broker/BrokerResponseNativeV2.java | 31 ++++++++++-----
.../response/broker/CursorResponseNative.java | 10 +++--
.../response/mapper/TimeSeriesResponseMapper.java | 4 ++
.../core/operator/InstanceResponseOperator.java | 10 +++++
.../query/reduce/ExecutionStatsAggregator.java | 8 ++++
.../operator/InstanceResponseOperatorTest.java | 36 +++++++++++++++++
.../query/reduce/ExecutionStatsAggregatorTest.java | 24 ++++++++++++
.../pinot/query/runtime/operator/LeafOperator.java | 12 +++++-
.../TimeSeriesExchangeReceiveOperator.java | 4 +-
.../query/runtime/operator/LeafOperatorTest.java | 33 ++++++++++++++++
.../org/apache/pinot/segment/spi/FetchContext.java | 23 +++++++++++
14 files changed, 224 insertions(+), 32 deletions(-)
diff --git
a/pinot-common/src/main/java/org/apache/pinot/common/datatable/DataTable.java
b/pinot-common/src/main/java/org/apache/pinot/common/datatable/DataTable.java
index d3b3a79dbaa..1187448c81b 100644
---
a/pinot-common/src/main/java/org/apache/pinot/common/datatable/DataTable.java
+++
b/pinot-common/src/main/java/org/apache/pinot/common/datatable/DataTable.java
@@ -160,11 +160,15 @@ public interface DataTable {
// inputs. How a downstream consumer reacts (skip, retry, accept with
annotation) is the
// consumer's policy.
INCOMPLETE_MERGE(43, "incompleteMerge", MetadataValueType.STRING),
- LITE_MODE_LEAF_STAGE_LIMIT_REACHED(44, "liteModeLeafStageLimitReached",
MetadataValueType.STRING);
+ LITE_MODE_LEAF_STAGE_LIMIT_REACHED(44, "liteModeLeafStageLimitReached",
MetadataValueType.STRING),
+ // Number of remote accesses (local cache misses) made to read segment
data from a remote storage tier, and the
+ // number of bytes they read.
+ NUM_REMOTE_ACCESSES(45, "numRemoteAccesses", MetadataValueType.LONG),
+ REMOTE_ACCESS_BYTES(46, "remoteAccessBytes", MetadataValueType.LONG);
// We keep this constant to track the max id added so far for backward
compatibility.
// Increase it when adding new keys, but NEVER DECREASE IT!!!
- private static final int MAX_ID =
LITE_MODE_LEAF_STAGE_LIMIT_REACHED.getId();
+ private static final int MAX_ID = REMOTE_ACCESS_BYTES.getId();
private static final MetadataKey[] ID_TO_ENUM_KEY_MAP = new
MetadataKey[MAX_ID + 1];
private static final Map<String, MetadataKey> NAME_TO_ENUM_KEY_MAP = new
HashMap<>();
diff --git
a/pinot-common/src/main/java/org/apache/pinot/common/response/BrokerResponse.java
b/pinot-common/src/main/java/org/apache/pinot/common/response/BrokerResponse.java
index 98362b1b4e2..cd9bc76f160 100644
---
a/pinot-common/src/main/java/org/apache/pinot/common/response/BrokerResponse.java
+++
b/pinot-common/src/main/java/org/apache/pinot/common/response/BrokerResponse.java
@@ -221,6 +221,14 @@ public interface BrokerResponse {
/// This value is always lower or equal than
[#getNumSegmentsPrunedByServer()]
long getNumSegmentsPrunedByValue();
+ /// Returns the number of remote accesses (local cache misses) made to read
segment data from a remote storage tier.
+ ///
+ /// This value is always `0` when the segments are not backed by a remote
storage tier.
+ long getNumRemoteAccesses();
+
+ /// Returns the number of bytes read by the remote accesses (see
[#getNumRemoteAccesses()]).
+ long getRemoteAccessBytes();
+
/// Returns the time used to reduce the server responses into the final
response in milliseconds.
long getBrokerReduceTimeMs();
diff --git
a/pinot-common/src/main/java/org/apache/pinot/common/response/broker/BrokerResponseNative.java
b/pinot-common/src/main/java/org/apache/pinot/common/response/broker/BrokerResponseNative.java
index b38bae56605..81749d6002f 100644
---
a/pinot-common/src/main/java/org/apache/pinot/common/response/broker/BrokerResponseNative.java
+++
b/pinot-common/src/main/java/org/apache/pinot/common/response/broker/BrokerResponseNative.java
@@ -42,23 +42,20 @@ import org.apache.pinot.spi.utils.JsonUtils;
/// This class can be used to serialize/deserialize the broker response.
@JsonPropertyOrder({
"resultTable", "numRowsResultSet", "partialResult", "exceptions",
"numGroupsLimitReached",
- "numGroupsWarningLimitReached", "maxRowsInDistinctReached",
- "maxRowsWithoutChangeInDistinctReached",
- "maxExecutionTimeInDistinctReached", "timeUsedMs",
- "requestId", "clientRequestId", "brokerId", "numDocsScanned", "totalDocs",
- "numEntriesScannedInFilter",
- "numEntriesScannedPostFilter", "numServersQueried", "numServersResponded",
"numSegmentsQueried",
- "numSegmentsProcessed", "numSegmentsMatched",
"numConsumingSegmentsQueried", "numConsumingSegmentsProcessed",
- "numConsumingSegmentsMatched", "minConsumingFreshnessTimeMs",
"numSegmentsPrunedByBroker",
- "numSegmentsPrunedByServer", "numSegmentsPrunedInvalid",
"numSegmentsPrunedByLimit", "numSegmentsPrunedByValue",
- "brokerReduceTimeMs", "offlineThreadCpuTimeNs", "realtimeThreadCpuTimeNs",
"offlineSystemActivitiesCpuTimeNs",
- "realtimeSystemActivitiesCpuTimeNs",
"offlineResponseSerializationCpuTimeNs",
+ "numGroupsWarningLimitReached", "maxRowsInDistinctReached",
"maxRowsWithoutChangeInDistinctReached",
+ "maxExecutionTimeInDistinctReached", "timeUsedMs", "requestId",
"clientRequestId", "brokerId", "numDocsScanned",
+ "totalDocs", "numEntriesScannedInFilter", "numEntriesScannedPostFilter",
"numServersQueried",
+ "numServersResponded", "numSegmentsQueried", "numSegmentsProcessed",
"numSegmentsMatched",
+ "numConsumingSegmentsQueried", "numConsumingSegmentsProcessed",
"numConsumingSegmentsMatched",
+ "minConsumingFreshnessTimeMs", "numSegmentsPrunedByBroker",
"numSegmentsPrunedByServer",
+ "numSegmentsPrunedInvalid", "numSegmentsPrunedByLimit",
"numSegmentsPrunedByValue", "numRemoteAccesses",
+ "remoteAccessBytes", "brokerReduceTimeMs", "offlineThreadCpuTimeNs",
"realtimeThreadCpuTimeNs",
+ "offlineSystemActivitiesCpuTimeNs", "realtimeSystemActivitiesCpuTimeNs",
"offlineResponseSerializationCpuTimeNs",
"realtimeResponseSerializationCpuTimeNs", "offlineTotalCpuTimeNs",
"realtimeTotalCpuTimeNs",
"explainPlanNumEmptyFilterSegments",
"explainPlanNumMatchAllFilterSegments", "traceInfo", "tablesQueried",
"offlineThreadMemAllocatedBytes", "realtimeThreadMemAllocatedBytes",
"offlineResponseSerMemAllocatedBytes",
- "realtimeResponseSerMemAllocatedBytes", "offlineTotalMemAllocatedBytes",
"realtimeTotalMemAllocatedBytes",
- "pools", "rlsFiltersApplied", "approximateFunctionApplied",
"groupsTrimmed", "materializedViewQueried",
- "serverStats"
+ "realtimeResponseSerMemAllocatedBytes", "offlineTotalMemAllocatedBytes",
"realtimeTotalMemAllocatedBytes", "pools",
+ "rlsFiltersApplied", "approximateFunctionApplied", "groupsTrimmed",
"materializedViewQueried", "serverStats"
})
@JsonIgnoreProperties(ignoreUnknown = true)
public class BrokerResponseNative implements BrokerResponse {
@@ -102,6 +99,8 @@ public class BrokerResponseNative implements BrokerResponse {
private long _numSegmentsPrunedInvalid = 0L;
private long _numSegmentsPrunedByLimit = 0L;
private long _numSegmentsPrunedByValue = 0L;
+ private long _numRemoteAccesses = 0L;
+ private long _remoteAccessBytes = 0L;
private long _brokerReduceTimeMs = 0L;
private long _offlineThreadCpuTimeNs = 0L;
private long _realtimeThreadCpuTimeNs = 0L;
@@ -481,6 +480,24 @@ public class BrokerResponseNative implements
BrokerResponse {
_numSegmentsPrunedByValue = numSegmentsPrunedByValue;
}
+ @Override
+ public long getNumRemoteAccesses() {
+ return _numRemoteAccesses;
+ }
+
+ public void setNumRemoteAccesses(long numRemoteAccesses) {
+ _numRemoteAccesses = numRemoteAccesses;
+ }
+
+ @Override
+ public long getRemoteAccessBytes() {
+ return _remoteAccessBytes;
+ }
+
+ public void setRemoteAccessBytes(long remoteAccessBytes) {
+ _remoteAccessBytes = remoteAccessBytes;
+ }
+
@Override
public long getBrokerReduceTimeMs() {
return _brokerReduceTimeMs;
diff --git
a/pinot-common/src/main/java/org/apache/pinot/common/response/broker/BrokerResponseNativeV2.java
b/pinot-common/src/main/java/org/apache/pinot/common/response/broker/BrokerResponseNativeV2.java
index 9919057b565..c9657f39ea6 100644
---
a/pinot-common/src/main/java/org/apache/pinot/common/response/broker/BrokerResponseNativeV2.java
+++
b/pinot-common/src/main/java/org/apache/pinot/common/response/broker/BrokerResponseNativeV2.java
@@ -40,23 +40,22 @@ import org.apache.pinot.common.response.ProcessingException;
/// TODO: Currently this class cannot be used to deserialize the JSON response.
@JsonPropertyOrder({
"resultTable", "numRowsResultSet", "partialResult", "exceptions",
"numGroupsLimitReached",
- "numGroupsWarningLimitReached", "numGroups", "earlyTerminationReasons",
- "maxRowsInJoinReached",
- "maxRowsInJoin", "maxRowsInWindowReached", "maxRowsInWindow",
"timeUsedMs", "stageStats", "streamStatsCoverage",
+ "numGroupsWarningLimitReached", "numGroups", "earlyTerminationReasons",
"maxRowsInJoinReached", "maxRowsInJoin",
+ "maxRowsInWindowReached", "maxRowsInWindow", "timeUsedMs", "stageStats",
"streamStatsCoverage",
"maxRowsInOperator", "requestId", "clientRequestId", "brokerId",
"numDocsScanned", "totalDocs",
"numEntriesScannedInFilter", "numEntriesScannedPostFilter",
"numServersQueried", "numServersResponded",
"numSegmentsQueried", "numSegmentsProcessed", "numSegmentsMatched",
"numConsumingSegmentsQueried",
"numConsumingSegmentsProcessed", "numConsumingSegmentsMatched",
"minConsumingFreshnessTimeMs",
"numSegmentsPrunedByBroker", "numSegmentsPrunedByServer",
"numSegmentsPrunedInvalid", "numSegmentsPrunedByLimit",
- "numSegmentsPrunedByValue", "brokerReduceTimeMs",
"offlineThreadCpuTimeNs", "realtimeThreadCpuTimeNs",
- "offlineSystemActivitiesCpuTimeNs", "realtimeSystemActivitiesCpuTimeNs",
"offlineResponseSerializationCpuTimeNs",
+ "numSegmentsPrunedByValue", "numRemoteAccesses", "remoteAccessBytes",
"brokerReduceTimeMs",
+ "offlineThreadCpuTimeNs", "realtimeThreadCpuTimeNs",
"offlineSystemActivitiesCpuTimeNs",
+ "realtimeSystemActivitiesCpuTimeNs",
"offlineResponseSerializationCpuTimeNs",
"realtimeResponseSerializationCpuTimeNs", "offlineTotalCpuTimeNs",
"realtimeTotalCpuTimeNs",
"explainPlanNumEmptyFilterSegments",
"explainPlanNumMatchAllFilterSegments", "traceInfo", "tablesQueried",
"offlineThreadMemAllocatedBytes", "realtimeThreadMemAllocatedBytes",
"offlineResponseSerMemAllocatedBytes",
- "realtimeResponseSerMemAllocatedBytes", "offlineTotalMemAllocatedBytes",
"realtimeTotalMemAllocatedBytes",
- "pools", "rlsFiltersApplied", "approximateFunctionApplied",
"groupsTrimmed",
- "mseLiteLeafStageLimitReached", "mseLiteLeafStageEffectiveLimit",
"mseLiteFanOutAdjustedLimitApplied",
- "responseMetadata"
+ "realtimeResponseSerMemAllocatedBytes", "offlineTotalMemAllocatedBytes",
"realtimeTotalMemAllocatedBytes", "pools",
+ "rlsFiltersApplied", "approximateFunctionApplied", "groupsTrimmed",
"mseLiteLeafStageLimitReached",
+ "mseLiteLeafStageEffectiveLimit", "mseLiteFanOutAdjustedLimitApplied",
"responseMetadata"
})
public class BrokerResponseNativeV2 implements BrokerResponse {
private final StatMap<StatKey> _brokerStats = new StatMap<>(StatKey.class);
@@ -410,6 +409,16 @@ public class BrokerResponseNativeV2 implements
BrokerResponse {
return _brokerStats.getLong(StatKey.NUM_SEGMENTS_PRUNED_BY_VALUE);
}
+ @Override
+ public long getNumRemoteAccesses() {
+ return _brokerStats.getLong(StatKey.NUM_REMOTE_ACCESSES);
+ }
+
+ @Override
+ public long getRemoteAccessBytes() {
+ return _brokerStats.getLong(StatKey.REMOTE_ACCESS_BYTES);
+ }
+
@Override
public long getBrokerReduceTimeMs() {
return _brokerReduceTimeMs;
@@ -571,7 +580,9 @@ public class BrokerResponseNativeV2 implements
BrokerResponse {
}
},
EARLY_TERMINATION_REASONS(StatMap.Type.STRING_SET),
- LITE_MODE_LEAF_STAGE_LIMIT_REACHED(StatMap.Type.BOOLEAN);
+ LITE_MODE_LEAF_STAGE_LIMIT_REACHED(StatMap.Type.BOOLEAN),
+ NUM_REMOTE_ACCESSES(StatMap.Type.LONG),
+ REMOTE_ACCESS_BYTES(StatMap.Type.LONG);
private final StatMap.Type _type;
diff --git
a/pinot-common/src/main/java/org/apache/pinot/common/response/broker/CursorResponseNative.java
b/pinot-common/src/main/java/org/apache/pinot/common/response/broker/CursorResponseNative.java
index 96565b68cad..d88d30f92e0 100644
---
a/pinot-common/src/main/java/org/apache/pinot/common/response/broker/CursorResponseNative.java
+++
b/pinot-common/src/main/java/org/apache/pinot/common/response/broker/CursorResponseNative.java
@@ -25,14 +25,14 @@ import org.apache.pinot.common.response.CursorResponse;
@JsonPropertyOrder({
"resultTable", "numRowsResultSet", "partialResult", "exceptions",
"numGroupsLimitReached",
- "numGroupsWarningLimitReached", "timeUsedMs", "requestId", "brokerId",
- "numDocsScanned", "totalDocs",
+ "numGroupsWarningLimitReached", "timeUsedMs", "requestId", "brokerId",
"numDocsScanned", "totalDocs",
"numEntriesScannedInFilter", "numEntriesScannedPostFilter",
"numServersQueried", "numServersResponded",
"numSegmentsQueried", "numSegmentsProcessed", "numSegmentsMatched",
"numConsumingSegmentsQueried",
"numConsumingSegmentsProcessed", "numConsumingSegmentsMatched",
"minConsumingFreshnessTimeMs",
"numSegmentsPrunedByBroker", "numSegmentsPrunedByServer",
"numSegmentsPrunedInvalid", "numSegmentsPrunedByLimit",
- "numSegmentsPrunedByValue", "brokerReduceTimeMs",
"offlineThreadCpuTimeNs", "realtimeThreadCpuTimeNs",
- "offlineSystemActivitiesCpuTimeNs", "realtimeSystemActivitiesCpuTimeNs",
"offlineResponseSerializationCpuTimeNs",
+ "numSegmentsPrunedByValue", "numRemoteAccesses", "remoteAccessBytes",
"brokerReduceTimeMs",
+ "offlineThreadCpuTimeNs", "realtimeThreadCpuTimeNs",
"offlineSystemActivitiesCpuTimeNs",
+ "realtimeSystemActivitiesCpuTimeNs",
"offlineResponseSerializationCpuTimeNs",
"realtimeResponseSerializationCpuTimeNs", "offlineTotalCpuTimeNs",
"realtimeTotalCpuTimeNs",
"explainPlanNumEmptyFilterSegments",
"explainPlanNumMatchAllFilterSegments", "traceInfo", "tableQueries",
"groupsTrimmed", "approximateFunctionApplied",
@@ -83,6 +83,8 @@ public class CursorResponseNative extends
BrokerResponseNative implements Cursor
setNumSegmentsPrunedInvalid(response.getNumSegmentsPrunedInvalid());
setNumSegmentsPrunedByLimit(response.getNumSegmentsPrunedByLimit());
setNumSegmentsPrunedByValue(response.getNumSegmentsPrunedByValue());
+ setNumRemoteAccesses(response.getNumRemoteAccesses());
+ setRemoteAccessBytes(response.getRemoteAccessBytes());
setBrokerReduceTimeMs(response.getBrokerReduceTimeMs());
setOfflineThreadCpuTimeNs(response.getOfflineThreadCpuTimeNs());
setRealtimeThreadCpuTimeNs(response.getRealtimeThreadCpuTimeNs());
diff --git
a/pinot-common/src/main/java/org/apache/pinot/common/response/mapper/TimeSeriesResponseMapper.java
b/pinot-common/src/main/java/org/apache/pinot/common/response/mapper/TimeSeriesResponseMapper.java
index 375e1979319..1d71932777b 100644
---
a/pinot-common/src/main/java/org/apache/pinot/common/response/mapper/TimeSeriesResponseMapper.java
+++
b/pinot-common/src/main/java/org/apache/pinot/common/response/mapper/TimeSeriesResponseMapper.java
@@ -166,6 +166,10 @@ public class TimeSeriesResponseMapper {
getIntMetadataValue(metadata,
DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_BY_LIMIT));
map.merge(BrokerResponseNativeV2.StatKey.NUM_SEGMENTS_PRUNED_BY_VALUE,
getIntMetadataValue(metadata,
DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_BY_VALUE));
+ map.merge(BrokerResponseNativeV2.StatKey.NUM_REMOTE_ACCESSES,
+ getLongMetadataValue(metadata,
DataTable.MetadataKey.NUM_REMOTE_ACCESSES));
+ map.merge(BrokerResponseNativeV2.StatKey.REMOTE_ACCESS_BYTES,
+ getLongMetadataValue(metadata,
DataTable.MetadataKey.REMOTE_ACCESS_BYTES));
brokerResponse.addBrokerStats(map);
brokerResponse.setTimeUsedMs(getLongMetadataValue(metadata,
DataTable.MetadataKey.TIME_USED_MS));
}
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/operator/InstanceResponseOperator.java
b/pinot-core/src/main/java/org/apache/pinot/core/operator/InstanceResponseOperator.java
index 031b5214496..5d63ac36cdd 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/operator/InstanceResponseOperator.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/operator/InstanceResponseOperator.java
@@ -99,6 +99,16 @@ public class InstanceResponseOperator extends
BaseOperator<InstanceResponseBlock
String.valueOf(_threadMemAllocatedBytes));
instanceResponseBlock.addMetadata(MetadataKey.SYSTEM_ACTIVITIES_CPU_TIME_NS.getName(),
String.valueOf(_systemActivitiesCpuTimeNs));
+ long numRemoteAccesses = 0;
+ long remoteAccessBytes = 0;
+ for (FetchContext fetchContext : _fetchContexts) {
+ numRemoteAccesses += fetchContext.getNumRemoteAccesses();
+ remoteAccessBytes += fetchContext.getRemoteAccessBytes();
+ }
+ if (numRemoteAccesses > 0) {
+
instanceResponseBlock.addMetadata(MetadataKey.NUM_REMOTE_ACCESSES.getName(),
Long.toString(numRemoteAccesses));
+
instanceResponseBlock.addMetadata(MetadataKey.REMOTE_ACCESS_BYTES.getName(),
Long.toString(remoteAccessBytes));
+ }
Integer implicitLimit =
QueryOptionsUtils.getLiteModeImplicitLeafStageLimit(
_queryContext.getQueryOptions());
// false-positive when table has exactly implicitLimit rows
diff --git
a/pinot-core/src/main/java/org/apache/pinot/core/query/reduce/ExecutionStatsAggregator.java
b/pinot-core/src/main/java/org/apache/pinot/core/query/reduce/ExecutionStatsAggregator.java
index 643b9fe606c..15cc51ef7b9 100644
---
a/pinot-core/src/main/java/org/apache/pinot/core/query/reduce/ExecutionStatsAggregator.java
+++
b/pinot-core/src/main/java/org/apache/pinot/core/query/reduce/ExecutionStatsAggregator.java
@@ -71,6 +71,8 @@ public class ExecutionStatsAggregator {
private long _numSegmentsPrunedInvalid = 0L;
private long _numSegmentsPrunedByLimit = 0L;
private long _numSegmentsPrunedByValue = 0L;
+ private long _numRemoteAccesses = 0L;
+ private long _remoteAccessBytes = 0L;
private long _explainPlanNumEmptyFilterSegments = 0L;
private long _explainPlanNumMatchAllFilterSegments = 0L;
private boolean _groupsTrimmed = false;
@@ -217,6 +219,8 @@ public class ExecutionStatsAggregator {
l -> _numSegmentsPrunedByLimit += l);
withNotNullLongMetadata(metadata,
DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_BY_VALUE,
l -> _numSegmentsPrunedByValue += l);
+ withNotNullLongMetadata(metadata,
DataTable.MetadataKey.NUM_REMOTE_ACCESSES, l -> _numRemoteAccesses += l);
+ withNotNullLongMetadata(metadata,
DataTable.MetadataKey.REMOTE_ACCESS_BYTES, l -> _remoteAccessBytes += l);
String explainPlanNumEmptyFilterSegments =
metadata.get(DataTable.MetadataKey.EXPLAIN_PLAN_NUM_EMPTY_FILTER_SEGMENTS.getName());
@@ -303,6 +307,8 @@ public class ExecutionStatsAggregator {
brokerResponseNative.setNumSegmentsPrunedInvalid(_numSegmentsPrunedInvalid);
brokerResponseNative.setNumSegmentsPrunedByLimit(_numSegmentsPrunedByLimit);
brokerResponseNative.setNumSegmentsPrunedByValue(_numSegmentsPrunedByValue);
+ brokerResponseNative.setNumRemoteAccesses(_numRemoteAccesses);
+ brokerResponseNative.setRemoteAccessBytes(_remoteAccessBytes);
brokerResponseNative.setExplainPlanNumEmptyFilterSegments(_explainPlanNumEmptyFilterSegments);
brokerResponseNative.setExplainPlanNumMatchAllFilterSegments(_explainPlanNumMatchAllFilterSegments);
if (_numConsumingSegmentsQueried > 0) {
@@ -400,6 +406,8 @@ public class ExecutionStatsAggregator {
putLong(metadata, DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_INVALID,
_numSegmentsPrunedInvalid);
putLong(metadata, DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_BY_LIMIT,
_numSegmentsPrunedByLimit);
putLong(metadata, DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_BY_VALUE,
_numSegmentsPrunedByValue);
+ putLong(metadata, DataTable.MetadataKey.NUM_REMOTE_ACCESSES,
_numRemoteAccesses);
+ putLong(metadata, DataTable.MetadataKey.REMOTE_ACCESS_BYTES,
_remoteAccessBytes);
putLong(metadata,
DataTable.MetadataKey.EXPLAIN_PLAN_NUM_EMPTY_FILTER_SEGMENTS,
_explainPlanNumEmptyFilterSegments);
putLong(metadata,
DataTable.MetadataKey.EXPLAIN_PLAN_NUM_MATCH_ALL_FILTER_SEGMENTS,
diff --git
a/pinot-core/src/test/java/org/apache/pinot/core/operator/InstanceResponseOperatorTest.java
b/pinot-core/src/test/java/org/apache/pinot/core/operator/InstanceResponseOperatorTest.java
index 92a2e1194da..ac700099b3b 100644
---
a/pinot-core/src/test/java/org/apache/pinot/core/operator/InstanceResponseOperatorTest.java
+++
b/pinot-core/src/test/java/org/apache/pinot/core/operator/InstanceResponseOperatorTest.java
@@ -21,11 +21,14 @@ package org.apache.pinot.core.operator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
import org.apache.pinot.common.datatable.DataTable.MetadataKey;
import org.apache.pinot.core.operator.blocks.InstanceResponseBlock;
import org.apache.pinot.core.operator.blocks.results.SelectionResultsBlock;
import org.apache.pinot.core.operator.combine.BaseCombineOperator;
import org.apache.pinot.core.query.request.context.QueryContext;
+import org.apache.pinot.segment.spi.FetchContext;
import
org.apache.pinot.spi.utils.CommonConstants.Broker.Request.QueryOptionKey;
import org.testng.annotations.Test;
@@ -124,4 +127,37 @@ public class InstanceResponseOperatorTest {
assertTrue(responseBlock.getResponseMetadata()
.containsKey(MetadataKey.LITE_MODE_LEAF_STAGE_LIMIT_REACHED.getName()));
}
+
+ @Test
+ public void testRemoteAccessesSummedAcrossFetchContexts() {
+ SelectionResultsBlock resultsBlock = mock(SelectionResultsBlock.class);
+ when(resultsBlock.getResultsMetadata()).thenReturn(new HashMap<>());
+ QueryContext queryContext = mock(QueryContext.class);
+ when(queryContext.getQueryOptions()).thenReturn(new HashMap<>());
+ FetchContext fetchContext1 = new FetchContext(UUID.randomUUID(),
"segment1", Set.of("col"));
+ fetchContext1.addRemoteAccesses(3, 300);
+ FetchContext fetchContext2 = new FetchContext(UUID.randomUUID(),
"segment2", Set.of("col"));
+ fetchContext2.addRemoteAccesses(4, 400);
+ InstanceResponseOperator operator = new
InstanceResponseOperator(mock(BaseCombineOperator.class), List.of(),
+ List.of(fetchContext1, fetchContext2), queryContext);
+ InstanceResponseBlock responseBlock =
operator.buildInstanceResponseBlock(resultsBlock);
+ Map<String, String> metadata = responseBlock.getResponseMetadata();
+ assertEquals(metadata.get(MetadataKey.NUM_REMOTE_ACCESSES.getName()), "7");
+ assertEquals(metadata.get(MetadataKey.REMOTE_ACCESS_BYTES.getName()),
"700");
+ }
+
+ @Test
+ public void testNoRemoteAccessMetadataWithoutRemoteAccesses() {
+ SelectionResultsBlock resultsBlock = mock(SelectionResultsBlock.class);
+ when(resultsBlock.getResultsMetadata()).thenReturn(new HashMap<>());
+ QueryContext queryContext = mock(QueryContext.class);
+ when(queryContext.getQueryOptions()).thenReturn(new HashMap<>());
+ FetchContext fetchContext = new FetchContext(UUID.randomUUID(), "segment",
Set.of("col"));
+ InstanceResponseOperator operator = new
InstanceResponseOperator(mock(BaseCombineOperator.class), List.of(),
+ List.of(fetchContext), queryContext);
+ InstanceResponseBlock responseBlock =
operator.buildInstanceResponseBlock(resultsBlock);
+ Map<String, String> metadata = responseBlock.getResponseMetadata();
+
assertFalse(metadata.containsKey(MetadataKey.NUM_REMOTE_ACCESSES.getName()));
+
assertFalse(metadata.containsKey(MetadataKey.REMOTE_ACCESS_BYTES.getName()));
+ }
}
diff --git
a/pinot-core/src/test/java/org/apache/pinot/core/query/reduce/ExecutionStatsAggregatorTest.java
b/pinot-core/src/test/java/org/apache/pinot/core/query/reduce/ExecutionStatsAggregatorTest.java
index 0759fc13d75..e351288ac43 100644
---
a/pinot-core/src/test/java/org/apache/pinot/core/query/reduce/ExecutionStatsAggregatorTest.java
+++
b/pinot-core/src/test/java/org/apache/pinot/core/query/reduce/ExecutionStatsAggregatorTest.java
@@ -29,6 +29,7 @@ import org.apache.pinot.spi.config.table.TableType;
import org.testng.annotations.Test;
import static org.mockito.Mockito.mock;
+import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
@@ -165,4 +166,27 @@ public class ExecutionStatsAggregatorTest {
assertFalse(flags[1]);
assertFalse(flags[2]);
}
+
+ @Test
+ public void testRemoteAccessesSumAcrossServersAndRoundTrip() {
+ DataTable table1 = emptyServerTable();
+ table1.getMetadata().put(MetadataKey.NUM_REMOTE_ACCESSES.getName(), "3");
+ table1.getMetadata().put(MetadataKey.REMOTE_ACCESS_BYTES.getName(), "300");
+ DataTable table2 = emptyServerTable();
+ table2.getMetadata().put(MetadataKey.NUM_REMOTE_ACCESSES.getName(), "4");
+ table2.getMetadata().put(MetadataKey.REMOTE_ACCESS_BYTES.getName(), "400");
+ ExecutionStatsAggregator aggregator = new ExecutionStatsAggregator(false);
+ aggregator.aggregate(SERVER, table1);
+ aggregator.aggregate(SERVER, table2);
+ aggregator.aggregate(SERVER, emptyServerTable());
+ BrokerResponseNative response = new BrokerResponseNative();
+ aggregator.setStats("testTable", response, mock(BrokerMetrics.class));
+ assertEquals(response.getNumRemoteAccesses(), 7L);
+ assertEquals(response.getRemoteAccessBytes(), 700L);
+
+ DataTable merged = emptyServerTable();
+ aggregator.setStatsOnMergedDataTable(merged);
+
assertEquals(merged.getMetadata().get(MetadataKey.NUM_REMOTE_ACCESSES.getName()),
"7");
+
assertEquals(merged.getMetadata().get(MetadataKey.REMOTE_ACCESS_BYTES.getName()),
"700");
+ }
}
diff --git
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/LeafOperator.java
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/LeafOperator.java
index dff4f49b05d..30290091d27 100644
---
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/LeafOperator.java
+++
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/operator/LeafOperator.java
@@ -436,6 +436,12 @@ public class LeafOperator extends MultiStageOperator
implements ExplainableOpera
case NUM_SEGMENTS_PRUNED_BY_VALUE:
_statMap.merge(StatKey.NUM_SEGMENTS_PRUNED_BY_VALUE,
Integer.parseInt(entry.getValue()));
break;
+ case NUM_REMOTE_ACCESSES:
+ _statMap.merge(StatKey.NUM_REMOTE_ACCESSES,
Long.parseLong(entry.getValue()));
+ break;
+ case REMOTE_ACCESS_BYTES:
+ _statMap.merge(StatKey.REMOTE_ACCESS_BYTES,
Long.parseLong(entry.getValue()));
+ break;
case EXPLAIN_PLAN_NUM_EMPTY_FILTER_SEGMENTS:
LOGGER.debug("Skipping empty filter segments: {}", entry.getValue());
break;
@@ -798,7 +804,11 @@ public class LeafOperator extends MultiStageOperator
implements ExplainableOpera
/// given something to read, which is the common case. A worker with no
segment was given no work at all, which
/// is different from a worker that read segments and produced nothing:
compare this against the `parallelism`
/// the stats tree renders on this node, and against what the operators
above it report.
- NON_ACTIVE_WORKERS(StatMap.Type.INT, null);
+ NON_ACTIVE_WORKERS(StatMap.Type.INT, null),
+ /// Number of remote accesses (local cache misses) made to read segment
data from a remote storage tier.
+ NUM_REMOTE_ACCESSES(StatMap.Type.LONG),
+ /// Number of bytes read by the remote accesses.
+ REMOTE_ACCESS_BYTES(StatMap.Type.LONG);
// IMPORTANT: When adding new StatKeys, make sure to either create the
same key in BrokerResponseNativeV2.StatKey or
// call the constructor that accepts a String as last argument and set it
to null.
// Otherwise the constructor will fail with an IllegalArgumentException
which will not be caught and will
diff --git
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/timeseries/TimeSeriesExchangeReceiveOperator.java
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/timeseries/TimeSeriesExchangeReceiveOperator.java
index 649cc607dc2..bfede821faf 100644
---
a/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/timeseries/TimeSeriesExchangeReceiveOperator.java
+++
b/pinot-query-runtime/src/main/java/org/apache/pinot/query/runtime/timeseries/TimeSeriesExchangeReceiveOperator.java
@@ -68,7 +68,9 @@ public class TimeSeriesExchangeReceiveOperator extends
BaseTimeSeriesOperator {
DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_BY_SERVER,
DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_INVALID,
DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_BY_LIMIT,
- DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_BY_VALUE
+ DataTable.MetadataKey.NUM_SEGMENTS_PRUNED_BY_VALUE,
+ DataTable.MetadataKey.NUM_REMOTE_ACCESSES,
+ DataTable.MetadataKey.REMOTE_ACCESS_BYTES
);
private static final List<DataTable.MetadataKey> MIN_STATS_KEYS = List.of(
diff --git
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/LeafOperatorTest.java
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/LeafOperatorTest.java
index 237d7033ff0..69d5baff59a 100644
---
a/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/LeafOperatorTest.java
+++
b/pinot-query-runtime/src/test/java/org/apache/pinot/query/runtime/operator/LeafOperatorTest.java
@@ -546,6 +546,39 @@ public class LeafOperatorTest {
operator.close();
}
+ @Test
+ public void shouldPropagateRemoteAccessStats() {
+ // Given:
+ DataSchema schema = new DataSchema(new String[]{"intCol"},
+ new DataSchema.ColumnDataType[]{DataSchema.ColumnDataType.INT});
+ InstanceResponseBlock metadataBlock = new InstanceResponseBlock(new
MetadataResultsBlock());
+
metadataBlock.getResponseMetadata().put(DataTable.MetadataKey.NUM_REMOTE_ACCESSES.getName(),
"7");
+
metadataBlock.getResponseMetadata().put(DataTable.MetadataKey.REMOTE_ACCESS_BYTES.getName(),
"700");
+ QueryExecutor queryExecutor = mockQueryExecutor(List.of(), metadataBlock);
+ LeafOperator operator =
+ new LeafOperator(OperatorTestUtil.getTracingContext(),
mockQueryRequests(1), schema, queryExecutor,
+ _executorService);
+ _operatorRef.set(operator);
+
+ // When:
+ assertTrue(operator.nextBlock().isEos(), "Expected EOS after reading the
metadata block");
+
+ // Then:
+ StatMap<LeafOperator.StatKey> leafStats = operator.copyStatMaps();
+ assertEquals(leafStats.getLong(LeafOperator.StatKey.NUM_REMOTE_ACCESSES),
7L);
+ assertEquals(leafStats.getLong(LeafOperator.StatKey.REMOTE_ACCESS_BYTES),
700L);
+
+ BrokerResponseNativeV2 brokerResponse = new BrokerResponseNativeV2();
+ MultiStageOperator.Type.LEAF.mergeInto(brokerResponse, leafStats);
+ assertEquals(brokerResponse.getNumRemoteAccesses(), 7L);
+ assertEquals(brokerResponse.getRemoteAccessBytes(), 700L);
+ JsonNode responseJson = JsonUtils.objectToJsonNode(brokerResponse);
+ assertEquals(responseJson.path("numRemoteAccesses").asLong(), 7L);
+ assertEquals(responseJson.path("remoteAccessBytes").asLong(), 700L);
+
+ operator.close();
+ }
+
@Test
public void shouldSkipNoneEarlyTerminationReason() {
assertSkippedEarlyTerminationReason(EarlyTerminationReason.NONE.name());
diff --git
a/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/FetchContext.java
b/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/FetchContext.java
index c2bd5b10177..9949aa44636 100644
---
a/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/FetchContext.java
+++
b/pinot-segment-spi/src/main/java/org/apache/pinot/segment/spi/FetchContext.java
@@ -23,14 +23,21 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
+import java.util.concurrent.atomic.AtomicLong;
import org.apache.pinot.segment.spi.index.IndexType;
/// The context for fetching buffers of a segment during query.
+///
+/// It also accumulates the remote accesses (local cache misses) performed to
serve the fetch and the bytes they read.
+/// A segment directory backed by a remote storage tier records them here,
typically when the fetched buffers are
+/// released, so that they can be reported in the query stats.
public class FetchContext {
private final UUID _fetchId;
private final String _segmentName;
private final Map<String, List<IndexType<?, ?, ?>>> _columnToIndexList;
+ private final AtomicLong _numRemoteAccesses = new AtomicLong();
+ private final AtomicLong _remoteAccessBytes = new AtomicLong();
/// Create a new FetchRequest for this segment, to fetch all buffers of the
given columns
/// @param fetchId unique fetch id
@@ -75,4 +82,20 @@ public class FetchContext {
public boolean isEmpty() {
return _columnToIndexList.isEmpty();
}
+
+ /// Records remote accesses (local cache misses) performed to serve this
fetch and the bytes they read.
+ public void addRemoteAccesses(long numRemoteAccesses, long
remoteAccessBytes) {
+ _numRemoteAccesses.addAndGet(numRemoteAccesses);
+ _remoteAccessBytes.addAndGet(remoteAccessBytes);
+ }
+
+ /// Returns the number of remote accesses (local cache misses) recorded for
this fetch.
+ public long getNumRemoteAccesses() {
+ return _numRemoteAccesses.get();
+ }
+
+ /// Returns the number of bytes read by the remote accesses recorded for
this fetch.
+ public long getRemoteAccessBytes() {
+ return _remoteAccessBytes.get();
+ }
}
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]