Skip to content

Commit 889a7c0

Browse files
committed
Refactor null out from estimation recursive loop
Signed-off-by: bowenlan-amzn <bowenlan23@gmail.com>
1 parent ddf0f03 commit 889a7c0

5 files changed

Lines changed: 23 additions & 59 deletions

File tree

server/src/main/java/org/opensearch/search/aggregations/AggregatorFactories.java

Lines changed: 16 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -307,16 +307,12 @@ public List<Aggregator> createTopLevelNonGlobalAggregators(SearchContext searchC
307307

308308
private List<Aggregator> createTopLevelAggregators(SearchContext searchContext, Predicate<AggregatorFactory> factoryFilter)
309309
throws IOException {
310-
// Estimate streaming cost from factories BEFORE creating any aggregators.
311-
// This allows the correct aggregator type to be created on the first try,
312-
// avoiding double-creation when streaming is not beneficial.
313310
if (searchContext.isStreamSearch() && searchContext.getFlushMode() == null) {
314-
StreamingCostMetrics metrics = estimateStreamingCostFromFactories(factories, searchContext);
315311
FlushMode decision;
316-
if (metrics == null) {
317-
// No factories provided streaming metrics - default to PER_SHARD
312+
if (factories.length == 0) {
318313
decision = FlushMode.PER_SHARD;
319314
} else {
315+
StreamingCostMetrics metrics = estimateStreamingCostFromFactories(factories, searchContext);
320316
long maxBucket = searchContext.getStreamingMaxEstimatedBucketCount();
321317
double minRatio = searchContext.getStreamingMinCardinalityRatio();
322318
long minBucket = searchContext.getStreamingMinEstimatedBucketCount();
@@ -348,25 +344,19 @@ private List<Aggregator> createTopLevelAggregators(SearchContext searchContext,
348344
/**
349345
* Recursively estimates streaming cost from the factory tree.
350346
*
351-
* <p>Traverses the aggregator factory tree, collecting streaming cost metrics from factories
352-
* that implement {@link StreamingCostEstimable}. Combines metrics from sibling factories
353-
* and nested sub-aggregations to produce a combined estimate.
354-
*
355-
* @param factories Array of aggregator factories to estimate
347+
* @param factories Array of aggregator factories to estimate (must be non-empty)
356348
* @param searchContext Search context providing access to index metadata
357-
* @return Combined streaming cost metrics, null if no factories provide metrics,
358-
* or non-streamable if any factory explicitly returns non-streamable
349+
* @return Combined streaming cost metrics, or non-streamable if any factory cannot be streamed
359350
*/
360351
private static StreamingCostMetrics estimateStreamingCostFromFactories(AggregatorFactory[] factories, SearchContext searchContext) {
352+
assert factories.length > 0 : "factories array must be non-empty";
361353
StreamingCostMetrics combined = null;
362354
for (AggregatorFactory factory : factories) {
363355
StreamingCostMetrics metrics = estimateFromFactory(factory, searchContext);
364-
if (metrics != null && !metrics.streamable()) {
356+
if (!metrics.streamable()) {
365357
return StreamingCostMetrics.nonStreamable();
366358
}
367-
if (metrics != null) {
368-
combined = (combined == null) ? metrics : combined.combineWithSibling(metrics);
369-
}
359+
combined = (combined == null) ? metrics : combined.combineWithSibling(metrics);
370360
}
371361
return combined;
372362
}
@@ -380,30 +370,26 @@ private static StreamingCostMetrics estimateStreamingCostFromFactories(Aggregato
380370
*
381371
* @param factory The aggregator factory to estimate
382372
* @param searchContext Search context providing access to index metadata
383-
* @return Streaming cost metrics for this factory and its sub-aggregations
373+
* @return Streaming cost metrics for this factory and its sub-aggregations (never null)
384374
*/
385375
private static StreamingCostMetrics estimateFromFactory(AggregatorFactory factory, SearchContext searchContext) {
386-
StreamingCostMetrics factoryMetrics;
387-
if (factory instanceof StreamingCostEstimable estimable) {
388-
factoryMetrics = estimable.estimateStreamingCost(searchContext);
389-
if (!factoryMetrics.streamable()) {
390-
return StreamingCostMetrics.nonStreamable();
391-
}
392-
} else {
393-
// Factory doesn't implement StreamingCostEstimable - not streaming compatible
376+
if (!(factory instanceof StreamingCostEstimable estimable)) {
377+
return StreamingCostMetrics.nonStreamable();
378+
}
379+
380+
StreamingCostMetrics factoryMetrics = estimable.estimateStreamingCost(searchContext);
381+
if (!factoryMetrics.streamable()) {
394382
return StreamingCostMetrics.nonStreamable();
395383
}
396384

397385
// Recursively estimate sub-factories
398386
AggregatorFactory[] subFactories = factory.getSubFactories().getFactories();
399387
if (subFactories.length > 0) {
400388
StreamingCostMetrics subMetrics = estimateStreamingCostFromFactories(subFactories, searchContext);
401-
if (subMetrics != null && !subMetrics.streamable()) {
389+
if (!subMetrics.streamable()) {
402390
return StreamingCostMetrics.nonStreamable();
403391
}
404-
if (subMetrics != null) {
405-
factoryMetrics = factoryMetrics.combineWithSubAggregation(subMetrics);
406-
}
392+
factoryMetrics = factoryMetrics.combineWithSubAggregation(subMetrics);
407393
}
408394

409395
return factoryMetrics;

server/src/main/java/org/opensearch/search/aggregations/bucket/terms/TermsAggregatorFactory.java

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -679,16 +679,6 @@ static Aggregator createStreamNumericTermsAggregator(
679679
);
680680
}
681681

682-
/**
683-
* Estimates streaming cost metrics before aggregator creation.
684-
*
685-
* <p>This method enables factory-level streaming decision making, allowing
686-
* the correct aggregator type to be selected BEFORE creation, avoiding
687-
* double-creation overhead.
688-
*
689-
* @param searchContext The search context providing access to index reader
690-
* @return Streaming cost metrics for this terms aggregation
691-
*/
692682
@Override
693683
public StreamingCostMetrics estimateStreamingCost(SearchContext searchContext) {
694684
ValuesSource valuesSource = config.getValuesSource();
@@ -709,8 +699,7 @@ public StreamingCostMetrics estimateStreamingCost(SearchContext searchContext) {
709699
return StreamingCostEstimator.estimateStringTerms(searchContext.searcher().getIndexReader(), ordinalsVS, effectiveShardSize);
710700
}
711701

712-
// Numeric terms - estimation is less reliable, return non-streamable
713-
// to let AggregatorTreeEvaluator handle it with collector-level metrics
702+
// Numeric terms - estimation is less reliable
714703
if (valuesSource instanceof ValuesSource.Numeric) {
715704
return StreamingCostEstimator.estimateNumericTerms(
716705
searchContext.searcher().getIndexReader(),

server/src/main/java/org/opensearch/search/streaming/FlushModeResolver.java

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,6 @@ private FlushModeResolver() {}
7171
/**
7272
* Evaluates cost metrics to determine if streaming is beneficial.
7373
*
74-
* <p>This method is used by factories to make streaming decisions before
75-
* creating aggregators, eliminating the need for double-creation when streaming
76-
* is not beneficial.
77-
*
7874
* @param metrics combined cost metrics from the factory tree
7975
* @param defaultMode fallback mode when streaming is not beneficial
8076
* @param maxBucketCount maximum bucket count threshold

server/src/main/java/org/opensearch/search/streaming/StreamingCostEstimable.java

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,8 @@
1414
/**
1515
* Interface for aggregator factories that can estimate streaming cost without creating aggregators.
1616
*
17-
* <p>This interface enables factory-level streaming cost estimation, allowing the streaming decision
18-
* to be made BEFORE any aggregators are created. This eliminates the double-creation problem where
19-
* streaming aggregators are created speculatively, metrics collected, and then recreated as traditional
20-
* aggregators if streaming is not beneficial.
21-
*
2217
* <p>Implementing classes should estimate the streaming cost based on field metadata (ordinals,
23-
* cardinality) without creating the actual aggregator instance.
18+
* cardinality)
2419
*
2520
* @opensearch.experimental
2621
*/
@@ -30,12 +25,8 @@ public interface StreamingCostEstimable {
3025
/**
3126
* Estimates streaming cost metrics before aggregator creation.
3227
*
33-
* <p>Called only when streaming search is enabled and flushMode has not yet been determined.
34-
* The returned metrics represent this factory only (excluding sub-factories, which are
35-
* handled separately by the caller).
36-
*
3728
* @param searchContext The search context providing access to index reader and configuration
38-
* @return StreamingCostMetrics for this factory, or {@link StreamingCostMetrics#nonStreamable()}
29+
* @return StreamingCostMetrics for this factory excluding sub-factories, or {@link StreamingCostMetrics#nonStreamable()}
3930
* if this factory cannot support streaming
4031
*/
4132
StreamingCostMetrics estimateStreamingCost(SearchContext searchContext);

server/src/main/java/org/opensearch/search/streaming/StreamingCostMetrics.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,8 @@ public static StreamingCostMetrics neutral() {
7070
* @return combined metrics reflecting the nested relationship, or non-streamable if either input is non-streamable
7171
*/
7272
public StreamingCostMetrics combineWithSubAggregation(StreamingCostMetrics subAggMetrics) {
73-
if (!this.streamable || subAggMetrics == null || !subAggMetrics.streamable) {
73+
assert subAggMetrics != null : "subAggMetrics must not be null";
74+
if (!this.streamable || !subAggMetrics.streamable) {
7475
return nonStreamable();
7576
}
7677

@@ -108,7 +109,8 @@ public StreamingCostMetrics combineWithSubAggregation(StreamingCostMetrics subAg
108109
* @return combined metrics reflecting the parallel relationship, or non-streamable if either input is non-streamable
109110
*/
110111
public StreamingCostMetrics combineWithSibling(StreamingCostMetrics siblingMetrics) {
111-
if (!this.streamable || siblingMetrics == null || !siblingMetrics.streamable) {
112+
assert siblingMetrics != null : "siblingMetrics must not be null";
113+
if (!this.streamable || !siblingMetrics.streamable) {
112114
return nonStreamable();
113115
}
114116

0 commit comments

Comments
 (0)