xiangfu0 commented on code in PR #19982:
URL: https://github.com/apache/pinot/pull/19982#discussion_r4345938744


##########
pinot-core/src/main/java/org/apache/pinot/core/transport/QueryRouter.java:
##########
@@ -214,6 +214,15 @@ void receiveDataTable(ServerRoutingInstance 
serverRoutingInstance, DataTable dat
     }
   }
 
+  // Issue #18866: Notify in-flight queries about server DataTable 
deserialization failure
+  void markServerDeserializationError(ServerRoutingInstance 
serverRoutingInstance, Exception exception,
+      int responseSize, int deserializationTimeMs) {
+    for (AsyncQueryResponse asyncQueryResponse : 
_asyncQueryResponseMap.values()) {

Review Comment:
   A one-line comment here would help: unlike `receiveDataTable`, a 
deserialization failure can't be tied to a single `requestId` (the bytes didn't 
parse), so this has to broadcast to every in-flight query on the server. That's 
consistent with `markServerUnavailable` and safe because the channel is closed 
right after, but the reasoning isn't obvious at first glance.



##########
pinot-core/src/test/java/org/apache/pinot/core/transport/QueryRoutingTest.java:
##########
@@ -473,7 +476,7 @@ public void testServerDown()
       assertTrue(response.containsKey(_offlineServerRoutingInstance));
       serverResponse = response.get(_offlineServerRoutingInstance);
       assertNull(serverResponse.getDataTable());
-      assertEquals(serverResponse.getSubmitDelayMs(), -1);
+      assertTrue(serverResponse.getSubmitDelayMs() >= -1);

Review Comment:
   This assertion was weakened from `assertEquals(getSubmitDelayMs(), -1)`, and 
it looks unrelated to the deserialization fix. `getSubmitDelayMs()` only ever 
returns `-1` or a non-negative value, so `>= -1` is always true and no longer 
checks anything. If the exact match started flaking it'd be worth understanding 
why rather than loosening it; otherwise consider dropping this line to keep the 
PR focused.



##########
pinot-core/src/main/java/org/apache/pinot/core/transport/AsyncQueryResponse.java:
##########
@@ -183,6 +184,26 @@ void markRequestSent(ServerRoutingInstance 
serverRoutingInstance, int requestSen
     
_responseMap.get(serverRoutingInstance).markRequestSent(requestSentLatencyMs);
   }
 
+  // Issue #18866: Handle server DataTable deserialization errors without 
waiting for query timeout
+  void receiveDeserializationError(ServerRoutingInstance 
serverRoutingInstance, Exception exception,
+      int responseSize, int deserializationTimeMs) {
+    if (!shouldActOnServerDown(serverRoutingInstance)) {
+      return;
+    }
+    if (!_countedDownServers.add(serverRoutingInstance)) {
+      return;
+    }
+    _failedServer = serverRoutingInstance;
+    DataTable dataTable = DataTableBuilderFactory.getEmptyDataTable();

Review Comment:
   Good that this synthetic table stays broker-side only and is never 
serialized over the wire — so the `getEmptyDataTable()` builder version carries 
no mixed-version/wire-compat risk here.



##########
pinot-core/src/main/java/org/apache/pinot/core/transport/DataTableHandler.java:
##########
@@ -97,6 +97,13 @@ protected void channelRead0(ChannelHandlerContext ctx, 
ByteBuf msg) {
       LOGGER.error("Caught exception while deserializing data table of size: 
{} from server: {}", responseSize,
           _serverRoutingInstance, e);
       
_brokerMetrics.addMeteredGlobalValue(BrokerMeter.DATA_TABLE_DESERIALIZATION_EXCEPTIONS,
 1);
+      // Issue #18866: Notify QueryRouter of deserialization failure and close 
corrupted channel
+      try {
+        _queryRouter.markServerDeserializationError(_serverRoutingInstance, e, 
responseSize,
+            (int) (System.currentTimeMillis() - deserializationStartTimeMs));
+      } finally {
+        ctx.close();

Review Comment:
   Closing the channel here is reasonable, but it now slightly contradicts the 
`exceptionCaught` comment below ("deserialization ... does not corrupt the 
channel's byte stream"). Could you reconcile the two — e.g. note here why a 
parse failure may leave framing desynced and so warrants a close?



##########
pinot-core/src/main/java/org/apache/pinot/core/transport/AsyncQueryResponse.java:
##########
@@ -183,6 +184,26 @@ void markRequestSent(ServerRoutingInstance 
serverRoutingInstance, int requestSen
     
_responseMap.get(serverRoutingInstance).markRequestSent(requestSentLatencyMs);
   }
 
+  // Issue #18866: Handle server DataTable deserialization errors without 
waiting for query timeout
+  void receiveDeserializationError(ServerRoutingInstance 
serverRoutingInstance, Exception exception,
+      int responseSize, int deserializationTimeMs) {
+    if (!shouldActOnServerDown(serverRoutingInstance)) {
+      return;
+    }
+    if (!_countedDownServers.add(serverRoutingInstance)) {
+      return;
+    }
+    _failedServer = serverRoutingInstance;
+    DataTable dataTable = DataTableBuilderFactory.getEmptyDataTable();
+    dataTable.getMetadata().put(DataTable.MetadataKey.REQUEST_ID.getName(), 
Long.toString(_requestId));
+    dataTable.addException(QueryErrorCode.DESERIALIZATION_ERROR.getId(),
+        exception.getMessage() != null ? exception.getMessage() : 
exception.toString());
+    ServerResponse response = _responseMap.get(serverRoutingInstance);
+    response.receiveDeserializationError(dataTable, responseSize, 
deserializationTimeMs);
+    _numServersResponded.getAndIncrement();
+    _countDownLatch.countDown();

Review Comment:
   Minor / pre-existing: `receiveDataTable` doesn't consult 
`_countedDownServers`, so if a genuine response for the same (query, server) 
ever arrived after this synthetic path, the latch would be counted down twice. 
The `ctx.close()` makes that very unlikely, so not blocking — just noting the 
new path widens that window slightly.



-- 
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