Skip to content

refactor: remove redundant sort-run reservation stream - #26930

Merged
jayzhan211 merged 2 commits into
apache:mainfrom
geoffreyclaude:codex/sort-iterator-lookahead
Nov 7, 2026
Merged

jayzhan211 merged 2 commits into
apache:mainfrom
geoffreyclaude:codex/sort-iterator-lookahead

Conversation

@geoffreyclaude

@geoffreyclaude geoffreyclaude commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor

Which issue does this PR close?

Follow-up to the lookahead discussion in #26242.

Rationale for this change

Sort runs use two different paths to return memory as their output batches are consumed. The path that handles borrowed spill workspace also works when nothing is borrowed, making the separate ReservationStream wrapper redundant.

What changes are included in this PR?

Use the existing workspace-aware path for all sort runs and remove the unused private wrapper and its dedicated tests. Each batch returns borrowed workspace first, then releases its ordinary reservation.

What is the testing strategy for this PR?

Existing tests check lookahead under tight memory and keep pending spill output reserved, including zero, partial and full workspace loans. Output and cancellation checks cover row integrity and reservation cleanup.

Are there any user-facing changes?

Memory handoff and output ordering are preserved.

Prepared with AI assistance.

@github-actions github-actions Bot added the physical-plan Changes to the physical-plan crate label Oct 30, 2026
@geoffreyclaude
geoffreyclaude force-pushed the codex/sort-iterator-lookahead branch 2 times, most recently from b04cb7e to 2be04ec Compare November 2, 2026 10:33
@geoffreyclaude

Copy link
Copy Markdown
Contributor Author

run benchmark sort_tpch sort_tpch10

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6575608609-4230-vhdkj 6.12.105+ #1 SMP Sat Sep 19 07:55:25 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing codex/sort-iterator-lookahead (2be04ec) to d4c2a32 (merge-base) diff

Run configuration
run benchmark sort_tpch10

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark running (GKE) | trigger
Instance: c4a-highmem-16 (12 vCPU / 65 GiB) | Linux bench-c6575608609-4229-tmwxn 6.12.105+ #1 SMP Sat Sep 19 07:55:25 UTC 2026 aarch64 GNU/Linux

CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected

Comparing codex/sort-iterator-lookahead (2be04ec) to d4c2a32 (merge-base) diff

Run configuration
run benchmark sort_tpch

Results will be posted here when complete


File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing codex/sort-iterator-lookahead (2be04ec) to d4c2a32 (merge-base) diff

