Skip to content

perf(sql): improve performance of cross joins#5741

Merged
bluestreak01 merged 23 commits intomasterfrom
vi_to_top
Jul 4, 2025
Merged

perf(sql): improve performance of cross joins#5741
bluestreak01 merged 23 commits intomasterfrom
vi_to_top

Conversation

@bluestreak01
Copy link
Copy Markdown
Member

@bluestreak01 bluestreak01 commented Jun 12, 2025

Background

Cross joins are in active production use. They rely heavily on RecordCursor.toTop() to be a lightweight operation to restart the cursor. Cross join will restart the secondary cursor for every row of the primary. Some of our cursor implementations are unnecessarily heavy to restart. Such as select count() ....

Implementation

I introduced an abstract method that is meant to compute the hash of the "heavy" state of the cursor. For example, "mapIsBuilt" boolean field is used as input to this hash. If toTop() changes the value of this field - hash will change too. assertQuery() exercises topTop() and asserts that this hash does not change before and after toTop call.

Details

  • modified non-intrinsic "limit" execution to perform quicker in cross-joins
  • modified "count()" cursor to perform better in cross-joins
  • modified "wal_tables()" to perform better in cross joins and also not drop over concurrently delete tables.
  • remove allocation inside "materialized_views()" cursor
  • added assertion that toTop() does not discard cursor's state where this state was pre-built

Important

  • removed distinct symbol and int specialisations, they seem to be covered by group-by now and these factories are not being hit by tests

@bluestreak01 bluestreak01 added SQL Issues or changes relating to SQL execution Performance Performance improvements labels Jul 1, 2025
Copy link
Copy Markdown
Contributor

@jerrinot jerrinot left a comment

Choose a reason for hiding this comment

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

please fix the bug in the distinct cursor and symbol columns. others things are not critical.

also: do you have a query + schema this is aiming to improve? I don't want to invent my own to test it.

@bluestreak01
Copy link
Copy Markdown
Member Author

thanks @jerrinot 👍

this was the trigger:

WITH
curr_usd_data AS (
    SELECT count(*) as symbol, sum(price)
    FROM trades
    WHERE side='buy'
)
SELECT dateadd('m', x::int, '2024-11-13T11:00:00.000000Z'::timestamp) AS send_ts, 'USD' as sym, 1.0 AS usd_rate, symbol
    FROM long_sequence(60)
    CROSS JOIN curr_usd_data;

@glasstiger
Copy link
Copy Markdown
Contributor

[PR Coverage check]

😍 pass : 204 / 235 (86.81%)

file detail

path covered line new line coverage
🔵 io/questdb/griffin/engine/groupby/DistinctTimeSeriesRecordCursorFactory.java 0 1 00.00%
🔵 io/questdb/griffin/engine/table/ShowCreateTableRecordCursorFactory.java 0 1 00.00%
🔵 io/questdb/griffin/engine/functions/catalogue/KeywordsFunctionFactory.java 0 1 00.00%
🔵 io/questdb/griffin/engine/join/HashOuterJoinFilteredRecordCursorFactory.java 0 1 00.00%
🔵 io/questdb/cairo/wal/WalDataCursor.java 0 1 00.00%
🔵 io/questdb/griffin/engine/functions/catalogue/CheckpointStatusFunctionFactory.java 0 1 00.00%
🔵 io/questdb/cairo/map/OrderedMapVarSizeCursor.java 0 1 00.00%
🔵 io/questdb/griffin/engine/join/RecordAsAFieldRecordCursorFactory.java 0 1 00.00%
🔵 io/questdb/cairo/RecordChain.java 0 1 00.00%
🔵 io/questdb/griffin/engine/functions/catalogue/ShowParametersCursorFactory.java 0 1 00.00%
🔵 io/questdb/cairo/map/Unordered2MapCursor.java 0 1 00.00%
🔵 io/questdb/cairo/map/UnorderedVarcharMapCursor.java 0 1 00.00%
🔵 io/questdb/griffin/engine/functions/catalogue/FunctionListFunctionFactory.java 0 1 00.00%
🔵 io/questdb/cairo/map/ShardedMapCursor.java 0 1 00.00%
🔵 io/questdb/griffin/engine/functions/test/TestDataUnavailableFunctionFactory.java 0 1 00.00%
🔵 io/questdb/griffin/engine/groupby/FillRangeRecordCursorFactory.java 0 1 00.00%
🔵 io/questdb/griffin/engine/functions/test/TestOwnerCountingFunctionFactory.java 0 1 00.00%
🔵 io/questdb/cairo/map/Unordered8MapCursor.java 0 1 00.00%
🔵 io/questdb/griffin/engine/EmptyTableRandomRecordCursor.java 0 1 00.00%
🔵 io/questdb/cairo/map/Unordered4MapCursor.java 0 1 00.00%
🔵 io/questdb/griffin/engine/functions/catalogue/PgNamespaceRecordCursor.java 0 1 00.00%
🔵 io/questdb/griffin/engine/EmptyTableNoSizeRecordCursor.java 0 1 00.00%
🔵 io/questdb/cairo/map/OrderedMapFixedSizeCursor.java 0 1 00.00%
🔵 io/questdb/griffin/engine/join/AsOfJoinLightRecordCursorFactory.java 0 1 00.00%
🔵 io/questdb/std/str/Path.java 1 6 16.67%
🔵 io/questdb/griffin/engine/functions/catalogue/PgProcCatalogueCursor.java 1 2 50.00%
🔵 io/questdb/griffin/engine/orderby/RecordTreeChain.java 1 2 50.00%
🔵 io/questdb/griffin/engine/join/AsOfJoinLightNoKeyRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/LatestByValueListRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/FilterOnSubQueryRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/EmptyTableRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/date/GenerateSeriesTimestampRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/TableStorageRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/ReaderPoolRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/MatViewsFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/table/ReadParquetRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/LatestByAllIndexedRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/orderby/SortedRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/AsOfJoinRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/orderby/LongSortedLightRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/AsyncFilteredNegativeLimitRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/NestedLoopLeftJoinRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/union/ExceptAllCastRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/date/GenerateSeriesTimestampStringRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/ExplainPlanFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/FilteredRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/AsyncFilteredRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/LatestBySubQueryRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/ShowServerVersionNumCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/InformationSchemaCharacterSetsFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/vect/GroupByNotKeyedVectorRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/LatestByRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/SampleByInterpolateRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/LatestByValueFilteredRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/activity/QueryActivityFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/StringLongTuplesRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/AsOfJoinNoKeyFastRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/SampleByFillNoneNotKeyedRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/PgExtensionFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/PgTypeCatalogueCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/union/IntersectAllCastRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/PgClassFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/DistinctRecordCursorFactory.java 51 51 100.00%
🔵 io/questdb/griffin/engine/join/HashOuterJoinLightRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/WriterPoolRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/AllTablesFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/PgDatabaseFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/LatestByValueRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/AsyncGroupByNotKeyedRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/orderby/LimitedSizeSortedLightRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/QueryProgress.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/InformationSchemaColumnsFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/GroupByRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/CrossJoinRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/orderby/SortedLightRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/union/ExceptRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/LatestByLightRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/WalTransactionsFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/SingleValueRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/orderby/LimitedSizePartiallySortedLightRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/LtJoinNoKeyFastRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/VirtualFunctionDirectSymbolRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/PgGetKeywordsFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/PgAttributeFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/LatestByValuesIndexedRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/LtJoinNoKeyRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/ShowPartitionsRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/union/IntersectCastRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/SampleByFirstLastRecordCursorFactory.java 2 2 100.00%
🔵 io/questdb/griffin/engine/functions/date/GenerateSeriesLongRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/AsOfJoinFastRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/WalTableListFunctionFactory.java 9 9 100.00%
🔵 io/questdb/griffin/engine/join/LtJoinRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/FilteredAsOfJoinNoKeyFastRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/SpliceJoinLightRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/vect/GroupByRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/AsyncGroupByRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/HashJoinRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/union/IntersectRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/union/ExceptCastRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/ShowColumnsRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/AbstractNoRecordSampleByCursor.java 1 1 100.00%
🔵 io/questdb/griffin/SqlCompilerImpl.java 1 1 100.00%
🔵 io/questdb/griffin/engine/union/IntersectAllRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/union/UnionRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/union/ExceptAllRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/TablesFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/SelectedRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/FilteredAsOfJoinFastRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/LimitRecordCursorFactory.java 18 18 100.00%
🔵 io/questdb/griffin/engine/window/CachedWindowRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/CountRecordCursorFactory.java 10 10 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/InformationSchemaTablesFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/SampleByFillValueRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/PgAttrDefFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/SqlCodeGenerator.java 5 5 100.00%
🔵 io/questdb/griffin/engine/table/LatestByValuesIndexedFilteredRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/PageFrameRecordCursorImpl.java 8 8 100.00%
🔵 io/questdb/griffin/engine/union/UnionAllRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/LatestByValueIndexedFilteredRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/AbstractDescendingRecordListCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/rnd/LongSequenceFunctionFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/date/GenerateSeriesDoubleRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/HashOuterJoinFilteredLightRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/orderby/LongTopKRecordCursor.java 1 1 100.00%
🔵 io/questdb/griffin/engine/groupby/GroupByNotKeyedRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/HashOuterJoinRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/functions/catalogue/ShowServerVersionCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/HashJoinLightRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/join/LtJoinLightRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/griffin/engine/table/ShowCreateMatViewRecordCursorFactory.java 1 1 100.00%
🔵 io/questdb/cairo/sql/RecordCursor.java 1 1 100.00%

@bluestreak01 bluestreak01 merged commit 3c238cc into master Jul 4, 2025
34 checks passed
@bluestreak01 bluestreak01 deleted the vi_to_top branch July 4, 2025 16:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Performance Performance improvements SQL Issues or changes relating to SQL execution

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants