yashmayya commented on code in PR #19816:
URL: https://github.com/apache/pinot/pull/19816#discussion_r4264834096


##########
pinot-integration-tests/src/test/java/org/apache/pinot/integration/tests/MultiNodesOfflineClusterIntegrationTest.java:
##########
@@ -326,6 +326,35 @@ public void testConstantExpressionQuery()
     assertEquals(result.get("numServersQueried").intValue(), 1);
   }
 
+  @Test
+  public void testMultiStageTotalMemAllocatedBytes()
+      throws Exception {
+    setUseMultiStageQueryEngine(true);
+
+    // The semi-join query uses a pipeline breaker
+    for (String query : List.of("SELECT Carrier, COUNT(*) FROM mytable GROUP 
BY Carrier",
+        "SELECT Carrier FROM mytable WHERE Origin IN (SELECT Origin FROM 
mytable WHERE ArrDelay > 0) LIMIT 10")) {
+      JsonNode result = postQuery(query);
+      long totalMemAllocatedBytes = 
result.path("totalMemAllocatedBytes").asLong();
+      assertTrue(totalMemAllocatedBytes > 0);
+      assertEquals(totalMemAllocatedBytes, 
sumAllocatedBytes(result.get("stageStats"), true));
+    }
+  }
+
+  /// Sums the allocation of each op chain root (the top node, each 
MAILBOX_SEND and each pipeline breaker, which is
+  /// rendered as the child of its LEAF) and of the single-stage threads of 
each LEAF.
+  private static long sumAllocatedBytes(JsonNode node, boolean isOpChainRoot) {
+    long bytes = node.path("threadMemAllocatedBytes").asLong();
+    if (isOpChainRoot) {
+      bytes += node.path("allocatedMemoryBytes").asLong();
+    }
+    boolean isLeaf = node.path("type").asText().equals("LEAF");
+    for (JsonNode child : node.path("children")) {
+      bytes += sumAllocatedBytes(child, isLeaf || 
child.path("type").asText().equals("MAILBOX_SEND"));

Review Comment:
   Thanks for checking this closely. I verified it locally. In the default 
(legacy) stats mode, the node under `LEAF` carries the breaker's own stats. 
Only its label is `MAILBOX_RECEIVE`.
   
   - `InStageStatsTreeBuilder.extractPipelineBreakerResult()` moves `_index` to 
the `PIPELINE_BREAKER` entry. Then `visitMailboxReceive()` calls 
`selfNode(MAILBOX_RECEIVE, context)`, which copies 
`_stageStats.getOperatorStats(_index)`, that is, the breaker's stat map. The 
breaker's receive operator is not rendered.
   - I ran this test with temporary logging of the raw leaf-stage stats. The 
allocated bytes were `MAILBOX_RECEIVE=14256`, `PIPELINE_BREAKER=19944`, 
`LEAF=2232` and `MAILBOX_SEND=136616`. The JSON node under `LEAF` had 
`allocatedMemoryBytes=19944`, with only the breaker's keys (`executionTimeMs`, 
`emittedRows`, `allocatedMemoryBytes`) and no `fanIn` or `upstreamWaitMs`. The 
exact equality held.
   - When I removed the `PIPELINE_BREAKER` addition in `mergeInto`, the test 
failed by exactly the bytes of that node.
   
   Your point is correct for stream mode: 
`MultiStageStatsTreeBuilder.jsonFromStatsTreeNode()` collapses the breaker and 
shows the stats of the receive. So the test now sets `streamStats = false`, and 
the helper's Javadoc states this.
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to