Run configuration
run benchmark sort_tpch
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and codex_sort-iterator-lookahead
--------------------
Benchmark sort_tpch1.json
--------------------
┏━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query ┃      HEAD ┃ codex_sort-iterator-lookahead ┃    Change ┃
┡━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Q1    │ 110.65 ms │                     110.47 ms │ no change │
│ Q2    │  97.90 ms │                      97.39 ms │ no change │
│ Q3    │ 618.27 ms │                     618.48 ms │ no change │
│ Q4    │ 157.24 ms │                     157.11 ms │ no change │
│ Q5    │ 234.17 ms │                     233.52 ms │ no change │
│ Q6    │ 246.80 ms │                     246.27 ms │ no change │
│ Q7    │ 391.19 ms │                     391.66 ms │ no change │
│ Q8    │ 294.76 ms │                     293.05 ms │ no change │
│ Q9    │ 310.59 ms │                     310.67 ms │ no change │
│ Q10   │ 451.64 ms │                     452.74 ms │ no change │
│ Q11   │ 238.73 ms │                     242.54 ms │ no change │
└───────┴───────────┴───────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                            ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                            │ 3151.95ms │
│ Total Time (codex_sort-iterator-lookahead)   │ 3153.90ms │
│ Average Time (HEAD)                          │  286.54ms │
│ Average Time (codex_sort-iterator-lookahead) │  286.72ms │
│ Queries Faster                               │         0 │
│ Queries Slower                               │         0 │
│ Queries with No Change                       │        11 │
│ Queries with Failure                         │         0 │
└──────────────────────────────────────────────┴───────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and codex_sort-iterator-lookahead
--------------------
Benchmark sort_tpch1.json
--------------------
┏━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query ┃                               HEAD ┃      codex_sort-iterator-lookahead ┃    Change ┃
┡━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Q1    │  110.65 / 111.82 ±0.97 / 113.54 ms │  110.47 / 111.03 ±0.81 / 112.63 ms │ no change │
│ Q2    │    97.90 / 99.10 ±0.87 / 100.58 ms │    97.39 / 98.50 ±1.16 / 100.67 ms │ no change │
│ Q3    │  618.27 / 620.42 ±1.83 / 623.19 ms │  618.48 / 620.62 ±2.56 / 625.47 ms │ no change │
│ Q4    │  157.24 / 158.58 ±1.08 / 160.51 ms │  157.11 / 157.83 ±0.62 / 158.75 ms │ no change │
│ Q5    │  234.17 / 234.71 ±0.49 / 235.41 ms │  233.52 / 234.22 ±0.65 / 235.38 ms │ no change │
│ Q6    │  246.80 / 248.23 ±1.87 / 251.90 ms │  246.27 / 247.84 ±2.08 / 251.90 ms │ no change │
│ Q7    │  391.19 / 392.80 ±1.58 / 395.80 ms │  391.66 / 393.19 ±1.32 / 395.46 ms │ no change │
│ Q8    │  294.76 / 299.89 ±3.47 / 303.33 ms │  293.05 / 298.57 ±4.78 / 305.46 ms │ no change │
│ Q9    │ 310.59 / 326.30 ±11.69 / 341.38 ms │ 310.67 / 329.15 ±12.55 / 344.10 ms │ no change │
│ Q10   │  451.64 / 459.58 ±7.23 / 471.32 ms │  452.74 / 458.48 ±4.32 / 465.20 ms │ no change │
│ Q11   │ 238.73 / 251.17 ±13.16 / 271.78 ms │  242.54 / 249.04 ±8.82 / 266.36 ms │ no change │
└───────┴────────────────────────────────────┴────────────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Benchmark Summary                            ┃           ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Total Time (HEAD)                            │ 3202.60ms │
│ Total Time (codex_sort-iterator-lookahead)   │ 3198.47ms │
│ Average Time (HEAD)                          │  291.15ms │
│ Average Time (codex_sort-iterator-lookahead) │  290.77ms │
│ Queries Faster                               │         0 │
│ Queries Slower                               │         0 │
│ Queries with No Change                       │        11 │
│ Queries with Failure                         │         0 │
└──────────────────────────────────────────────┴───────────┘

Resource Usage

sort_tpch — base (merge-base)

Metric Value
Wall time 20.0s
Peak memory 2.3 GiB
Avg memory 1.1 GiB
CPU user 61.0s
CPU sys 2.8s
Peak spill 0 B

sort_tpch — branch

Metric Value
Wall time 20.0s
Peak memory 2.3 GiB
Avg memory 1.1 GiB
CPU user 61.0s
CPU sys 2.8s
Peak spill 0 B

File an issue against this benchmark runner

@adriangbot

Copy link
Copy Markdown

🤖 Benchmark completed (GKE) | trigger

Instance: c4a-highmem-16 (12 vCPU / 65 GiB)

Comparing codex/sort-iterator-lookahead (2be04ec) to d4c2a32 (merge-base) diff

Run configuration
run benchmark sort_tpch10
CPU Details (lscpu)
Architecture:                            aarch64
CPU op-mode(s):                          64-bit
Byte Order:                              Little Endian
CPU(s):                                  16
On-line CPU(s) list:                     0-15
Vendor ID:                               ARM
Model name:                              Neoverse-V2
Model:                                   1
Thread(s) per core:                      1
Core(s) per cluster:                     16
Socket(s):                               -
Cluster(s):                              1
Stepping:                                r0p1
BogoMIPS:                                2000.00
Flags:                                   fp asimd evtstrm aes pmull sha1 sha2 crc32 atomics fphp asimdhp cpuid asimdrdm jscvt fcma lrcpc dcpop sha3 sm3 sm4 asimddp sha512 sve asimdfhm dit uscat ilrcpc flagm sb paca pacg dcpodp sve2 sveaes svepmull svebitperm svesha3 svesm4 flagm2 frint svei8mm svebf16 i8mm bf16 dgh rng bti
L1d cache:                               1 MiB (16 instances)
L1i cache:                               1 MiB (16 instances)
L2 cache:                                32 MiB (16 instances)
L3 cache:                                80 MiB (1 instance)
NUMA node(s):                            1
NUMA node0 CPU(s):                       0-15
Vulnerability Gather data sampling:      Not affected
Vulnerability Indirect target selection: Not affected
Vulnerability Itlb multihit:             Not affected
Vulnerability L1tf:                      Not affected
Vulnerability Mds:                       Not affected
Vulnerability Meltdown:                  Not affected
Vulnerability Mmio stale data:           Not affected
Vulnerability Reg file data sampling:    Not affected
Vulnerability Retbleed:                  Not affected
Vulnerability Spec rstack overflow:      Not affected
Vulnerability Spec store bypass:         Mitigation; Speculative Store Bypass disabled via prctl
Vulnerability Spectre v1:                Mitigation; __user pointer sanitization
Vulnerability Spectre v2:                Mitigation; CSV2, BHB
Vulnerability Srbds:                     Not affected
Vulnerability Tsa:                       Not affected
Vulnerability Tsx async abort:           Not affected
Vulnerability Vmscape:                   Not affected
Details

