perf: reduce allocations in hot query execution paths#154
Merged
Conversation
kuseman
commented
Jun 2, 2026
Owner
- TupleVector.validate: use List path stack instead of eager string concatenation on each recursive schema descent; path is only joined when an exception is thrown
- TemporaryTable.IndexTupleVector: cache selected columns to avoid recreating SelectedValueVector (+ int[] copy) on every getColumn call
- ExecutionContext.copy: share stateless ExpressionFactory instance instead of allocating a new one per NestedLoop outer-row iteration
d92cd8b to
c7f8a44
Compare
Performance fixes (allocation hot spots):
- TupleVector.validate: replace eager string concatenation on each
recursive schema descent with a List<String> path stack; string is
only joined when an exception is thrown (eliminated 248 GB/s of
byte[] allocation in production JFR)
- HashMatch, NestedLoop: return cached schema field from getSchema()
instead of calling joinSchema() on every probe/iteration; both
operators stored the schema in the constructor but the public
override recomputed it each time
- TemporaryTable.IndexTupleVector: cache selected columns to avoid
recreating SelectedValueVector (+ int[] copy) on every getColumn call
- ExecutionContext.copy: share the stateless ExpressionFactory instance
instead of allocating a new one per NestedLoop outer-row iteration
BREAKING CHANGE:
Memory leak fix:
- IDatasink.execute signature changed from TupleIterator to
Supplier<TupleIterator> so sinks can re-execute the upstream plan
on demand (cache hit skips execution; cache refresh calls input.get()
for a fresh iterator each time, matching old versions behaviour)
- InsertInto: removed LazyTupleIterator; passes () -> input.execute(context)
as the supplier, forwards estimatedBatchCount/estimatedRowCount,
guards against sinks that forget to close the iterator
- SelectIntoTempTableSink: materialise result through TupleVectorBuilder
instead of relying on PlanUtils.concat's single-batch fast-path which
returned the raw TableScan$1$1 anonymous TupleVector; that vector held
a strong reference via this$1 -> TableScan$1 -> val$context ->
ExecutionContext -> QuerySession -> temporaryTables, retaining the
entire execution context chain for the lifetime of the cached entry
- AInMemoryCache: document that expired entries reload asynchronously
for alwaysLoadAsync=false; the Supplier<TupleIterator> API change
ensures async reload always has a re-executable supplier
c7f8a44 to
a92db8c
Compare
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.