This is an automated email from the ASF dual-hosted git repository.
alexpl pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite-extensions.git
The following commit(s) were added to refs/heads/master by this push:
new 8f901406 IGNITE-21863 Reduce memory consumption by performance
statistics QueryHandler (#258)
8f901406 is described below
commit 8f901406b336d4a4c66ed102e4b022a69baaddd7
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Fri Mar 29 16:34:25 2024 +0300
IGNITE-21863 Reduce memory consumption by performance statistics
QueryHandler (#258)
---
.../handlers/QueryHandler.java | 182 ++++++++++++---------
.../util/OrderedFixedSizeStructure.java | 13 +-
2 files changed, 112 insertions(+), 83 deletions(-)
diff --git
a/modules/performance-statistics-ext/src/main/java/org/apache/ignite/internal/performancestatistics/handlers/QueryHandler.java
b/modules/performance-statistics-ext/src/main/java/org/apache/ignite/internal/performancestatistics/handlers/QueryHandler.java
index d702772d..a8294bd2 100644
---
a/modules/performance-statistics-ext/src/main/java/org/apache/ignite/internal/performancestatistics/handlers/QueryHandler.java
+++
b/modules/performance-statistics-ext/src/main/java/org/apache/ignite/internal/performancestatistics/handlers/QueryHandler.java
@@ -20,9 +20,7 @@ package
org.apache.ignite.internal.performancestatistics.handlers;
import java.util.Collection;
import java.util.EnumMap;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
-import java.util.Set;
import java.util.TreeMap;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
@@ -102,27 +100,42 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
new EnumMap<>(GridCacheQueryType.class);
/** {@inheritDoc} */
- @Override public void query(UUID nodeId, GridCacheQueryType type, String
text, long id, long startTime,
- long duration, boolean success) {
- Query query = new Query(type, text, nodeId, id, startTime, duration,
success);
+ @Override public void query(
+ UUID nodeId,
+ GridCacheQueryType type,
+ String text,
+ long id,
+ long startTime,
+ long duration,
+ boolean success
+ ) {
+ Query qry = new Query(type, text, nodeId, id, startTime, duration,
success);
OrderedFixedSizeStructure<Long, Query> tree =
topSlow.computeIfAbsent(type,
queryType -> new OrderedFixedSizeStructure<>());
- tree.put(duration, query);
-
AggregatedQueryInfo info = aggrQuery.computeIfAbsent(type, queryType
-> new HashMap<>())
.computeIfAbsent(text, queryText -> new AggregatedQueryInfo());
- info.merge(nodeId, id, duration, success);
+ info.merge(duration, success);
+
+ Query evicted = tree.put(duration, qry);
+
+ if (evicted != null)
+ aggregateQuery(evicted);
}
/** {@inheritDoc} */
- @Override public void queryReads(UUID nodeId, GridCacheQueryType type,
UUID queryNodeId, long id, long logicalReads,
- long physicalReads) {
-
+ @Override public void queryReads(
+ UUID nodeId,
+ GridCacheQueryType type,
+ UUID qryNodeId,
+ long id,
+ long logicalReads,
+ long physicalReads
+ ) {
Map<Long, long[]> ids = readsById.computeIfAbsent(type, queryType ->
new HashMap<>())
- .computeIfAbsent(queryNodeId, node -> new HashMap<>());
+ .computeIfAbsent(qryNodeId, node -> new HashMap<>());
long[] readsArr = ids.computeIfAbsent(id, queryId -> new long[] {0,
0});
@@ -169,90 +182,105 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
/** {@inheritDoc} */
@Override public Map<String, JsonNode> results() {
- ObjectNode sqlRes = MAPPER.createObjectNode();
- ObjectNode scanRes = MAPPER.createObjectNode();
- ObjectNode indexRes = MAPPER.createObjectNode();
-
- buildResult(GridCacheQueryType.SQL_FIELDS, sqlRes);
- buildResult(GridCacheQueryType.SCAN, scanRes);
- buildResult(GridCacheQueryType.INDEX, indexRes);
-
ArrayNode topSlowSql = MAPPER.createArrayNode();
ArrayNode topSlowScan = MAPPER.createArrayNode();
- ArrayNode topSlowIndex = MAPPER.createArrayNode();
+ ArrayNode topSlowIdx = MAPPER.createArrayNode();
buildTopSlowResult(GridCacheQueryType.SQL_FIELDS, topSlowSql);
buildTopSlowResult(GridCacheQueryType.SCAN, topSlowScan);
- buildTopSlowResult(GridCacheQueryType.INDEX, topSlowIndex);
+ buildTopSlowResult(GridCacheQueryType.INDEX, topSlowIdx);
+
+ ObjectNode sqlRes = MAPPER.createObjectNode();
+ ObjectNode scanRes = MAPPER.createObjectNode();
+ ObjectNode idxRes = MAPPER.createObjectNode();
+
+ buildResult(GridCacheQueryType.SQL_FIELDS, sqlRes);
+ buildResult(GridCacheQueryType.SCAN, scanRes);
+ buildResult(GridCacheQueryType.INDEX, idxRes);
Map<String, JsonNode> res = new HashMap<>();
res.put("sql", sqlRes);
res.put("scan", scanRes);
- res.put("index", indexRes);
+ res.put("index", idxRes);
res.put("topSlowSql", topSlowSql);
res.put("topSlowScan", topSlowScan);
- res.put("topSlowIndex", topSlowIndex);
+ res.put("topSlowIndex", topSlowIdx);
return res;
}
- /** Builds JSON. */
- private void buildResult(GridCacheQueryType type, ObjectNode jsonRes) {
- if (!aggrQuery.containsKey(type))
+ /**
+ * Aggregates query reads/rows/properties and remove detailed info.
+ */
+ private void aggregateQuery(Query qry) {
+ if (!aggrQuery.containsKey(qry.type))
return;
- Map<String, AggregatedQueryInfo> res = aggrQuery.get(type);
+ Map<String, AggregatedQueryInfo> typeAggrs = aggrQuery.get(qry.type);
- res.forEach((text, info) -> {
- info.ids.forEach((uuid, ids) -> {
- if (readsById.containsKey(type) &&
readsById.get(type).containsKey(uuid)) {
- Map<Long, long[]> reads = readsById.get(type).get(uuid);
+ AggregatedQueryInfo info = typeAggrs.get(qry.text);
- ids.forEach(id -> {
- long[] readsArr = reads.get(id);
+ // Reads.
+ Map<UUID, Map<Long, long[]>> typeReads = readsById.get(qry.type);
+ Map<Long, long[]> nodeReads = typeReads == null ? null :
typeReads.get(qry.queryNodeId);
+ long[] qryReads = nodeReads == null ? null : nodeReads.remove(qry.id);
- if (readsArr != null) {
- info.logicalReads += readsArr[0];
- info.physicalReads += readsArr[1];
- }
- });
- }
+ if (qryReads != null) {
+ info.logicalReads += qryReads[0];
+ info.physicalReads += qryReads[1];
+ }
- if (type == GridCacheQueryType.SQL_FIELDS) {
- Map<Long, Map<String, long[]>> nodeRows =
rowsById.get(uuid);
- Map<Long, Map<String, T3<String, String, long[]>>>
nodeProps = propsById.get(uuid);
-
- ids.forEach(id -> {
- Map<String, T3<String, String, long[]>> qryProps =
nodeProps == null ? null : nodeProps.get(id);
-
- if (!F.isEmpty(qryProps)) {
- qryProps.forEach((propKey0, prop0) ->
info.props.compute(propKey0, (propKey1, prop1) -> {
- if (prop1 == null)
- return new T3<>(prop0.get1(),
prop0.get2(), new long[] {prop0.get3()[0]});
- else {
- prop1.get3()[0] += prop0.get3()[0];
- return prop1;
- }
- }));
- }
-
- Map<String, long[]> qryRows = nodeRows == null ? null
: nodeRows.get(id);
-
- if (!F.isEmpty(qryRows)) {
- qryRows.forEach((act0, rows0) ->
info.rows.compute(act0, (act1, rows1) -> {
- if (rows1 == null)
- return new long[] {rows0[0]};
- else {
- rows1[0] += rows0[0];
- return rows1;
- }
- }));
- }
- });
- }
- });
+ if (qry.type == GridCacheQueryType.SQL_FIELDS) {
+ // Properties.
+ Map<Long, Map<String, T3<String, String, long[]>>> nodeProps =
propsById.get(qry.queryNodeId);
+ Map<String, T3<String, String, long[]>> qryProps = nodeProps ==
null ? null : nodeProps.remove(qry.id);
+
+ if (!F.isEmpty(qryProps)) {
+ qryProps.forEach((propKey0, prop0) ->
info.props.compute(propKey0, (propKey1, prop1) -> {
+ if (prop1 == null)
+ return new T3<>(prop0.get1(), prop0.get2(), new long[]
{prop0.get3()[0]});
+ else {
+ prop1.get3()[0] += prop0.get3()[0];
+ return prop1;
+ }
+ }));
+ }
+
+ // Rows.
+ Map<Long, Map<String, long[]>> nodeRows =
rowsById.get(qry.queryNodeId);
+ Map<String, long[]> qryRows = nodeRows == null ? null :
nodeRows.remove(qry.id);
+
+ if (!F.isEmpty(qryRows)) {
+ qryRows.forEach((act0, rows0) -> info.rows.compute(act0,
(act1, rows1) -> {
+ if (rows1 == null)
+ return new long[] {rows0[0]};
+ else {
+ rows1[0] += rows0[0];
+ return rows1;
+ }
+ }));
+ }
+ }
+ }
+ /** Builds JSON. */
+ private void buildResult(GridCacheQueryType type, ObjectNode jsonRes) {
+ OrderedFixedSizeStructure<Long, Query> topSlowForType =
topSlow.get(type);
+
+ // Up to this moment we've aggregated and removed detailed info for
all queries except top slow. Now (after
+ // result for top slow queries was built) we can aggregate and remove
details for top slow queries too.
+ if (topSlowForType != null) {
+ for (Query qry : topSlowForType.values())
+ aggregateQuery(qry);
+ }
+
+ Map<String, AggregatedQueryInfo> res = aggrQuery.get(type);
+
+ if (res == null)
+ return;
+
+ res.forEach((text, info) -> {
ObjectNode sql = (ObjectNode)jsonRes.get(text);
if (sql == null) {
@@ -374,19 +402,13 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
/** Number of processed rows (by different actions). */
Map<String, long[]> rows = new TreeMap<>();
- /** Query ids. Parsed from global query id: NodeId -> queryIds */
- final Map<UUID, Set<Long>> ids = new HashMap<>();
-
/** */
- public void merge(UUID queryNodeId, long id, long duration, boolean
success) {
+ public void merge(long duration, boolean success) {
count += 1;
totalDuration += duration;
if (!success)
failures += 1;
-
- ids.computeIfAbsent(queryNodeId, k -> new HashSet<>())
- .add(id);
}
}
diff --git
a/modules/performance-statistics-ext/src/main/java/org/apache/ignite/internal/performancestatistics/util/OrderedFixedSizeStructure.java
b/modules/performance-statistics-ext/src/main/java/org/apache/ignite/internal/performancestatistics/util/OrderedFixedSizeStructure.java
index f1168a23..4caf022f 100644
---
a/modules/performance-statistics-ext/src/main/java/org/apache/ignite/internal/performancestatistics/util/OrderedFixedSizeStructure.java
+++
b/modules/performance-statistics-ext/src/main/java/org/apache/ignite/internal/performancestatistics/util/OrderedFixedSizeStructure.java
@@ -18,8 +18,10 @@
package org.apache.ignite.internal.performancestatistics.util;
import java.util.Collection;
+import java.util.Map;
import java.util.NavigableMap;
import java.util.TreeMap;
+import org.jetbrains.annotations.Nullable;
/**
* Data structure for keeping the top N elements in DESC sort order.
@@ -52,19 +54,24 @@ public class OrderedFixedSizeStructure<K extends
Comparable<? super K>, V> {
/**
* @param key Key.
* @param value Value.
+ * @return Evicted value.
*/
- public void put(K key, V value) {
+ public @Nullable V put(K key, V value) {
if (map.size() < capacity) {
map.put(key, value);
- return;
+ return null;
}
if (map.firstKey().compareTo(key) < 0) {
- map.pollFirstEntry();
+ Map.Entry<K, V> old = map.pollFirstEntry();
map.put(key, value);
+
+ return old.getValue();
}
+
+ return value;
}
/** @return Values. */