Comparing HEAD and codex_sort-iterator-lookahead
--------------------
Benchmark sort_tpch10.json
--------------------
┏━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query ┃       HEAD ┃ codex_sort-iterator-lookahead ┃    Change ┃
┡━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Q1    │ 1455.12 ms │                    1428.17 ms │ no change │
│ Q2    │ 1360.76 ms │                    1311.11 ms │ no change │
│ Q3    │ 8729.94 ms │                    8632.45 ms │ no change │
│ Q4    │ 2178.75 ms │                    2170.28 ms │ no change │
│ Q5    │ 2403.36 ms │                    2368.86 ms │ no change │
│ Q6    │ 2519.05 ms │                    2501.69 ms │ no change │
│ Q7    │ 4394.62 ms │                    4390.31 ms │ no change │
│ Q8    │ 3391.61 ms │                    3359.19 ms │ no change │
│ Q9    │ 3740.77 ms │                    3619.79 ms │ no change │
│ Q10   │ 5876.85 ms │                    5743.18 ms │ no change │
│ Q11   │ 3398.63 ms │                    3425.22 ms │ no change │
└───────┴────────────┴───────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Benchmark Summary                            ┃            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Total Time (HEAD)                            │ 39449.45ms │
│ Total Time (codex_sort-iterator-lookahead)   │ 38950.24ms │
│ Average Time (HEAD)                          │  3586.31ms │
│ Average Time (codex_sort-iterator-lookahead) │  3540.93ms │
│ Queries Faster                               │          0 │
│ Queries Slower                               │          0 │
│ Queries with No Change                       │         11 │
│ Queries with Failure                         │          0 │
└──────────────────────────────────────────────┴────────────┘

Distribution per query (min / mean ±stddev / max):

Comparing HEAD and codex_sort-iterator-lookahead
--------------------
Benchmark sort_tpch10.json
--------------------
┏━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━┓
┃ Query ┃                                   HEAD ┃          codex_sort-iterator-lookahead ┃    Change ┃
┡━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━┩
│ Q1    │   1455.12 / 1462.81 ±5.05 / 1469.37 ms │   1428.17 / 1432.36 ±2.83 / 1435.79 ms │ no change │
│ Q2    │  1360.76 / 1383.87 ±20.78 / 1414.95 ms │  1311.11 / 1341.72 ±23.73 / 1373.76 ms │ no change │
│ Q3    │ 8729.94 / 8944.20 ±129.14 / 9096.27 ms │ 8632.45 / 8787.46 ±117.96 / 8942.34 ms │ no change │
│ Q4    │  2178.75 / 2207.81 ±29.79 / 2260.56 ms │  2170.28 / 2191.36 ±19.51 / 2227.28 ms │ no change │
│ Q5    │   2403.36 / 2410.22 ±8.37 / 2425.55 ms │  2368.86 / 2390.46 ±19.76 / 2414.22 ms │ no change │
│ Q6    │  2519.05 / 2537.89 ±10.41 / 2550.31 ms │  2501.69 / 2517.44 ±10.49 / 2533.84 ms │ no change │
│ Q7    │  4394.62 / 4460.67 ±43.99 / 4519.00 ms │  4390.31 / 4413.13 ±14.64 / 4434.88 ms │ no change │
│ Q8    │  3391.61 / 3477.80 ±64.07 / 3568.34 ms │  3359.19 / 3436.72 ±56.65 / 3514.28 ms │ no change │
│ Q9    │  3740.77 / 3787.96 ±36.68 / 3837.00 ms │  3619.79 / 3762.02 ±74.84 / 3827.40 ms │ no change │
│ Q10   │ 5876.85 / 6216.62 ±170.62 / 6327.73 ms │ 5743.18 / 6081.56 ±181.57 / 6240.66 ms │ no change │
│ Q11   │  3398.63 / 3431.30 ±40.21 / 3507.59 ms │  3425.22 / 3453.90 ±31.37 / 3507.57 ms │ no change │
└───────┴────────────────────────────────────────┴────────────────────────────────────────┴───────────┘
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┓
┃ Benchmark Summary                            ┃            ┃
┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━┩
│ Total Time (HEAD)                            │ 40321.14ms │
│ Total Time (codex_sort-iterator-lookahead)   │ 39808.10ms │
│ Average Time (HEAD)                          │  3665.56ms │
│ Average Time (codex_sort-iterator-lookahead) │  3618.92ms │
│ Queries Faster                               │          0 │
│ Queries Slower                               │          0 │
│ Queries with No Change                       │         11 │
│ Queries with Failure                         │          0 │
└──────────────────────────────────────────────┴────────────┘

