Skip to content

fix(planner): cap a lookup join by its flat outer rows - #1370

Merged
adsharma merged 1 commit into
LadybugDB:mainfrom
zahariash:fix/lookup-join-flattened-cap
Oct 24, 2026
Merged

adsharma merged 1 commit into
LadybugDB:mainfrom
zahariash:fix/lookup-join-flattened-cap

Conversation

@zahariash

Copy link
Copy Markdown
Contributor

This work was produced with the help of language models.

Fixes #1369.

LDBC SF1 complex query 5 (query_complex14.py 5) took 1.14 s on 0.22.0.dev20261017 and 2.34 s on dev20261021. Its MATCH (friend)<-[membership:hasMember]-(forum:Forum) now fetches forum.title and forum.ID with 1,569,282 lookups by node ID, where it used to scan Forum once and hash-join it.

Fix. The lookup join from #1285 compared LOOKUP_JOIN_MAX_OUTER_CARDINALITY (1024) with the outer side's estimate before flattening it, but it probes once per flat row. An extend keeps its neighbours unflat, so that estimate was 73 here and the flattened one 11,895. The cap, the fan-out discount, the lookup's cost and its reported cardinality now use the flattened estimate. The unflattened check stays in front as a pre-filter.

Plans. Of 50 LDBC SF1 queries (30 short, 6 heavy, IC1-14), only IC5, IC2 and IC10 change: IC5 and IC2 go back to hash joins, and IC10 keeps one of its two lookup joins. The other 47 plans are identical, including the lookup joins in q1, q3, q7, q17 and IC7, and results are identical on all 50.

per execution, single-threaded instructions, main this PR allocations, main this PR
IC5 47,404M 42,968M 19,296,517 14,624,992
IC2 2,055M 2,112M 227,456 222,672
IC10 15,038M 14,945M 7,523,021 7,434,724

IC5 is back to the 10-17 nightly's 43.0G, and IC2 gives back a 2.7% instruction win. Wall times are left out because the machine was under heavy load.

Tests. LookupJoinCountsFlatOuterRows gives every outer node 20 neighbours. 100 nodes (2,000 probes into 5,000 rows) and 50 nodes (1,000 probes into 500 rows) got lookup joins on main and get hash joins here. 10 nodes keep theirs, now reported as 200 rows instead of 10. Reverting the cap, the discount or the cardinality alone fails its case; the cost change has no case of its own. Full ctest: 3181/3181.

#1307's runtime fallback is still worth having: IC5's flattened estimate is about 130x below its actual probes, because the friend set is estimated at 730 instead of 9,892 and the joinDate filter at 10% instead of 97%.

The lookup join checked LOOKUP_JOIN_MAX_OUTER_CARDINALITY against the
outer side's factorized estimate, but it flattens that side and probes
once per flat row. An extend leaves its neighbours unflat, so a side that
fans out passed the cap far above it: on LDBC SF1 complex query 5 the
hasMember side was estimated at 73 rows before flattening and 11,895
after, and ran 1,569,282 Forum lookups in place of one scan and hash join.

The cap, the fan-out discount and the lookup's cost and cardinality now
use the flattened estimate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@adsharma

Copy link
Copy Markdown
Contributor

IC5's flattened estimate is about 130x below its actual probes

Thanks for getting to the bottom of this and this honest assessment. The planner is less than perfect, but the flat vs unflat error is the higher order bit.

@adsharma
adsharma merged commit 7bcc47d into LadybugDB:main Oct 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

q5 from LDBC SNB complex14 set regressed

2 participants