@@ -56,17 +56,17 @@ public class GroupByCombineOperator extends BaseSingleBlockCombineOperator<Group
5656 private static final Logger LOGGER = LoggerFactory .getLogger (GroupByCombineOperator .class );
5757 private static final String EXPLAIN_NAME = "COMBINE_GROUP_BY" ;
5858
59- private final int _numAggregationFunctions ;
60- private final int _numGroupByExpressions ;
61- private final int _numColumns ;
59+ protected final int _numAggregationFunctions ;
60+ protected final int _numGroupByExpressions ;
61+ protected final int _numColumns ;
6262 // We use a CountDownLatch to track if all Futures are finished by the query timeout, and cancel the unfinished
6363 // _futures (try to interrupt the execution if it already started).
64- private final CountDownLatch _operatorLatch ;
64+ protected final CountDownLatch _operatorLatch ;
6565
66- private volatile IndexedTable _indexedTable ;
67- private volatile boolean _groupsTrimmed ;
68- private volatile boolean _numGroupsLimitReached ;
69- private volatile boolean _numGroupsWarningLimitReached ;
66+ protected volatile IndexedTable _indexedTable ;
67+ protected volatile boolean _groupsTrimmed ;
68+ protected volatile boolean _numGroupsLimitReached ;
69+ protected volatile boolean _numGroupsWarningLimitReached ;
7070
7171 public GroupByCombineOperator (List <Operator > operators , QueryContext queryContext , ExecutorService executorService ) {
7272 super (null , operators , overrideMaxExecutionThreads (queryContext , operators .size ()), executorService );
@@ -113,59 +113,11 @@ protected void processSegments() {
113113 if (_indexedTable == null ) {
114114 synchronized (this ) {
115115 if (_indexedTable == null ) {
116- _indexedTable = GroupByUtils .createIndexedTableForCombineOperator (resultsBlock , _queryContext , _numTasks ,
117- _executorService );
116+ _indexedTable = createIndexedTable (resultsBlock , _numTasks );
118117 }
119118 }
120119 }
121-
122- if (resultsBlock .isGroupsTrimmed ()) {
123- _groupsTrimmed = true ;
124- }
125- // Set groups limit reached flag.
126- if (resultsBlock .isNumGroupsLimitReached ()) {
127- _numGroupsLimitReached = true ;
128- }
129- if (resultsBlock .isNumGroupsWarningLimitReached ()) {
130- _numGroupsWarningLimitReached = true ;
131- }
132-
133- // Merge aggregation group-by result.
134- // Iterate over the group-by keys, for each key, update the group-by result in the indexedTable
135- Collection <IntermediateRecord > intermediateRecords = resultsBlock .getIntermediateRecords ();
136- // Count the number of merged keys
137- int mergedKeys = 0 ;
138- // For now, only GroupBy OrderBy query has pre-constructed intermediate records
139- if (intermediateRecords == null ) {
140- // Merge aggregation group-by result.
141- AggregationGroupByResult aggregationGroupByResult = resultsBlock .getAggregationGroupByResult ();
142- if (aggregationGroupByResult != null ) {
143- // Iterate over the group-by keys, for each key, update the group-by result in the indexedTable
144- try {
145- Iterator <GroupKeyGenerator .GroupKey > dicGroupKeyIterator = aggregationGroupByResult .getGroupKeyIterator ();
146- while (dicGroupKeyIterator .hasNext ()) {
147- QueryThreadContext .checkTerminationAndSampleUsagePeriodically (mergedKeys ++, EXPLAIN_NAME );
148- GroupKeyGenerator .GroupKey groupKey = dicGroupKeyIterator .next ();
149- Object [] keys = groupKey ._keys ;
150- Object [] values = Arrays .copyOf (keys , _numColumns );
151- int groupId = groupKey ._groupId ;
152- for (int i = 0 ; i < _numAggregationFunctions ; i ++) {
153- values [_numGroupByExpressions + i ] = aggregationGroupByResult .getResultForGroupId (i , groupId );
154- }
155- _indexedTable .upsert (new Key (keys ), new Record (values ));
156- }
157- } finally {
158- // Release the resources used by the group key generator
159- aggregationGroupByResult .closeGroupKeyGenerator ();
160- }
161- }
162- } else {
163- for (IntermediateRecord intermediateResult : intermediateRecords ) {
164- QueryThreadContext .checkTerminationAndSampleUsagePeriodically (mergedKeys ++, EXPLAIN_NAME );
165- //TODO: change upsert api so that it accepts intermediateRecord directly
166- _indexedTable .upsert (intermediateResult ._key , intermediateResult ._record );
167- }
168- }
120+ mergeGroupByResultsBlock (_indexedTable , resultsBlock , EXPLAIN_NAME );
169121 } catch (RuntimeException e ) {
170122 throw wrapOperatorException (operator , e );
171123 } finally {
@@ -176,6 +128,56 @@ protected void processSegments() {
176128 }
177129 }
178130
131+ protected IndexedTable createIndexedTable (GroupByResultsBlock resultsBlock , int numThreads ) {
132+ return GroupByUtils .createIndexedTableForCombineOperator (resultsBlock , _queryContext , numThreads , _executorService );
133+ }
134+
135+ protected void updateCombineResultsStats (GroupByResultsBlock resultsBlock ) {
136+ if (resultsBlock .isGroupsTrimmed ()) {
137+ _groupsTrimmed = true ;
138+ }
139+ if (resultsBlock .isNumGroupsLimitReached ()) {
140+ _numGroupsLimitReached = true ;
141+ }
142+ if (resultsBlock .isNumGroupsWarningLimitReached ()) {
143+ _numGroupsWarningLimitReached = true ;
144+ }
145+ }
146+
147+ protected void mergeGroupByResultsBlock (IndexedTable indexedTable , GroupByResultsBlock resultsBlock ,
148+ String explainName ) {
149+ updateCombineResultsStats (resultsBlock );
150+
151+ Collection <IntermediateRecord > intermediateRecords = resultsBlock .getIntermediateRecords ();
152+ int mergedKeys = 0 ;
153+ if (intermediateRecords == null ) {
154+ AggregationGroupByResult aggregationGroupByResult = resultsBlock .getAggregationGroupByResult ();
155+ if (aggregationGroupByResult != null ) {
156+ try {
157+ Iterator <GroupKeyGenerator .GroupKey > dicGroupKeyIterator = aggregationGroupByResult .getGroupKeyIterator ();
158+ while (dicGroupKeyIterator .hasNext ()) {
159+ QueryThreadContext .checkTerminationAndSampleUsagePeriodically (mergedKeys ++, explainName );
160+ GroupKeyGenerator .GroupKey groupKey = dicGroupKeyIterator .next ();
161+ Object [] keys = groupKey ._keys ;
162+ Object [] values = Arrays .copyOf (keys , _numColumns );
163+ int groupId = groupKey ._groupId ;
164+ for (int i = 0 ; i < _numAggregationFunctions ; i ++) {
165+ values [_numGroupByExpressions + i ] = aggregationGroupByResult .getResultForGroupId (i , groupId );
166+ }
167+ indexedTable .upsert (new Key (keys ), new Record (values ));
168+ }
169+ } finally {
170+ aggregationGroupByResult .closeGroupKeyGenerator ();
171+ }
172+ }
173+ } else {
174+ for (IntermediateRecord intermediateResult : intermediateRecords ) {
175+ QueryThreadContext .checkTerminationAndSampleUsagePeriodically (mergedKeys ++, explainName );
176+ indexedTable .upsert (intermediateResult ._key , intermediateResult ._record );
177+ }
178+ }
179+ }
180+
179181 @ Override
180182 public void onProcessSegmentsException (Throwable t ) {
181183 _processingException .compareAndSet (null , t );
@@ -205,33 +207,41 @@ public BaseResultsBlock mergeResults()
205207 long timeoutMs = _queryContext .getEndTimeMs () - System .currentTimeMillis ();
206208 boolean opCompleted = _operatorLatch .await (timeoutMs , TimeUnit .MILLISECONDS );
207209 if (!opCompleted ) {
208- // If this happens, the broker side should already timed out, just log the error and return
209- String userError = "Timed out while combining group-by order-by results after " + timeoutMs + "ms" ;
210- String logMsg = userError + ", queryContext = " + _queryContext ;
211- LOGGER .error (logMsg );
212- return new ExceptionResultsBlock (new QueryErrorMessage (QueryErrorCode .EXECUTION_TIMEOUT , userError , logMsg ));
210+ return getTimeoutResultsBlock (timeoutMs );
213211 }
214212
215213 Throwable ex = _processingException .get ();
216214 if (ex != null ) {
217- String userError = "Caught exception while processing group-by order-by query" ;
218- String devError = userError + ": " + ex .getMessage ();
219- QueryErrorMessage errMsg ;
220- if (ex instanceof QueryException ) {
221- // If the exception is a QueryException, use the error code from the exception and trust the error message
222- errMsg = new QueryErrorMessage (((QueryException ) ex ).getErrorCode (), devError , devError );
223- } else {
224- // If the exception is not a QueryException, use the generic error code and don't expose the exception message
225- errMsg = new QueryErrorMessage (QueryErrorCode .QUERY_EXECUTION , userError , devError );
226- }
227- return new ExceptionResultsBlock (errMsg );
215+ return getExceptionResultsBlock (ex );
228216 }
229217
230- if (_indexedTable .isTrimmed () && _queryContext .isUnsafeTrim ()) {
218+ return getMergedResultsBlock (_indexedTable );
219+ }
220+
221+ protected ExceptionResultsBlock getTimeoutResultsBlock (long timeoutMs ) {
222+ String userError = "Timed out while combining group-by results after " + Math .max (0 , timeoutMs ) + "ms" ;
223+ String logMsg = userError + ", queryContext = " + _queryContext ;
224+ LOGGER .error (logMsg );
225+ return new ExceptionResultsBlock (new QueryErrorMessage (QueryErrorCode .EXECUTION_TIMEOUT , userError , logMsg ));
226+ }
227+
228+ protected ExceptionResultsBlock getExceptionResultsBlock (Throwable ex ) {
229+ String userError = "Caught exception while processing group-by query" ;
230+ String devError = userError + ": " + ex .getMessage ();
231+ QueryErrorMessage errMsg ;
232+ if (ex instanceof QueryException ) {
233+ errMsg = new QueryErrorMessage (((QueryException ) ex ).getErrorCode (), devError , devError );
234+ } else {
235+ errMsg = new QueryErrorMessage (QueryErrorCode .QUERY_EXECUTION , userError , devError );
236+ }
237+ return new ExceptionResultsBlock (errMsg );
238+ }
239+
240+ protected GroupByResultsBlock getMergedResultsBlock (IndexedTable indexedTable ) {
241+ if (indexedTable .isTrimmed () && _queryContext .isUnsafeTrim ()) {
231242 _groupsTrimmed = true ;
232243 }
233244
234- IndexedTable indexedTable = _indexedTable ;
235245 if (_queryContext .isServerReturnFinalResult ()) {
236246 indexedTable .finish (true , true );
237247 } else if (_queryContext .isServerReturnFinalResultKeyUnpartitioned ()) {
0 commit comments