Resource Usage

sort_tpch10 — base (merge-base)

Metric Value
Wall time 205.0s
Peak memory 19.2 GiB
Avg memory 7.1 GiB
CPU user 834.9s
CPU sys 73.3s
Peak spill 0 B

sort_tpch10 — branch

Metric Value
Wall time 200.0s
Peak memory 19.1 GiB
Avg memory 7.3 GiB
CPU user 822.9s
CPU sys 71.7s
Peak spill 0 B

File an issue against this benchmark runner

@geoffreyclaude
geoffreyclaude marked this pull request as ready for review November 2, 2026 16:43
@geoffreyclaude
geoffreyclaude marked this pull request as draft November 2, 2026 16:47
@geoffreyclaude
geoffreyclaude force-pushed the codex/sort-iterator-lookahead branch from 2be04ec to dc31005 Compare November 4, 2026 19:44
@geoffreyclaude
geoffreyclaude force-pushed the codex/sort-iterator-lookahead branch from dc31005 to 5001e40 Compare November 4, 2026 19:59
@geoffreyclaude
geoffreyclaude marked this pull request as ready for review November 4, 2026 20:32

@jayzhan211 jayzhan211 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@geoffreyclaude thanks for this cleanup! I compared reservation traces and tight-memory outcomes against main, and they match, so the inline comments are all optional.

Comment on lines +1000 to +1001
/// Reservations release as output is read; parallel preparation reads one
/// batch ahead to return spill workspace before the merge builds its cursor.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Optional cleanup: the old with_lookahead doc said a run can hold a prefetched batch that's no longer reserved. That's worth keeping, since it's the one place the accounting runs behind the data.

Suggested change
/// Reservations release as output is read; parallel preparation reads one
/// batch ahead to return spill workspace before the merge builds its cursor.
/// Reservations release as output is read; parallel preparation reads one
/// batch ahead to return spill workspace before the merge builds its cursor,
/// so each run can hold one prefetched batch whose reservation is already
/// released.

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.

Restored the original with_lookahead documentation, including this caveat.

batch: RecordBatch,
reservation: MemoryReservation,
shared_input: Option<Arc<MemoryReservation>>,
read_ahead: bool,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Optional, and an alternative to my other suggestion: keep with_lookahead and buffered(2) from main and delete only the ReservationStream branch. I ran that: 200 deletions, no additions, no read_ahead parameter, and the same tests and capacity-sweep results as this PR. You called the per-batch bookkeeping premature on #26242 and the bot shows no change here, so is there a cost buffered(2) still has that you want to remove? If not, I'd take the smaller diff.

-use crate::stream::ReservationStream;
-            if workspace.size() == 0 {
-                return Ok(Box::pin(ReservationStream::new(
-                    Arc::clone(&schema),
-                    Box::pin(RecordBatchStreamAdapter::new(
-                        Arc::clone(&schema),
-                        futures::stream::iter(sorted_batches.into_iter().map(Ok)),
-                    )),
-                    reservation,
-                )) as SendableRecordBatchStream);
-            }
-

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.

Agreed, the measurements don’t justify the extra code. Adopted your smaller alternative: the PR now removes 200 lines with no additions.

Comment on lines 1042 to 1075
let workspace =
merge_pool.borrow(total_sorted_size.saturating_sub(reservation.size()));
reservation
.try_resize(total_sorted_size - workspace.size())
.map_err(Self::err_with_oom_context)?;

if workspace.size() == 0 {
return Ok(Box::pin(ReservationStream::new(
Arc::clone(&schema),
Box::pin(RecordBatchStreamAdapter::new(
Arc::clone(&schema),
futures::stream::iter(sorted_batches.into_iter().map(Ok)),
)),
reservation,
)) as SendableRecordBatchStream);
}

