Skip to content

Conversation

@fang-xing-esql
Copy link
Member

@fang-xing-esql fang-xing-esql commented Aug 6, 2025

This PR pushes the RoundTo function down to Lucene by rewriting the QueryBuilder in EsQueryExec to multiple QueryBuilders with tags. Each QueryBuilder is responsible for one RoundTo bucket.

EsPhysicalOperationProviders.sourcePhysicalOperation builds LuceneSliceQueue for LuceneSourceOperator to process the list of QueryAndTags.

Some major changes to EsQueryExec:

  • The serialization of EsQueryExec is removed, as it is created and used by local data nodes only.
  • The two constructors of EsQueryExec that take QueryBuilder as input have been removed and replaced by the new one that takes a list of QueryBuilderAndTags.

Examples
This is one of the examples from the description of ReplaceRoundToWithQueryAndTags

 *    | STATS COUNT(*) BY d = DATE_TRUNC(1 day, date)
 *    becomes, the rounding points are calculated according to SearchStats and predicates from the query.
 *    | EVAL d = ROUND_TO(hire_date, 1697760000000, 1697846400000, 1697932800000)
 *    | STATS COUNT(*) BY d
 *    becomes
 *    [QueryBuilderAndTags[query={
 *     "esql_single_value" : {
 *      "field" : "date",
 *      "next" : {
 *       "range" : {
 *         "date" : {
 *           "lt" : "2023-10-21T00:00:00.000Z",
 *           "time_zone" : "Z",
 *           "format" : "strict_date_optional_time",
 *           "boost" : 0.0
 *         }
 *       }
 *      },
 *      "source" : "date_trunc(1 day, date)@2:25"
 *     }
 *    }, tags=[1697760000000]], QueryBuilderAndTags[query={
 *    "esql_single_value" : {
 *     "field" : "date",
 *     "next" : {
 *       "range" : {
 *         "date" : {
 *           "gte" : "2023-10-21T00:00:00.000Z",
 *           "lt" : "2023-10-22T00:00:00.000Z",
 *           "time_zone" : "Z",
 *           "format" : "strict_date_optional_time",
 *           "boost" : 0.0
 *         }
 *       }
 *      },
 *      "source" : "date_trunc(1 day, date)@2:25"
 *     }
 *    }, tags=[1697846400000]], QueryBuilderAndTags[query={
 *    "esql_single_value" : {
 *     "field" : "date",
 *     "next" : {
 *       "range" : {
 *         "date" : {
 *           "gte" : "2023-10-22T00:00:00.000Z",
 *           "time_zone" : "Z",
 *           "format" : "strict_date_optional_time",
 *           "boost" : 0.0
 *         }
 *       }
 *      },
 *      "source" : "date_trunc(1 day, date)@2:25"
 *     }
 *    }, tags=[1697932800000]], QueryBuilderAndTags[query={
 *    "bool" : {
 *     "must_not" : [
 *       {
 *         "exists" : {
 *           "field" : "date",
 *           "boost" : 0.0
 *         }
 *       }
 *     ],
 *     "boost" : 1.0
 *    }
 *   }, tags=[null]]]
 *

Limitations
RoundTo won't be rewritten to QueryAndTags in the following situations:

  • LuceneTopNSourceOperator does not support QueryAndTags
  • TimeSeriesSourceOperator does not support QueryAndTags
  • When there is FORK in the query
  • When there is LOOKUP JOIN in the query
  • When there are multiple RoundTos referenced by the query

There is a cluster setting and a pragma added to disable the rewrite of RoundTo to QueryAndTags

The cluster setting defaults to 127, which means if the number of rounding points is greater than 127, RoundTo is not rewritten to QueryAndTags.

Pragma defaults to -1 which means cluster setting takes effects. If the pragma is set to a value that's greater than -1, it overrides cluster setting.

{
  "persistent": {
    "esql.query.roundto_pushdown_threshold": 127 
  }
}

"pragma": {"roundto_pushdown_threshold":-1}

Performance Observations:

The nyc_taxis dataset is used in the ES|QL performance regression tests related to date_histogram and date_trunc. The queries discussed below are built on this dataset and vary by calendar interval, date range, and data volume. An example query is shown below:

FROM nyc_taxis 
| where dropoff_datetime < "2016-01-01" AND dropoff_datetime >= "2015-01-01" 
| eval dropoffs_over_time=date_trunc(1 month, dropoff_datetime) 
| stats c = count(dropoff_datetime) by dropoffs_over_time 
| sort dropoffs_over_time

The elapsed times reported are the averages of 10 runs for each query, measured on both the main and this branch. The observation is that for larger data volumes, rewriting (pushing down) RoundTo into QueryAndTags tends to improve performance. However, for smaller datasets, QueryAndTags can perform worse than RoundTo. This is likely because QueryAndTags may query the same index multiple times. That said, when data volume is small, all queries complete within subsecond range, and the performance difference is generally not quite noticeable.

Q1 - monthly interval on 12 months of data, 12 buckets
FROM nyc_taxis 
| where dropoff_datetime < "2016-01-01" AND dropoff_datetime >= "2015-01-01" 
| eval dropoffs_over_time=date_trunc(1 month, dropoff_datetime) 
| stats c = count(dropoff_datetime) by dropoffs_over_time 
| sort dropoffs_over_time

main: 1790 ms
this branch without merging range queries: 579 ms, 68% faster
merge range queries: 642 ms, 64% faster

Q2 - monthly interval on 9 months of data, 9 buckets
FROM nyc_taxis 
| where dropoff_datetime < "2015-10-01" AND dropoff_datetime >= "2015-01-01" 
| eval dropoffs_over_time=date_trunc(1 month, dropoff_datetime) 
| stats c = count(dropoff_datetime) by dropoffs_over_time 
| sort dropoffs_over_time

main: 1312 ms
this branch without merging range queries: 447 ms, 66% faster
merge range queries: 473 ms, 64% faster

Q3 - monthly interval on 3 months of data, 3 buckets
FROM nyc_taxis 
| where dropoff_datetime <= "2015-03-01" AND dropoff_datetime >= "2015-01-01" 
| eval dropoffs_over_time=date_trunc(1 month, dropoff_datetime) 
| stats c = count(dropoff_datetime) by dropoffs_over_time 
| sort dropoffs_over_time

main: 413 ms
this branch without merging range queries: 171 ms, 59% faster
merge range queries: 181 ms, 56% faster

Q4 - weekly interval on 12 weeks of data, 12 buckets
FROM nyc_taxis 
| where dropoff_datetime < "2015-03-23" AND dropoff_datetime >= "2015-01-01" 
| eval dropoffs_over_time=date_trunc(1 week, dropoff_datetime) 
| stats c = count(dropoff_datetime) by dropoffs_over_time 
| sort dropoffs_over_time

main: 585 ms
this branch without merging range queries: 362 ms, 38% faster
merge range queries: 328 ms, 44% faster

Q5 - weekly interval on 9 weeks of data, 9 buckets
FROM nyc_taxis 
| where dropoff_datetime < "2015-03-01" AND dropoff_datetime >= "2015-01-01" 
| eval dropoffs_over_time=date_trunc(1 week, dropoff_datetime) 
| stats c = count(dropoff_datetime) by dropoffs_over_time 
| sort dropoffs_over_time

main: 456 ms
this branch without merging range queries: 230 ms, 50% faster
merge range queries: 254 ms, 44% faster

Q6 - weekly interval on 3 weeks of data, 3 buckets
FROM nyc_taxis 
| where dropoff_datetime <= "2015-01-19" AND dropoff_datetime >= "2015-01-01" 
| eval dropoffs_over_time=date_trunc(1 week, dropoff_datetime) 
| stats c = count(dropoff_datetime) by dropoffs_over_time 
| sort dropoffs_over_time

main: 167 ms
this branch without merging range queries: 152 ms, 25% faster
merge range queries: 136 ms, 19% faster

Q7 - daily interval on 12 days of data, 12 buckets
FROM nyc_taxis 
| where dropoff_datetime < "2015-01-13" AND dropoff_datetime >= "2015-01-01" 
| eval dropoffs_over_time=date_trunc(1 week, dropoff_datetime) 
| stats c = count(dropoff_datetime) by dropoffs_over_time 
| sort dropoffs_over_time

main: 136 ms
this branch without merging range queries: 127 ms, 6% faster
merge range queries: 132 ms, 3% faster

Q8 - daily interval on 9 days of data, 9 buckets
FROM nyc_taxis 
| where dropoff_datetime < "2015-01-10" AND dropoff_datetime >= "2015-01-01" 
| eval dropoffs_over_time=date_trunc(1 week, dropoff_datetime) 
| stats c = count(dropoff_datetime) by dropoffs_over_time 
| sort dropoffs_over_time

main: 99 ms
this branch without merging range queries: 93 ms, 6% faster
merge range queries: 101 ms, 2% slower

Q9 - daily interval on 3 days of data, 3 buckets
FROM nyc_taxis 
| where dropoff_datetime <= "2015-01-04" AND dropoff_datetime >= "2015-01-01" 
| eval dropoffs_over_time=date_trunc(1 week, dropoff_datetime) 
| stats c = count(dropoff_datetime) by dropoffs_over_time 
| sort dropoffs_over_time

main: 48 ms
this branch without merging range queries: 50 ms, 5% slower
merge range queries: 46 ms, 2% faster

@elasticsearchmachine
Copy link
Collaborator

Hi @fang-xing-esql, I've created a changelog YAML for you.

@nik9000
Copy link
Member

nik9000 commented Aug 8, 2025

I think the perf numbers look great! It'd be worth picking at the cases that are slower. If we can detect those cases or fix them that'd be great, but it's only the very fast ones that are very fast already that get slower - and they don't get slow.

Question - what are the counts? My instinct is that if there aren't many results the overhead of doing query things is higher than just scanning.

@fang-xing-esql
Copy link
Member Author

fang-xing-esql commented Aug 11, 2025

Question - what are the counts? My instinct is that if there aren't many results the overhead of doing query things is higher than just scanning.

I took a closer look at the 12 days data, with daily interval, 12 buckets, here are the counts @nik9000 .

    "query": "FROM nyc_taxis | where dropoff_datetime < \"2015-01-13\" AND dropoff_datetime >= \"2015-01-01\" | eval dropoffs_over_time=date_trunc(1 day, dropoff_datetime) | stats c = count(dropoff_datetime) by dropoffs_over_time | sort dropoffs_over_time"
}'
       c       |   dropoffs_over_time   
---------------+------------------------
442063         |2015-01-01T00:00:00.000Z
386472         |2015-01-02T00:00:00.000Z
460322         |2015-01-03T00:00:00.000Z
373423         |2015-01-04T00:00:00.000Z
405326         |2015-01-05T00:00:00.000Z
424627         |2015-01-06T00:00:00.000Z
476972         |2015-01-07T00:00:00.000Z
498014         |2015-01-08T00:00:00.000Z
498430         |2015-01-09T00:00:00.000Z
579023         |2015-01-10T00:00:00.000Z
475352         |2015-01-11T00:00:00.000Z
437210         |2015-01-12T00:00:00.000Z

And the comparison of its profile between main and this branch shows LuceneSourceOperator spent quite longer time on the branch comparing to main on comparable numbers of rows processed.

main - 501,751 rows, 11,422,916 nanos, there are 5 instances of LuceneSourceOperator, each process one slice.

          {
            "operator" : "LuceneSourceOperator[shards = [nyc_taxis:0], maxPageSize = 13107, remainingDocs = 2147481727]",
            "status" : {
              "processed_slices" : 1,
              "processed_queries" : [
                "IndexOrDocValuesQuery(indexQuery=dropoff_datetime:[1420070400000 TO 1421107199999], dvQuery=dropoff_datetime:[1420070400000 TO 1421107199999])"
              ],
              "processed_shards" : [
                "nyc_taxis:0"
              ],
              "process_nanos" : 11422916,
              "slice_index" : 0,
              "total_slices" : 5,
              "pages_emitted" : 74,
              "slice_min" : 0,
              "slice_max" : 0,
              "current" : 2147483647,
              "rows_emitted" : 501751,
              "partitioning_strategies" : {
                "nyc_taxis:0" : "SEGMENT"
              }
            }
          },

branch - 393,765 rows, 166,409,495 nanos, there are 25 instances of LuceneSourceOperator , and some of them process multiple slices, it looks like processing multiple slices in one LuceneSourceOperator adds quite a bit overhead.

          {
            "operator" : "LuceneSourceOperator[shards = [nyc_taxis:0], maxPageSize = 13107, remainingDocs = 2147483514]",
            "status" : {
              "processed_slices" : 3,
              "processed_queries" : [
                "#IndexOrDocValuesQuery(indexQuery=dropoff_datetime:[1420070400000 TO 1421107199999], dvQuery=dropoff_datetime:[1420070400000 TO 1421107199999]) #IndexOrDocValuesQuery(indexQuery=dropoff_datetime:[-9223372036854775808 TO 1420243199999], dvQuery=dropoff_datetime:[-9223372036854775808 TO 1420243199999]) #IndexOrDocValuesQuery(indexQuery=dropoff_datetime:[1420156800000 TO 9223372036854775807], dvQuery=dropoff_datetime:[1420156800000 TO 9223372036854775807])",
                "#IndexOrDocValuesQuery(indexQuery=dropoff_datetime:[1420070400000 TO 1421107199999], dvQuery=dropoff_datetime:[1420070400000 TO 1421107199999]) #IndexOrDocValuesQuery(indexQuery=dropoff_datetime:[-9223372036854775808 TO 1420761599999], dvQuery=dropoff_datetime:[-9223372036854775808 TO 1420761599999]) #IndexOrDocValuesQuery(indexQuery=dropoff_datetime:[1420675200000 TO 9223372036854775807], dvQuery=dropoff_datetime:[1420675200000 TO 9223372036854775807])",
                "#IndexOrDocValuesQuery(indexQuery=dropoff_datetime:[1420070400000 TO 1421107199999], dvQuery=dropoff_datetime:[1420070400000 TO 1421107199999]) #IndexOrDocValuesQuery(indexQuery=dropoff_datetime:[-9223372036854775808 TO 1421020799999], dvQuery=dropoff_datetime:[-9223372036854775808 TO 1421020799999]) #IndexOrDocValuesQuery(indexQuery=dropoff_datetime:[1420934400000 TO 9223372036854775807], dvQuery=dropoff_datetime:[1420934400000 TO 9223372036854775807])"
              ],
              "processed_shards" : [
                "nyc_taxis:0"
              ],
              "process_nanos" : 166409495,
              "slice_index" : 0,
              "total_slices" : 66,
              "pages_emitted" : 61,
              "slice_min" : 0,
              "slice_max" : 0,
              "current" : 2147483647,
              "rows_emitted" : 393765,
              "partitioning_strategies" : {
                "nyc_taxis:0" : "SHARD"
              }
            }
          },

@fang-xing-esql
Copy link
Member Author

fang-xing-esql commented Aug 15, 2025

I think the perf numbers look great! It'd be worth picking at the cases that are slower. If we can detect those cases or fix them that'd be great, but it's only the very fast ones that are very fast already that get slower - and they don't get slow.

Question - what are the counts? My instinct is that if there aren't many results the overhead of doing query things is higher than just scanning.

I remeasured this branch after refactoring EsQueryExec, the previous performance degradation is gone, the numbers look actually great. The numbers in the description is updated. The performance numbers of merging range queries on the roundTo field is also updated in #132781, they are quite comparable! It looks like we don't get much benefit from merging range queries, it might not be necessary to introduce an extra layer of complexity(in #132781) to merge the range queries.

@fang-xing-esql fang-xing-esql marked this pull request as ready for review August 19, 2025 02:28
@elasticsearchmachine elasticsearchmachine added the Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) label Aug 19, 2025
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-analytical-engine (Team:Analytics)

@fang-xing-esql
Copy link
Member Author

Thanks a lot for reviewing @nik9000 ! The sort of rounding points has been refactored into RoundTo, and it is also shared by the RoundTo evaluators, and two constructors of EsQueryExec that take QueryBuilder as input have been removed and replaced by the new one that takes a list of QueryBuilderAndTags. The serialization of EsQueryExec is removed as well.

Copy link
Member

@nik9000 nik9000 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Worth having a QL expert looking too!

Copy link
Member

@dnhatn dnhatn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since time-series queries without rate are opted into this optimization, I benchmarked the change using the metrics benchmark. It sped up the following query by 10-15%:

TS my* 
    | WHERE `metrics.system.memory.utilization` IS NOT NULL AND @timestamp >= "2025-07-25T14:55:59.000Z" AND @timestamp <= "2025-07-25T16:25:59.000Z"
    | STATS AVG(AVG_OVER_TIME(`metrics.system.memory.utilization`)) BY host.name, BUCKET(@timestamp, 1h)

Great work. Thank you, Fang!

return null;
}
List<Expression> roundingPoints = roundTo.points();
int count = roundingPoints.size();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we avoid pushdown when there are many buckets? Reading values and then bucketing them seems safer and more efficient than executing many small sub-queries.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we avoid pushdown when there are many buckets? Reading values and then bucketing them seems safer and more efficient than executing many small sub-queries.

I set the upper limit of replacing RoundTo with range queries to 127, the same as the one used in Rounding. The 127 buckets query that I tested run to completion, and it performs better than not pushing down RoundTo. The performance measurements on different number of buckets are below, the performance of range queries look very promising, each bucket has about 400K documents in the queries below, and the number of buckets range from 20 to 127.

The query is like below, we get different number of buckets by adjusting the upper bound of dropoff_datetime
20 buckets
RoundTo without pushdown: 209 ms
RangeQueries: 163 ms 22% faster

40 buckets
RoundTo without pushdown: 404 ms
RangeQueries: 247 ms 39% faster

60 buckets
RoundTo without pushdown: 600 ms
RangeQueries: 323 ms 46% faster

80 buckets
RoundTo without pushdown: 671 ms
RangeQueries: 417 ms. 38% faster

100 buckets
RoundTo without pushdown: 825 ms
RangeQueries: 521 ms 37% faster

127 buckets
RoundTo without pushdown: 1068 ms
RangeQueries: 686 ms 36% faster

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, Fang! I think we can settle on 128 buckets. We could make this a cluster setting to allow lowering it if there is any performance issue, but that's optional and can be done in a follow-up.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @dnhatn ! I'd like to make this a cluster setting in this PR, just in case there is performance issue we have a way to protect ourselves.

Copy link
Member Author

@fang-xing-esql fang-xing-esql Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ROUNDTO_PUSHDOWN_THRESHOLD is added in QueryPragmas and EsqlFlags to control the pushdown behavior at query and cluster level. Query level pragma overrides cluster settings if it is not set to default(-1).

I added an entry in EsqlFlags instead of PysicalSettings , as EsqlFlags is already in LocalLogicalPlanOptimizer's context, PhysicalSettings is not accessible to LocalLogicalPlanOptimizer's yet.

@dnhatn Could you help take a look if the added knobs make sense? Thank you!

@fang-xing-esql
Copy link
Member Author

Since time-series queries without rate are opted into this optimization, I benchmarked the change using the metrics benchmark. It sped up the following query by 10-15%:

TS my* 
    | WHERE `metrics.system.memory.utilization` IS NOT NULL AND @timestamp >= "2025-07-25T14:55:59.000Z" AND @timestamp <= "2025-07-25T16:25:59.000Z"
    | STATS AVG(AVG_OVER_TIME(`metrics.system.memory.utilization`)) BY host.name, BUCKET(@timestamp, 1h)

Great work. Thank you, Fang!

Thanks for reviewing and measuring time-series queries @dnhatn! I didn't realize that it applies to time-series queries without rate. It is also a good point to put a threshold on the number of buckets.

When DateTrunc is converted to RoundTo, there is a maximum(128) number of buckets here, however if the RoundTo points was hard coded in the query, we don't actually have an upper limit. I'll do more measurements on the number of bucket, hope to come up with a reasonable upper limit. Thanks a lot for pointing this out!

@dnhatn
Copy link
Member

dnhatn commented Aug 20, 2025

@nik9000 I opened #133245 to adjust SliceQueue to account for multiple queries.

Copy link
Contributor

@astefan astefan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I stopped looking in detail when I reached EsQueryExec change. I've added a comment there, maybe @nik9000 has more insight into our test coverage regarding random serialization infra we have for PhysicalPlans.

.filter(RoundTo.class::isInstance)
.map(RoundTo.class::cast)
.toList();
// It is not clear how to push down multiple RoundTos, dealing with multiple RoundTos is out of the scope of this PR.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is appropriate for the PR itself, not necessarily for the code.

PhysicalPlan plan = evalExec;
// TimeSeriesSourceOperator and LuceneTopNSourceOperator do not support QueryAndTags, skip them
// Lookup join is not supported yet
if (evalExec.child() instanceof EsQueryExec queryExec && queryExec.canSubstituteRoundToWithQueryBuilderAndTags()) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check here EVAL .... -> EsQueryExec is very strict. Is this really the only edge case scenario that this PR is targeting?
What if I have two evals or an eval -> project -> esqueryexec?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there are limitations, I’d prefer to start with a smaller scope and extend it later as we see new patterns.

@dnhatn
Copy link
Member

dnhatn commented Aug 22, 2025

@fang-xing-esql I also benchmarked this change using LAST_OVER_TIME, which needs both the actual and the rounded timestamp.

TS my* 
| WHERE `metrics.system.memory.utilization` IS NOT NULL 
    AND @timestamp >= \"2025-07-25T14:55:59.000Z\" 
    AND @timestamp <= \"2025-07-25T16:25:59.000Z\" 
| STATS AVG(LAST_OVER_TIME(`metrics.system.memory.utilization`)) BY host.name, BUCKET(@timestamp, 1h)

Unfortunately, this optimization doesn't speed up the query and sometimes makes it slower. Unlike other queries, LAST_OVER_TIME requires the actual timestamp, so the timestamp field still has to be read. Have we considered only pushing down when the input value of the eval not used after the eval?

You can check this by changing count(dropoffs_over_time) to count(dropoff_datetime).

As I said before, this PR is great and has significantly sped up many important queries. We love it and are eagerly waiting for it to be merged to help with the time-series use cases :). My feedback was just to ensure the optimization doesn't accidentally slow down some specific cases.

Copy link
Contributor

@astefan astefan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@fang-xing-esql
Copy link
Member Author

fang-xing-esql commented Aug 22, 2025

@fang-xing-esql I also benchmarked this change using LAST_OVER_TIME, which needs both the actual and the rounded timestamp.

TS my* 
| WHERE `metrics.system.memory.utilization` IS NOT NULL 
    AND @timestamp >= \"2025-07-25T14:55:59.000Z\" 
    AND @timestamp <= \"2025-07-25T16:25:59.000Z\" 
| STATS AVG(LAST_OVER_TIME(`metrics.system.memory.utilization`)) BY host.name, BUCKET(@timestamp, 1h)

Unfortunately, this optimization doesn't speed up the query and sometimes makes it slower. Unlike other queries, LAST_OVER_TIME requires the actual timestamp, so the timestamp field still has to be read. Have we considered only pushing down when the input value of the eval not used after the eval?

You can check this by changing count(dropoffs_over_time) to count(dropoff_datetime).

As I said before, this PR is great and has significantly sped up many important queries. We love it and are eagerly waiting for it to be merged to help with the time-series use cases :). My feedback was just to ensure the optimization doesn't accidentally slow down some specific cases.

@dnhatn Do you happen to save the profile of this query before and after this change? I'd like to look into them further. Thank you!

After regular STATS, the input to date_trunc(for example) is not accessible, as regular STATS doesn't keep all its inputs, it keeps the aggregations and groupings only. I'll look into it a bit further.

dnhatn added a commit that referenced this pull request Aug 22, 2025
With query and tags, SliceQueue will contain more slices (see #132512). 
This change introduces an additional priority for query heads, allowing 
Drivers to pull slices from the same query and segment first. This
minimizes the overhead of switching between queries and segments.

Relates #132774
@fang-xing-esql
Copy link
Member Author

Thank you for reviewing @astefan @nik9000 @dnhatn !

@fang-xing-esql fang-xing-esql merged commit 79b86c8 into elastic:main Aug 25, 2025
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

:Analytics/ES|QL AKA ESQL >enhancement Team:Analytics Meta label for analytical engine team (ESQL/Aggs/Geo) v9.2.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants