You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pinot.apache.org by xi...@apache.org on 2026/04/19 02:20:05 UTC

(pinot) branch master updated: feat: expose BrokerResponse to subclasses via overridable onQueryCompletion hook (#18247)

This is an automated email from the ASF dual-hosted git repository.

xiangfu0 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 3eff094baa2 feat: expose BrokerResponse to subclasses via overridable onQueryCompletion hook (#18247)
3eff094baa2 is described below

commit 3eff094baa2164a2592462a81fce1ee7738d28be
Author: st-omarkhalid <om...@startree.ai>
AuthorDate: Sat Apr 18 21:19:59 2026 -0500

    feat: expose BrokerResponse to subclasses via overridable onQueryCompletion hook (#18247)
    
    * feat: expose BrokerResponse to subclasses via overridable onQueryCompletion hook
    
    Add a protected `onQueryCompletion(RequestContext, BrokerResponse)` method to
    `BaseBrokerRequestHandler` so subclasses can intercept the fully-populated response
    (serverStats, partialResult, pools, rlsFiltersApplied, etc.) without changing the
    `BrokerQueryEventListener` SPI.
    
    Add a protected `createSingleStageBrokerRequestHandler(...)` factory method to
    `BaseBrokerStarter` so subclasses (e.g. StarTreePinotBrokerStarter) can supply a
    custom `SingleConnectionBrokerRequestHandler` subclass that overrides the hook.
    
    The default behaviour is unchanged: the existing SPI listener is still invoked.
    
    Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
    
    * test: add unit test for onQueryCompletion hook in BaseBrokerRequestHandler
    
    Verifies that the protected onQueryCompletion hook is invoked with a
    non-null BrokerResponse after every query, covering the new hook added
    alongside the factory method in BaseBrokerStarter.
    
    Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
    
    * fix: sort BrokerResponse import alphabetically to satisfy Spotless
    
    Co-Authored-By: Claude Sonnet 4.6 <no...@anthropic.com>
    
    ---------
    
    Co-authored-by: Claude Sonnet 4.6 <no...@anthropic.com>
---
 .../broker/broker/helix/BaseBrokerStarter.java     | 20 ++++++++-
 .../requesthandler/BaseBrokerRequestHandler.java   | 13 +++++-
 .../BaseSingleStageBrokerRequestHandlerTest.java   | 50 ++++++++++++++++++++++
 3 files changed, 81 insertions(+), 2 deletions(-)

diff --git a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java
index 790b632019b..1a30dea0ea7 100644
--- a/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java
+++ b/pinot-broker/src/main/java/org/apache/pinot/broker/broker/helix/BaseBrokerStarter.java
@@ -47,6 +47,7 @@ import org.apache.pinot.broker.broker.AccessControlFactory;
 import org.apache.pinot.broker.broker.BrokerAdminApiApplication;
 import org.apache.pinot.broker.grpc.BrokerGrpcServer;
 import org.apache.pinot.broker.queryquota.HelixExternalViewBasedQueryQuotaManager;
+import org.apache.pinot.broker.queryquota.QueryQuotaManager;
 import org.apache.pinot.broker.requesthandler.BaseSingleStageBrokerRequestHandler;
 import org.apache.pinot.broker.requesthandler.BrokerRequestHandler;
 import org.apache.pinot.broker.requesthandler.BrokerRequestHandlerDelegate;
@@ -249,6 +250,23 @@ public abstract class BaseBrokerStarter implements ServiceStartable {
     return new WorkerManager(brokerId, hostname, port, routingManager);
   }
 
+  /**
+   * Override to supply a custom {@link SingleConnectionBrokerRequestHandler} subclass (e.g. one
+   * that overrides {@code onQueryCompletion(RequestContext, BrokerResponse)} for async query
+   * logging). The default implementation returns a plain {@link SingleConnectionBrokerRequestHandler}.
+   */
+  protected SingleConnectionBrokerRequestHandler createSingleStageBrokerRequestHandler(
+      PinotConfiguration config, String brokerId, BrokerRequestIdGenerator requestIdGenerator,
+      RoutingManager routingManager, AccessControlFactory accessControlFactory,
+      QueryQuotaManager queryQuotaManager, TableCache tableCache, NettyConfig nettyConfig,
+      TlsConfig tlsConfig, ServerRoutingStatsManager serverRoutingStatsManager,
+      FailureDetector failureDetector, ThreadAccountant threadAccountant,
+      MultiClusterRoutingContext multiClusterRoutingContext) {
+    return new SingleConnectionBrokerRequestHandler(config, brokerId, requestIdGenerator, routingManager,
+        accessControlFactory, queryQuotaManager, tableCache, nettyConfig, tlsConfig,
+        serverRoutingStatsManager, failureDetector, threadAccountant, multiClusterRoutingContext);
+  }
+
   private void setupHelixSystemProperties() {
     // NOTE: Helix will disconnect the manager and disable the instance if it detects flapping (too frequent disconnect
     // from ZooKeeper). Setting flapping time window to a small value can avoid this from happening. Helix ignores the
@@ -440,7 +458,7 @@ public abstract class BaseBrokerStarter implements ServiceStartable {
         brokerContext.setServerHttpsContext(sslContext);
       }
       singleStageBrokerRequestHandler =
-          new SingleConnectionBrokerRequestHandler(_brokerConf, brokerId, requestIdGenerator, _routingManager,
+          createSingleStageBrokerRequestHandler(_brokerConf, brokerId, requestIdGenerator, _routingManager,
               _accessControlFactory, _queryQuotaManager, _tableCache, nettyDefaults, tlsDefaults,
               _serverRoutingStatsManager, _failureDetector, _threadAccountant, multiClusterRoutingContext);
     }
diff --git a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseBrokerRequestHandler.java b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseBrokerRequestHandler.java
index 30d09fa2360..1517b698a9d 100644
--- a/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseBrokerRequestHandler.java
+++ b/pinot-broker/src/main/java/org/apache/pinot/broker/requesthandler/BaseBrokerRequestHandler.java
@@ -219,11 +219,22 @@ public abstract class BaseBrokerRequestHandler implements BrokerRequestHandler {
             accessControl);
     brokerResponse.setBrokerId(_brokerId);
     brokerResponse.setRequestId(Long.toString(requestId));
-    _brokerQueryEventListener.onQueryCompletion(requestContext);
+    onQueryCompletion(requestContext, brokerResponse);
 
     return brokerResponse;
   }
 
+  /**
+   * Called after every successfully executed query with the fully-populated {@link RequestContext}
+   * and {@link BrokerResponse}. The default implementation fires the configured
+   * {@link org.apache.pinot.spi.eventlistener.query.BrokerQueryEventListener}. Subclasses may
+   * override to intercept the complete response (e.g. for async query-log pipelines) while still
+   * calling {@code super} to preserve the SPI listener behaviour.
+   */
+  protected void onQueryCompletion(RequestContext requestContext, BrokerResponse brokerResponse) {
+    _brokerQueryEventListener.onQueryCompletion(requestContext);
+  }
+
   protected abstract BrokerResponse handleRequest(long requestId, String query, SqlNodeAndOptions sqlNodeAndOptions,
       JsonNode request, @Nullable RequesterIdentity requesterIdentity, RequestContext requestContext,
       @Nullable HttpHeaders httpHeaders, AccessControl accessControl)
diff --git a/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandlerTest.java b/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandlerTest.java
index ca216e1b145..aa31a46f82e 100644
--- a/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandlerTest.java
+++ b/pinot-broker/src/test/java/org/apache/pinot/broker/requesthandler/BaseSingleStageBrokerRequestHandlerTest.java
@@ -35,6 +35,7 @@ import org.apache.pinot.common.request.BrokerRequest;
 import org.apache.pinot.common.request.Expression;
 import org.apache.pinot.common.request.Function;
 import org.apache.pinot.common.request.PinotQuery;
+import org.apache.pinot.common.response.BrokerResponse;
 import org.apache.pinot.common.response.broker.BrokerResponseNative;
 import org.apache.pinot.core.routing.RoutingTable;
 import org.apache.pinot.core.routing.SegmentsToQuery;
@@ -95,6 +96,55 @@ public class BaseSingleStageBrokerRequestHandlerTest {
     }
   }
 
+  @Test
+  public void testOnQueryCompletionHookReceivesBrokerResponse() {
+    // Verify that the overridable onQueryCompletion(RequestContext, BrokerResponse) hook is invoked
+    // and receives the BrokerResponse that handleRequest() produced.
+    AtomicReference<BrokerResponse> capturedResponse = new AtomicReference<>();
+
+    PinotConfiguration config = new PinotConfiguration();
+    BrokerQueryEventListenerFactory.init(config);
+    BrokerMetrics.register(mock(BrokerMetrics.class));
+    QueryQuotaManager queryQuotaManager = mock(QueryQuotaManager.class);
+    when(queryQuotaManager.acquire(anyString())).thenReturn(true);
+    when(queryQuotaManager.acquireDatabase(anyString())).thenReturn(true);
+    when(queryQuotaManager.acquireApplication(anyString())).thenReturn(true);
+    TableCache tableCache = mock(TableCache.class);
+
+    BaseSingleStageBrokerRequestHandler handler =
+        new BaseSingleStageBrokerRequestHandler(config, "testBrokerId", new BrokerRequestIdGenerator(),
+            mock(org.apache.pinot.core.routing.RoutingManager.class), new AllowAllAccessControlFactory(),
+            queryQuotaManager, tableCache, ThreadAccountantUtils.getNoOpAccountant(), null) {
+          @Override
+          public void start() {
+          }
+
+          @Override
+          public void shutDown() {
+          }
+
+          @Override
+          protected BrokerResponseNative processBrokerRequest(long requestId, BrokerRequest originalBrokerRequest,
+              BrokerRequest serverBrokerRequest, TableRouteInfo route, long timeoutMs, ServerStats serverStats,
+              RequestContext requestContext) {
+            return new BrokerResponseNative();
+          }
+
+          @Override
+          protected void onQueryCompletion(RequestContext requestContext, BrokerResponse brokerResponse) {
+            capturedResponse.set(brokerResponse);
+          }
+        };
+
+    try {
+      handler.handleRequest("SELECT 1");
+    } catch (Exception ignored) {
+      // routing may fail — we only care that the hook was called with a non-null response
+    }
+    Assert.assertNotNull(capturedResponse.get(),
+        "onQueryCompletion hook must be called with the BrokerResponse from handleRequest");
+  }
+
   @Test
   public void testGetActualColumnNameCaseSensitive() {
     Map<String, String> columnNameMap = new HashMap<>();


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@pinot.apache.org
For additional commands, e-mail: commits-help@pinot.apache.org