Skip to content

feat(zql): batch flip join requests - #5928

Merged
tantaman merged 9 commits into
mainfrom
mlaw/flip-in
May 14, 2026
Merged

feat(zql): batch flip join requests#5928
tantaman merged 9 commits into
mainfrom
mlaw/flip-in

Conversation

@tantaman

@tantaman tantaman commented May 7, 2026

Copy link
Copy Markdown
Contributor
N main elapsed branch elapsed speedup main us/row branch us/row
100 2.5ms 1.0ms 2.5× 24.9 10.2
500 17.0ms 2.5ms 6.8× 33.9 5.0
1,000 47.0ms 4.5ms 10.4× 47.0 4.5
2,500 227.5ms 10.5ms 21.7× 91.0 4.2
5,000 872.0ms 19.6ms 44.5× 174.4 3.9
10,000 3,125.9ms 37.0ms 84.5× 312.6 3.7

@vercel

vercel Bot commented May 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
replicache-docs Ready Ready Preview, Comment May 14, 2026 5:22pm
zbugs Ready Ready Preview, Comment May 14, 2026 5:22pm

Request Review

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Branchmlaw/flip-in
TestbedLinux
Click to view all benchmark results
BenchmarkFile SizeBenchmark Result
kilobytes (KB)
(Result Δ%)
Upper Boundary
kilobytes (KB)
(Limit %)
zero-package.tgz📈 view plot
🚷 view threshold
2,110.91 KB
(+0.04%)Baseline: 2,110.04 KB
2,152.24 KB
(98.08%)
zero.js📈 view plot
🚷 view threshold
279.48 KB
(-0.13%)Baseline: 279.83 KB
285.43 KB
(97.91%)
zero.js.br📈 view plot
🚷 view threshold
74.21 KB
(-0.09%)Baseline: 74.28 KB
75.76 KB
(97.95%)
🐰 View full continuous benchmarking report in Bencher

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Branchmlaw/flip-in
Testbedself-hosted-metal
Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
src/db/pg-copy.bench.ts > pg-copy benchmark > copy📈 view plot
🚷 view threshold
18.01 ops/s
(+0.09%)Baseline: 17.99 ops/s
17.56 ops/s
(97.52%)
🐰 View full continuous benchmarking report in Bencher

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Branchmlaw/flip-in
Testbedself-hosted-metal
Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s) x 1e3
(Result Δ%)
Lower Boundary
operations / second (ops/s) x 1e3
(Limit %)
src/client/custom.bench.ts > big schema📈 view plot
🚷 view threshold
35.96 ops/s x 1e3
(+1.76%)Baseline: 35.34 ops/s x 1e3
33.37 ops/s x 1e3
(92.81%)
src/client/zero.bench.ts > basics > All 1000 rows x 10 columns (numbers)📈 view plot
🚷 view threshold
1.11 ops/s x 1e3
(+1.15%)Baseline: 1.10 ops/s x 1e3
1.04 ops/s x 1e3
(93.85%)
src/client/zero.bench.ts > pk compare > pk = N📈 view plot
🚷 view threshold
19.08 ops/s x 1e3
(-0.47%)Baseline: 19.17 ops/s x 1e3
16.58 ops/s x 1e3
(86.91%)
src/client/zero.bench.ts > with filter > Lower rows 500 x 10 columns (numbers)📈 view plot
🚷 view threshold
1.50 ops/s x 1e3
(-1.01%)Baseline: 1.51 ops/s x 1e3
1.33 ops/s x 1e3
(88.96%)
🐰 View full continuous benchmarking report in Bencher

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Branchmlaw/flip-in
Testbedself-hosted-metal
Click to view all benchmark results
BenchmarkThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
src/btree-set.bench.ts > BTreeSet iterator next() in isolation > forward iterator next()📈 view plot
🚷 view threshold
37,565.01 ops/s
(-35.06%)Baseline: 57,849.94 ops/s
-50,675.87 ops/s
(-134.90%)
src/btree-set.bench.ts > BTreeSet iterator next() in isolation > forward iterator next() from mid📈 view plot
🚷 view threshold
71,718.51 ops/s
(-37.04%)Baseline: 113,917.13 ops/s
-102,226.53 ops/s
(-142.54%)
src/btree-set.bench.ts > BTreeSet iterator next() in isolation > reverse iterator next()📈 view plot
🚷 view threshold
37,933.69 ops/s
(-34.59%)Baseline: 57,991.37 ops/s
-43,955.21 ops/s
(-115.87%)
src/btree-set.bench.ts > BTreeSet iterator next() in isolation > reverse iterator next() from mid📈 view plot
🚷 view threshold
74,887.12 ops/s
(-34.68%)Baseline: 114,644.66 ops/s
-88,839.68 ops/s
(-118.63%)
src/btree-set.bench.ts > BTreeSet iterators > [Symbol.iterator]() full scan📈 view plot
🚷 view threshold
41,028.24 ops/s
(-34.13%)Baseline: 62,285.88 ops/s
-47,449.31 ops/s
(-115.65%)
src/btree-set.bench.ts > BTreeSet iterators > values() full scan📈 view plot
🚷 view threshold
40,533.08 ops/s
(-33.41%)Baseline: 60,866.25 ops/s
-45,796.41 ops/s
(-112.99%)
src/btree-set.bench.ts > BTreeSet iterators > valuesFrom() from mid📈 view plot
🚷 view threshold
79,132.74 ops/s
(-36.28%)Baseline: 124,195.97 ops/s
-105,112.44 ops/s
(-132.83%)
src/btree-set.bench.ts > BTreeSet iterators > valuesFromReversed() from mid📈 view plot
🚷 view threshold
82,096.33 ops/s
(-34.00%)Baseline: 124,385.24 ops/s
-91,833.46 ops/s
(-111.86%)
src/btree-set.bench.ts > BTreeSet iterators > valuesReversed() full scan📈 view plot
🚷 view threshold
41,306.98 ops/s
(-34.24%)Baseline: 62,814.48 ops/s
-47,030.37 ops/s
(-113.86%)
src/btree-set.bench.ts > BTreeSet lookups > get() hit📈 view plot
🚷 view threshold
4,369,066.67 ops/s
(-7.36%)Baseline: 4,716,141.02 ops/s
2,923,408.50 ops/s
(66.91%)
src/btree-set.bench.ts > BTreeSet lookups > has() hit📈 view plot
🚷 view threshold
4,391,254.92 ops/s
(-6.93%)Baseline: 4,718,042.57 ops/s
3,018,689.01 ops/s
(68.74%)
src/btree-set.bench.ts > BTreeSet lookups > has() miss📈 view plot
🚷 view threshold
6,537,152.48 ops/s
(-4.08%)Baseline: 6,815,274.01 ops/s
5,242,553.22 ops/s
(80.20%)
src/btree-set.bench.ts > BTreeSet mutations > add() 100 sequential keys📈 view plot
🚷 view threshold
38,147.22 ops/s
(-19.10%)Baseline: 47,153.07 ops/s
-667.42 ops/s
(-1.75%)
src/btree-set.bench.ts > BTreeSet mutations > add() 1000 sequential keys📈 view plot
🚷 view threshold
3,375.06 ops/s
(-18.56%)Baseline: 4,144.05 ops/s
52.97 ops/s
(1.57%)
src/btree-set.bench.ts > BTreeSet mutations > add() then delete() single key📈 view plot
🚷 view threshold
1,939,963.40 ops/s
(-22.51%)Baseline: 2,503,430.72 ops/s
-466,338.08 ops/s
(-24.04%)
src/btree-set.bench.ts > BTreeSet mutations > fromSorted() 100 sequential keys📈 view plot
🚷 view threshold
440,169.90 ops/s
(-8.04%)Baseline: 478,663.68 ops/s
351,992.51 ops/s
(79.97%)
src/btree-set.bench.ts > BTreeSet mutations > fromSorted() 1000 sequential keys📈 view plot
🚷 view threshold
46,902.55 ops/s
(-12.93%)Baseline: 53,867.99 ops/s
24,829.96 ops/s
(52.94%)
src/btree-set.bench.ts > BTreeSet mutations > getOrCreateIndex pattern (new): sort + fromSorted()📈 view plot
🚷 view threshold
21,801.50 ops/s
(-3.65%)Baseline: 22,626.49 ops/s
17,173.57 ops/s
(78.77%)
src/btree-set.bench.ts > BTreeSet mutations > getOrCreateIndex pattern (old): add() loop after sort📈 view plot
🚷 view threshold
2,498.22 ops/s
(-11.90%)Baseline: 2,835.70 ops/s
1,006.04 ops/s
(40.27%)
src/size-of-value.bench.ts > getSizeOfValue performance > arrays > large array (100 items)📈 view plot
🚷 view threshold
575,961.80 ops/s
(-10.64%)Baseline: 644,530.81 ops/s
292,372.90 ops/s
(50.76%)
src/size-of-value.bench.ts > getSizeOfValue performance > arrays > small array (10 items)📈 view plot
🚷 view threshold
4,517,179.25 ops/s
(-10.83%)Baseline: 5,065,572.50 ops/s
2,166,096.37 ops/s
(47.95%)
src/size-of-value.bench.ts > getSizeOfValue performance > datasets > large dataset (100x512B)📈 view plot
🚷 view threshold
45,839.96 ops/s
(+14.58%)Baseline: 40,007.86 ops/s
9,528.05 ops/s
(20.79%)
src/size-of-value.bench.ts > getSizeOfValue performance > datasets > small dataset (10x256B)📈 view plot
🚷 view threshold
455,693.73 ops/s
(+15.28%)Baseline: 395,281.84 ops/s
93,285.53 ops/s
(20.47%)
src/size-of-value.bench.ts > getSizeOfValue performance > objects > nested object📈 view plot
🚷 view threshold
3,124,099.13 ops/s
(+10.49%)Baseline: 2,827,551.69 ops/s
1,357,985.09 ops/s
(43.47%)
src/size-of-value.bench.ts > getSizeOfValue performance > objects > structured object (1KB)📈 view plot
🚷 view threshold
4,732,057.47 ops/s
(+13.99%)Baseline: 4,151,130.40 ops/s
1,079,705.79 ops/s
(22.82%)
src/size-of-value.bench.ts > getSizeOfValue performance > objects > structured object (256B)📈 view plot
🚷 view threshold
4,732,044.23 ops/s
(+13.98%)Baseline: 4,151,645.46 ops/s
1,069,220.35 ops/s
(22.60%)
src/size-of-value.bench.ts > getSizeOfValue performance > primitives > boolean📈 view plot
🚷 view threshold
138,214,899.45 ops/s
(+21.82%)Baseline: 113,462,846.67 ops/s
-7,656,046.89 ops/s
(-5.54%)
src/size-of-value.bench.ts > getSizeOfValue performance > primitives > integer📈 view plot
🚷 view threshold
99,008,660.85 ops/s
(+16.75%)Baseline: 84,804,833.32 ops/s
12,560,277.86 ops/s
(12.69%)
src/size-of-value.bench.ts > getSizeOfValue performance > primitives > null📈 view plot
🚷 view threshold
117,515,048.92 ops/s
(+17.85%)Baseline: 99,716,857.75 ops/s
1,837,498.07 ops/s
(1.56%)
src/size-of-value.bench.ts > getSizeOfValue performance > primitives > string (100 chars)📈 view plot
🚷 view threshold
649,499.97 ops/s
(+5.54%)Baseline: 615,380.55 ops/s
452,767.43 ops/s
(69.71%)
src/tdigest.bench.ts > TDigest Benchmarks > add📈 view plot
🚷 view threshold
1.47 ops/s
(-2.74%)Baseline: 1.51 ops/s
1.17 ops/s
(79.33%)
src/tdigest.bench.ts > TDigest Benchmarks > addCentroid📈 view plot
🚷 view threshold
1.40 ops/s
(+1.00%)Baseline: 1.38 ops/s
1.33 ops/s
(95.59%)
src/tdigest.bench.ts > TDigest Benchmarks > addCentroidList📈 view plot
🚷 view threshold
1.39 ops/s
(+0.31%)Baseline: 1.39 ops/s
1.34 ops/s
(96.60%)
src/tdigest.bench.ts > TDigest Benchmarks > merge > addCentroid📈 view plot
🚷 view threshold
12,134.57 ops/s
(-0.59%)Baseline: 12,206.13 ops/s
9,279.64 ops/s
(76.47%)
src/tdigest.bench.ts > TDigest Benchmarks > merge > merge📈 view plot
🚷 view threshold
14,929.07 ops/s
(+2.52%)Baseline: 14,562.45 ops/s
11,482.91 ops/s
(76.92%)
src/tdigest.bench.ts > TDigest Benchmarks > quantile📈 view plot
🚷 view threshold
1.45 ops/s
(-3.91%)Baseline: 1.51 ops/s
1.24 ops/s
(85.62%)
🐰 View full continuous benchmarking report in Bencher

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown

🐰 Bencher Report

Branchmlaw/flip-in
Testbedself-hosted-metal

⚠️ WARNING: Truncated view!

The full continuous benchmarking report exceeds the maximum length allowed on this platform.

🚨 4 Alerts

🐰 View full continuous benchmarking report in Bencher

@tantaman tantaman changed the title Mlaw/flip in feat(zql): batch flip join requests May 8, 2026
@tantaman
tantaman changed the base branch from main to mlaw/flip-in-cleanup May 13, 2026 16:36
@tantaman
tantaman changed the base branch from mlaw/flip-in-cleanup to main May 13, 2026 16:37
@@ -42,7 +42,7 @@ describe('Chinook planner execution cost validation', () => {
extraIndexValidations: [

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

most of all the planner statistics got better which is good news.

Comment on lines +58 to +59
expect(pick(ast, ['where', 'conditions', 0, 'flip'])).toBe(false);
expect(pick(ast, ['where', 'conditions', 1, 'flip'])).toBe(true);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

hmm, why would we flip genre over album now? Maybe costs are about equal so it just picks one at random?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This flip cost change caused planner benchmarks to regress 30x.

// SCAN are now amortized across one combined IN-list query.
// Tightened from -0.5 / 10 / 10.
['correlation', 0.9],
['within-optimal', 1],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

wow, this is a big win!

['within-optimal', 1.4],
// Tightened from -1 / 1.4 after multi-IN AND propagation lets
// the source filter both join keys in one query.
['correlation', 0.9],

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🤩

tantaman and others added 7 commits May 14, 2026 11:46
Treat merge-sort as the canonical FlippedJoin fetch path. Drops
#fetchQuicksort, the #parentKeyIsUnique branch, and the now-unused
SourceSchema.uniqueIndexes plumbing through TableSource. Merge-sort
already groups child→parent lookups by parent-key value, so the
unique-key shortcut no longer earns its complexity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Uses `WHERE IN (...)` so flip-join fetches BATCH_SIZE keys at once.

This gives Goblin's "school administrator viewing students" query a 6x speedup.

(cherry picked from commit b94ff72)
@tantaman
tantaman added this pull request to the merge queue May 14, 2026
Merged via the queue into main with commit 8916f80 May 14, 2026
31 of 33 checks passed
@tantaman
tantaman deleted the mlaw/flip-in branch May 14, 2026 17:41
@tantaman

tantaman commented Jun 25, 2026 via email

Copy link
Copy Markdown
Contributor Author

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.

2 participants