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 42522a6a IGNITE-21990 Fix performance statistics query records
aggregation - Fixes #259.
42522a6a is described below
commit 42522a6ae8404b07c97bab63de2730b1500d28c3
Author: Aleksey Plekhanov <[email protected]>
AuthorDate: Thu May 30 11:00:43 2024 +0300
IGNITE-21990 Fix performance statistics query records aggregation - Fixes
#259.
Signed-off-by: Aleksey Plekhanov <[email protected]>
---
.../performance-statistics-ext/report/js/sqlTab.js | 2 +-
.../handlers/QueryHandler.java | 115 ++++++++++++++-------
.../PerformanceStatisticsReportSelfTest.java | 95 ++++++++++++++++-
3 files changed, 173 insertions(+), 39 deletions(-)
diff --git a/modules/performance-statistics-ext/report/js/sqlTab.js
b/modules/performance-statistics-ext/report/js/sqlTab.js
index 70b2b9c8..de04153f 100644
--- a/modules/performance-statistics-ext/report/js/sqlTab.js
+++ b/modules/performance-statistics-ext/report/js/sqlTab.js
@@ -159,7 +159,7 @@ function buildPropertiesSubTable($el, properties) {
$.each(properties, function (k, prop) {
data.push({
- name: k,
+ name: prop["name"],
value: prop["value"],
count: prop["count"]
});
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 a8294bd2..435767c3 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
@@ -46,10 +46,10 @@ import static
org.apache.ignite.internal.performancestatistics.util.Utils.MAPPER
* "logicalReads" : $logicalReads,
* "physicalReads" : $physicalReads,
* "failures" : $failures,
- * "properties" : {
- * $propName : {"value" : $propValue, "count" : $propCount},
+ * "properties" : [
+ * {"name" : $propName, "value" : $propValue, "count" :
$propCount},
* ...
- * },
+ * ],
* "rows" : {
* $action : $rowsCount,
* ...
@@ -68,10 +68,10 @@ import static
org.apache.ignite.internal.performancestatistics.util.Utils.MAPPER
* "logicalReads" : $logicalReads,
* "physicalReads" : $physicalReads,
* "success" : $success,
- * "properties" : {
- * $propName : {"value" : $propValue, "count" : $propCount},
+ * "properties" : [
+ * {"name" : $propName, "value" : $propValue, "count" : $propCount},
* ...
- * },
+ * ],
* "rows" : {
* $action : $rowsCount,
* ...
@@ -85,6 +85,10 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
private final Map<GridCacheQueryType, Map<String, AggregatedQueryInfo>>
aggrQuery =
new EnumMap<>(GridCacheQueryType.class);
+ /** Queries results: queryType -> nodeId -> queryId -> aggregatedInfo. */
+ private final Map<GridCacheQueryType, Map<UUID, Map<Long,
AggregatedQueryInfo>>> aggrQryById =
+ new EnumMap<>(GridCacheQueryType.class);
+
/** Parsed reads: queryType -> queryNodeId -> queryId -> reads. */
private final Map<GridCacheQueryType, Map<UUID, Map<Long, long[]>>>
readsById =
new EnumMap<>(GridCacheQueryType.class);
@@ -134,6 +138,13 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
long logicalReads,
long physicalReads
) {
+ AggregatedQueryInfo info = aggregatedQueryInfoById(type, qryNodeId,
id);
+
+ if (info != null) {
+ info.mergeReads(logicalReads, physicalReads);
+ return;
+ }
+
Map<Long, long[]> ids = readsById.computeIfAbsent(type, queryType ->
new HashMap<>())
.computeIfAbsent(qryNodeId, node -> new HashMap<>());
@@ -152,6 +163,13 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
String action,
long rows
) {
+ AggregatedQueryInfo info = aggregatedQueryInfoById(type, qryNodeId,
id);
+
+ if (info != null) {
+ info.mergeRows(action.intern(), rows);
+ return;
+ }
+
Map<String, long[]> actions = rowsById.computeIfAbsent(qryNodeId, node
-> new HashMap<>())
.computeIfAbsent(id, qryId -> new HashMap<>());
@@ -169,11 +187,18 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
String name,
String val
) {
+ String key = (name + '=' + val).intern();
+
+ AggregatedQueryInfo info = aggregatedQueryInfoById(type, qryNodeId,
id);
+
+ if (info != null) {
+ info.mergeProperty(key, name.intern(), val.intern(), 1);
+ return;
+ }
+
Map<String, T3<String, String, long[]>> props =
propsById.computeIfAbsent(qryNodeId, node -> new HashMap<>())
.computeIfAbsent(id, qryId -> new HashMap<>());
- String key = (name + '=' + val).intern();
-
T3<String, String, long[]> prop = props.computeIfAbsent(key,
nv -> new T3<>(name.intern(), val.intern(), new long[] {0}));
@@ -210,6 +235,19 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
return res;
}
+ /**
+ * Gets aggregeted query info by global query id.
+ * @param type Query type.
+ * @param nodeId Query originator node id.
+ * @param id Query id.
+ * @return Aggregated query info.
+ */
+ private AggregatedQueryInfo aggregatedQueryInfoById(GridCacheQueryType
type, UUID nodeId, long id) {
+ Map<UUID, Map<Long, AggregatedQueryInfo>> typeAggrs =
aggrQryById.get(type);
+ Map<Long, AggregatedQueryInfo> nodeAggrs = typeAggrs == null ? null :
typeAggrs.get(nodeId);
+ return nodeAggrs == null ? null : nodeAggrs.get(id);
+ }
+
/**
* Aggregates query reads/rows/properties and remove detailed info.
*/
@@ -221,46 +259,32 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
AggregatedQueryInfo info = typeAggrs.get(qry.text);
+ aggrQryById.computeIfAbsent(qry.type, t -> new HashMap<>())
+ .computeIfAbsent(qry.queryNodeId, n -> new HashMap<>())
+ .put(qry.id, info);
+
// 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 (qryReads != null) {
- info.logicalReads += qryReads[0];
- info.physicalReads += qryReads[1];
- }
+ if (qryReads != null)
+ info.mergeReads(qryReads[0], qryReads[1]);
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;
- }
- }));
- }
+ if (!F.isEmpty(qryProps))
+ qryProps.forEach((propKey, prop) ->
info.mergeProperty(propKey, prop.get1(), prop.get2(), prop.get3()[0]));
// 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;
- }
- }));
- }
+ if (!F.isEmpty(qryRows))
+ qryRows.forEach((act, rows) -> info.mergeRows(act, rows[0]));
}
}
@@ -293,15 +317,16 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
sql.put("failures", info.failures);
if (!F.isEmpty(info.props)) {
- ObjectNode node = MAPPER.createObjectNode();
+ ArrayNode node = MAPPER.createArrayNode();
info.props.forEach((propKey, prop) -> {
ObjectNode valCntNode = MAPPER.createObjectNode();
+ valCntNode.put("name", prop.get1());
valCntNode.put("value", prop.get2());
valCntNode.put("count", prop.get3()[0]);
- node.putIfAbsent(prop.get1(), valCntNode);
+ node.add(valCntNode);
});
sql.putIfAbsent("properties", node);
@@ -351,16 +376,17 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
if (type == GridCacheQueryType.SQL_FIELDS) {
if (propsById.containsKey(query.queryNodeId) &&
propsById.get(query.queryNodeId).containsKey(query.id)) {
- ObjectNode node = MAPPER.createObjectNode();
+ ArrayNode node = MAPPER.createArrayNode();
Collection<T3<String, String, long[]>> props =
propsById.get(query.queryNodeId).get(query.id).values();
props.forEach(prop -> {
ObjectNode valCntNode = MAPPER.createObjectNode();
+ valCntNode.put("name", prop.get1());
valCntNode.put("value", prop.get2());
valCntNode.put("count", prop.get3()[0]);
- node.putIfAbsent(prop.get1(), valCntNode);
+ node.add(valCntNode);
});
json.putIfAbsent("properties", node);
@@ -410,6 +436,23 @@ public class QueryHandler implements
IgnitePerformanceStatisticsHandler {
if (!success)
failures += 1;
}
+
+ /** */
+ public void mergeReads(long logicalReads, long physicalReads) {
+ this.logicalReads += logicalReads;
+ this.physicalReads += physicalReads;
+ }
+
+ /** */
+ public void mergeRows(String action, long cnt) {
+ rows.computeIfAbsent(action, act -> new long[] {0})[0] += cnt;
+ }
+
+ /** */
+ public void mergeProperty(String key, String name, String val, long
cnt) {
+ props.computeIfAbsent(key, k -> new T3<>(name, val, new long[]
{0})).get3()[0] += cnt;
+ }
+
}
/** Query. */
diff --git
a/modules/performance-statistics-ext/src/test/java/org/apache/ignite/internal/performancestatistics/PerformanceStatisticsReportSelfTest.java
b/modules/performance-statistics-ext/src/test/java/org/apache/ignite/internal/performancestatistics/PerformanceStatisticsReportSelfTest.java
index 770c7239..6720f77d 100644
---
a/modules/performance-statistics-ext/src/test/java/org/apache/ignite/internal/performancestatistics/PerformanceStatisticsReportSelfTest.java
+++
b/modules/performance-statistics-ext/src/test/java/org/apache/ignite/internal/performancestatistics/PerformanceStatisticsReportSelfTest.java
@@ -21,6 +21,9 @@ import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+import com.fasterxml.jackson.databind.JsonNode;
import org.apache.ignite.Ignite;
import org.apache.ignite.IgniteCache;
import org.apache.ignite.IgniteException;
@@ -36,8 +39,11 @@ import org.apache.ignite.compute.ComputeTaskAdapter;
import org.apache.ignite.configuration.CacheConfiguration;
import org.apache.ignite.configuration.IgniteConfiguration;
import org.apache.ignite.internal.IgniteEx;
+import org.apache.ignite.internal.performancestatistics.handlers.QueryHandler;
+import org.apache.ignite.internal.processors.cache.query.GridCacheQueryType;
import org.apache.ignite.internal.util.typedef.F;
import org.apache.ignite.internal.util.typedef.internal.U;
+import org.apache.ignite.spi.discovery.tcp.TcpDiscoverySpi;
import org.apache.ignite.transactions.Transaction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -47,7 +53,9 @@ import static
org.apache.ignite.cache.query.IndexQueryCriteriaBuilder.gt;
import static
org.apache.ignite.internal.processors.performancestatistics.AbstractPerformanceStatisticsTest.waitForStatisticsEnabled;
import static
org.apache.ignite.internal.processors.performancestatistics.FilePerformanceStatisticsWriter.PERF_STAT_DIR;
import static
org.apache.ignite.testframework.GridTestUtils.assertThrowsWithCause;
+import static
org.apache.ignite.testframework.junits.GridAbstractTest.LOCAL_IP_FINDER;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
/**
@@ -58,10 +66,12 @@ public class PerformanceStatisticsReportSelfTest {
@Test
public void testCreateReport() throws Exception {
try (
- Ignite srv = Ignition.start(new
IgniteConfiguration().setIgniteInstanceName("srv"));
+ Ignite srv = Ignition.start(new
IgniteConfiguration().setIgniteInstanceName("srv")
+ .setDiscoverySpi(new
TcpDiscoverySpi().setIpFinder(LOCAL_IP_FINDER)));
IgniteEx client = (IgniteEx)Ignition.start(new
IgniteConfiguration()
.setIgniteInstanceName("client")
+ .setDiscoverySpi(new
TcpDiscoverySpi().setIpFinder(LOCAL_IP_FINDER))
.setClientMode(true))
) {
client.context().performanceStatistics().startCollectStatistics();
@@ -106,7 +116,8 @@ public class PerformanceStatisticsReportSelfTest {
cache.query(new SqlFieldsQuery("select * from
sys.tables").setEnforceJoinOrder(true)).getAll();
- cache.query(new SqlFieldsQuery("select sum(_VAL) from
\"cache\".Integer")).getAll();
+ for (int i = 0; i < 100; i++)
+ cache.query(new SqlFieldsQuery("select sum(_VAL) from
\"cache\".Integer")).getAll();
cache.query(new IndexQuery<>(Integer.class).setCriteria(gt("_KEY",
0))).getAll();
@@ -139,6 +150,86 @@ public class PerformanceStatisticsReportSelfTest {
}
}
+ /** @throws Exception If failed. */
+ @Test
+ public void testQueryHandlerAggregation() throws Exception {
+ QueryHandler qryHnd = new QueryHandler();
+
+ for (int nodeIdx = 0; nodeIdx < 10; nodeIdx++) {
+ UUID nodeId = new UUID(0, nodeIdx);
+
+ for (long id = 0; id < 1000; id++) {
+ String text = "query" + (id / 100);
+ UUID origNodeId = new UUID(0, id % 10);
+ qryHnd.queryReads(nodeId, GridCacheQueryType.SQL_FIELDS,
origNodeId, id, 1, 1);
+ qryHnd.queryRows(nodeId, GridCacheQueryType.SQL_FIELDS,
origNodeId, id, "ROWS", 1);
+ qryHnd.queryRows(nodeId, GridCacheQueryType.SQL_FIELDS,
origNodeId, id, "ROWSx2", 2);
+ qryHnd.queryProperty(nodeId, GridCacheQueryType.SQL_FIELDS,
origNodeId, id, "prop1", "val1");
+ qryHnd.queryProperty(nodeId, GridCacheQueryType.SQL_FIELDS,
origNodeId, id, "prop1", "val2");
+ qryHnd.queryProperty(nodeId, GridCacheQueryType.SQL_FIELDS,
origNodeId, id, "prop2", "val2");
+ if (nodeId.equals(origNodeId)) {
+ qryHnd.query(nodeId, GridCacheQueryType.SQL_FIELDS, text,
id, 0,
+ TimeUnit.MILLISECONDS.toNanos(id), true);
+ }
+ }
+ }
+
+ Map<String, JsonNode> res = qryHnd.results();
+ JsonNode aggrSql = res.get("sql");
+ assertEquals(10, aggrSql.size());
+
+ for (int i = 0; i < 10; i++) {
+ JsonNode aggrQry = aggrSql.get("query" + i);
+ assertNotNull(aggrQry);
+ assertEquals(100, aggrQry.get("count").asInt());
+
+ assertEquals(1000, aggrQry.get("logicalReads").asInt());
+ assertEquals(1000, aggrQry.get("physicalReads").asInt());
+
+ JsonNode props = aggrQry.get("properties");
+ assertNotNull(props);
+ assertEquals(3, props.size());
+ for (int j = 0; j < 3; j++) {
+ JsonNode prop = props.get(j);
+ assertNotNull(prop);
+ assertTrue(prop.get("name").asText().startsWith("prop"));
+ assertTrue(prop.get("value").asText().startsWith("val"));
+ assertEquals(1000, prop.get("count").asInt());
+ }
+
+ JsonNode rows = aggrQry.get("rows");
+ assertNotNull(rows);
+ assertEquals(1000, rows.get("ROWS").asInt());
+ assertEquals(2000, rows.get("ROWSx2").asInt());
+ }
+
+ JsonNode slowSql = res.get("topSlowSql");
+ assertEquals(30, slowSql.size());
+
+ for (int i = 0; i < 30; i++) {
+ JsonNode slowQry = slowSql.get(i);
+ assertNotNull(slowQry);
+ assertEquals(10, slowQry.get("logicalReads").asInt());
+ assertEquals(10, slowQry.get("physicalReads").asInt());
+
+ JsonNode props = slowQry.get("properties");
+ assertNotNull(props);
+ assertEquals(3, props.size());
+ for (int j = 0; j < 3; j++) {
+ JsonNode prop = props.get(j);
+ assertNotNull(prop);
+ assertTrue(prop.get("name").asText().startsWith("prop"));
+ assertTrue(prop.get("value").asText().startsWith("val"));
+ assertEquals(10, prop.get("count").asInt());
+ }
+
+ JsonNode rows = slowQry.get("rows");
+ assertNotNull(rows);
+ assertEquals(10, rows.get("ROWS").asInt());
+ assertEquals(20, rows.get("ROWSx2").asInt());
+ }
+ }
+
/** */
private static class TaskWithoutJobs extends ComputeTaskAdapter<Object,
Object> {
/** {@inheritDoc} */