Experiment with ES|QL lookup joins for stack rollups - #2511
Draft
ejsmith wants to merge 7 commits into
Draft
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
main/stack-rollupsand/stack-rollups/statsresourcesLOOKUP JOINbefore/afterkeyset cursors for both ranked rollups and stored-stack management; no stack list uses page-number paginationstack_newfilter behavior, and the 20,000-stack-id materialization pathWhy pursue this
The old stack page was not really paging stacks. It was:
termsaggregation forskip + limit + 1ranked buckets;skipbuckets in the API; andThat has four structural problems:
termscandidates outside Elasticsearch's documented safe ordering cases; andmodevalues implicitly changed columns, aggregation shape, sort, and even filtering (stack_new) inside a generic events endpoint.The join pipeline is a much better fit for the resource we are returning:
All rollup rows now have the same explicit columns. “Most frequent,” “most users,” and “new” are ordinary filters/sorts or saved views, not server-side modes. The chart uses a typed stats endpoint built from the same event/stack semantics.
How much better
These are isolated Elasticsearch 9.5 runs on the same development machine. Each scenario used two warmups and seven measured iterations with alternating execution order. They are synthetic benchmarks, not production capacity numbers, but they directly compare the former query with the new query.
5,000 stacks / 15,000 events / page size 25
25,000 stacks / 25,000 events / page size 25
The important result is not only lower latency. The join removes the cardinality cliff and keeps returned work page-sized as the cursor moves deeper. Page 100 is effectively the same latency as page 1 instead of more than doubling.
API and implementation model
/stack-rollupsis the time-bound ranked stack summary resource. It has fixed rollup columns and supportstotal,users,first_occurrence, andlast_occurrencesorts in both directions./stack-rollups/statsis typed and replaces the stack page's free-form aggregation string./stacksremains the stored stack-document resource used for project stack management. It uses Foundatio repository search-after cursors; it does not need a join.Intentional breaking changes
stack_recent,stack_frequent,stack_new, andstack_usersevent modes are removed and return 400.page; callers usebefore/afterpluslimit.first_occurrence:[start TO end]filter instead of a hidden mode mutation.Tradeoffs and prerequisites
COUNT_DISTINCTremains approximate. The overall stack total uses a 40,000 precision threshold and the benchmark accepts a 1% cardinality tolerance.Verification
StackRollupBenchmarkTestsnpm run validate: formatting/lint passed; Svelte diagnostics 0 errors, 0 warningsnpm run build: production build passedstacks-v2,index.mode=lookup, one primary shard, and the cursor E2E passed/next/and/api/v2/aboutsmoke checks returned 200Official references: LOOKUP JOIN, lookup prerequisites, QSTR, BUCKET, COUNT_DISTINCT, and terms aggregation ordering.