7777import org .apache .pinot .spi .utils .CommonConstants .Helix ;
7878import org .apache .pinot .spi .utils .CommonConstants .MultiStageQueryRunner ;
7979import org .apache .pinot .spi .utils .CommonConstants .MultiStageQueryRunner .JoinOverFlowMode ;
80+ import org .apache .pinot .spi .utils .CommonConstants .MultiStageQueryRunner .TimeoutOverflowMode ;
8081import org .apache .pinot .spi .utils .CommonConstants .MultiStageQueryRunner .WindowOverFlowMode ;
8182import org .apache .pinot .spi .utils .CommonConstants .Query .Request ;
8283import org .apache .pinot .spi .utils .CommonConstants .Query .Response ;
9899 */
99100public class QueryRunner {
100101 private static final Logger LOGGER = LoggerFactory .getLogger (QueryRunner .class );
102+ private static final double DEFAULT_LEAF_TIMEOUT_RATIO = 0.8 ;
101103
102104 private ExecutorService _executorService ;
103105 private OpChainSchedulerService _opChainScheduler ;
@@ -130,6 +132,8 @@ public class QueryRunner {
130132 @ Nullable
131133 private WindowOverFlowMode _windowOverflowMode ;
132134 @ Nullable
135+ private TimeoutOverflowMode _timeoutOverflowMode ;
136+ @ Nullable
133137 private PhysicalTimeSeriesServerPlanVisitor _timeSeriesPhysicalPlanVisitor ;
134138 /// Cluster-level decision on whether to send stats over the mailbox path, driven by the {@code SendStatsPredicate}
135139 /// at startup time. <b>May be overridden per-request</b> via the {@code KEY_OF_STATS_REPORTING_MODE} metadata key —
@@ -191,6 +195,10 @@ public void init(PinotConfiguration serverConf, String instanceId, @Nullable Ins
191195 String windowOverflowModeStr = serverConf .getProperty (MultiStageQueryRunner .KEY_OF_WINDOW_OVERFLOW_MODE );
192196 _windowOverflowMode = windowOverflowModeStr != null ? WindowOverFlowMode .valueOf (windowOverflowModeStr ) : null ;
193197
198+ String timeoutOverflowModeStr = serverConf .getProperty (MultiStageQueryRunner .KEY_OF_TIMEOUT_OVERFLOW_MODE );
199+ _timeoutOverflowMode =
200+ timeoutOverflowModeStr != null ? TimeoutOverflowMode .valueOf (timeoutOverflowModeStr ) : null ;
201+
194202 ExecutorService baseExecutorService =
195203 ExecutorServiceUtils .create (serverConf , Server .MULTISTAGE_EXECUTOR_CONFIG_PREFIX , "query-runner-on-" + port ,
196204 Server .DEFAULT_MULTISTAGE_EXECUTOR_TYPE );
@@ -301,6 +309,7 @@ private void processQueryBlocking(WorkerMetadata workerMetadata, StagePlan stage
301309 Map <String , String > requestMetadata ) {
302310 StageMetadata stageMetadata = stagePlan .getStageMetadata ();
303311 Map <String , String > opChainMetadata = consolidateMetadata (stageMetadata .getCustomProperties (), requestMetadata );
312+ applyLeafDeadlineOverrides (workerMetadata , opChainMetadata );
304313
305314 // The cluster-level _sendStats decision can be overridden per-request by the SubmitWithStream RPC handler via
306315 // MultiStageQueryRunner.KEY_OF_STATS_REPORTING_MODE; in stream mode stats travel out-of-band
@@ -557,6 +566,14 @@ private Map<String, String> consolidateMetadata(Map<String, String> customProper
557566 opChainMetadata .put (QueryOptionKey .WINDOW_OVERFLOW_MODE , windowOverflowMode .name ());
558567 }
559568
569+ TimeoutOverflowMode timeoutOverflowMode = QueryOptionsUtils .getTimeoutOverflowMode (opChainMetadata );
570+ if (timeoutOverflowMode == null ) {
571+ timeoutOverflowMode = _timeoutOverflowMode ;
572+ }
573+ if (timeoutOverflowMode != null ) {
574+ opChainMetadata .put (QueryOptionKey .TIMEOUT_OVERFLOW_MODE , timeoutOverflowMode .name ());
575+ }
576+
560577 return opChainMetadata ;
561578 }
562579
@@ -604,6 +621,53 @@ public void unregisterOpChainCompletionListener(long requestId) {
604621 _opChainScheduler .unregisterCompletionListener (requestId );
605622 }
606623
624+ private void applyLeafDeadlineOverrides (WorkerMetadata workerMetadata , Map <String , String > opChainMetadata ) {
625+ if (!workerMetadata .isLeafStageWorker ()) {
626+ return ;
627+ }
628+ Long leafTimeoutMs = QueryOptionsUtils .getLeafTimeoutMs (opChainMetadata );
629+ Long leafExtraPassiveTimeoutMs = QueryOptionsUtils .getLeafExtraPassiveTimeoutMs (opChainMetadata );
630+ TimeoutOverflowMode timeoutOverflowMode = _timeoutOverflowMode ;
631+ String timeoutOverflowModeStr = opChainMetadata .get (QueryOptionKey .TIMEOUT_OVERFLOW_MODE );
632+ if (timeoutOverflowModeStr != null ) {
633+ try {
634+ timeoutOverflowMode = TimeoutOverflowMode .valueOf (timeoutOverflowModeStr );
635+ } catch (IllegalArgumentException e ) {
636+ LOGGER .warn ("Invalid timeout overflow mode: {}" , timeoutOverflowModeStr , e );
637+ }
638+ }
639+ QueryThreadContext threadContext = QueryThreadContext .getIfAvailable ();
640+ if (threadContext == null ) {
641+ return ;
642+ }
643+ QueryExecutionContext executionContext = threadContext .getExecutionContext ();
644+ long startTimeMs = executionContext .getStartTimeMs ();
645+ if (leafTimeoutMs == null && timeoutOverflowMode == TimeoutOverflowMode .BREAK ) {
646+ long globalActiveWindowMs = Math .max (1L , executionContext .getActiveDeadlineMs () - startTimeMs );
647+ leafTimeoutMs = Math .max (1L , (long ) Math .floor (globalActiveWindowMs * DEFAULT_LEAF_TIMEOUT_RATIO ));
648+ }
649+ if (leafExtraPassiveTimeoutMs == null && timeoutOverflowMode == TimeoutOverflowMode .BREAK ) {
650+ long globalPassiveWindowMs =
651+ Math .max (0L , executionContext .getPassiveDeadlineMs () - executionContext .getActiveDeadlineMs ());
652+ leafExtraPassiveTimeoutMs =
653+ Math .max (0L , (long ) Math .floor (globalPassiveWindowMs * DEFAULT_LEAF_TIMEOUT_RATIO ));
654+ }
655+ if (leafTimeoutMs == null && leafExtraPassiveTimeoutMs == null ) {
656+ return ;
657+ }
658+ long activeDeadlineMs = executionContext .getActiveDeadlineMs ();
659+ long passiveDeadlineMs = executionContext .getPassiveDeadlineMs ();
660+ if (leafTimeoutMs != null ) {
661+ activeDeadlineMs = Math .min (activeDeadlineMs , startTimeMs + leafTimeoutMs );
662+ }
663+ if (leafExtraPassiveTimeoutMs != null ) {
664+ passiveDeadlineMs = Math .min (passiveDeadlineMs , activeDeadlineMs + leafExtraPassiveTimeoutMs );
665+ }
666+ passiveDeadlineMs = Math .max (passiveDeadlineMs , activeDeadlineMs );
667+ opChainMetadata .put (LeafOperator .LEAF_ACTIVE_DEADLINE_METADATA_KEY , Long .toString (activeDeadlineMs ));
668+ opChainMetadata .put (LeafOperator .LEAF_PASSIVE_DEADLINE_METADATA_KEY , Long .toString (passiveDeadlineMs ));
669+ }
670+
607671 public StagePlan explainQuery (WorkerMetadata workerMetadata , StagePlan stagePlan ,
608672 Map <String , String > requestMetadata ) {
609673 if (!workerMetadata .isLeafStageWorker ()) {
@@ -613,6 +677,7 @@ public StagePlan explainQuery(WorkerMetadata workerMetadata, StagePlan stagePlan
613677
614678 StageMetadata stageMetadata = stagePlan .getStageMetadata ();
615679 Map <String , String > opChainMetadata = consolidateMetadata (stageMetadata .getCustomProperties (), requestMetadata );
680+ applyLeafDeadlineOverrides (workerMetadata , opChainMetadata );
616681
617682 if (PipelineBreakerExecutor .hasPipelineBreakers (stagePlan )) {
618683 //TODO: See https://github.com/apache/pinot/pull/13733#discussion_r1752031714
0 commit comments