// Return borrowed workspace first so the merge's cursors can reuse
// it immediately. Both reservations also release on stream drop.
let batches = sorted_batches.into_iter().map(move |batch| {
let size = get_record_batch_memory_size(&batch);
let borrowed = size.min(workspace.size());
workspace.shrink(borrowed);
reservation.shrink(size - borrowed);
Ok(batch)
// An empty loan must not keep unused merge workspace alive.
let mut workspace = (workspace.size() != 0).then_some(workspace);
let mut batches = Some(
sorted_batches
.into_iter()
.inspect(move |batch| {
let size = get_record_batch_memory_size(batch);
let borrowed = workspace.as_mut().map_or(0, |workspace| {
let borrowed = size.min(workspace.size());
workspace.shrink(borrowed);
borrowed
});
reservation.shrink(size - borrowed);
})
.peekable(),
);
// Output is already in memory. Peeking releases the next batch's
// reservation before the merge allocates its current cursor.
let batches = std::iter::from_fn(move || {
let iter = batches.as_mut()?;
let batch = iter.next()?;
if read_ahead && iter.peek().is_none() {
// Release the exhausted iterator's workspace loan now, so it
// cannot keep unused merge memory reserved until another poll.
drop(batches.take());
}
Some(Ok(batch))
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Optional cleanup: the Options and drop(batches.take()) can go. The loan is already 0 bytes at exhaustion and the merge's reservation keeps the pool alive, so pool bytes didn't change in any trace, and all sorts:: tests pass without them. The one difference is a single-run stream outliving its sorter with workspace still retained, which only tests do: sort() calls release_unused first and the spill path keeps the sorter alive.

Suggested change
let workspace =
merge_pool.borrow(total_sorted_size.saturating_sub(reservation.size()));
reservation
.try_resize(total_sorted_size - workspace.size())
.map_err(Self::err_with_oom_context)?;
if workspace.size() == 0 {
return Ok(Box::pin(ReservationStream::new(
Arc::clone(&schema),
Box::pin(RecordBatchStreamAdapter::new(
Arc::clone(&schema),
futures::stream::iter(sorted_batches.into_iter().map(Ok)),
)),
reservation,
)) as SendableRecordBatchStream);
}
// Return borrowed workspace first so the merge's cursors can reuse
// it immediately. Both reservations also release on stream drop.
let batches = sorted_batches.into_iter().map(move |batch| {
let size = get_record_batch_memory_size(&batch);
let borrowed = size.min(workspace.size());
workspace.shrink(borrowed);
reservation.shrink(size - borrowed);
Ok(batch)
// An empty loan must not keep unused merge workspace alive.
let mut workspace = (workspace.size() != 0).then_some(workspace);
let mut batches = Some(
sorted_batches
.into_iter()
.inspect(move |batch| {
let size = get_record_batch_memory_size(batch);
let borrowed = workspace.as_mut().map_or(0, |workspace| {
let borrowed = size.min(workspace.size());
workspace.shrink(borrowed);
borrowed
});
reservation.shrink(size - borrowed);
})
.peekable(),
);
// Output is already in memory. Peeking releases the next batch's
// reservation before the merge allocates its current cursor.
let batches = std::iter::from_fn(move || {
let iter = batches.as_mut()?;
let batch = iter.next()?;
if read_ahead && iter.peek().is_none() {
// Release the exhausted iterator's workspace loan now, so it
// cannot keep unused merge memory reserved until another poll.
drop(batches.take());
}
Some(Ok(batch))
});
let mut workspace =
merge_pool.borrow(total_sorted_size.saturating_sub(reservation.size()));
reservation
.try_resize(total_sorted_size - workspace.size())
.map_err(Self::err_with_oom_context)?;
let mut batches = sorted_batches
.into_iter()
.inspect(move |batch| {
let size = get_record_batch_memory_size(batch);
let borrowed = size.min(workspace.size());
workspace.shrink(borrowed);
reservation.shrink(size - borrowed);
})
.peekable();
// Output is already in memory. Peeking releases the next batch's
// reservation before the merge allocates its current cursor.
let batches = std::iter::from_fn(move || {
let batch = batches.next()?;
if read_ahead {
batches.peek();
}
Some(Ok(batch))
});

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.

Your smaller alternative takes care of this too: the custom iterator, both Options and the manual cleanup are gone.

@geoffreyclaude geoffreyclaude changed the title refactor: use iterator lookahead for materialized sort runs refactor: remove redundant sort-run reservation stream Nov 6, 2026

@jayzhan211 jayzhan211 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks @geoffreyclaude !

@jayzhan211
jayzhan211 added this pull request to the merge queue Nov 7, 2026
Merged via the queue into apache:main with commit 8acb40d Nov 7, 2026
44 of 45 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

physical-plan Changes to the physical-plan crate v57.0.0

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants