Skip to content

WindowAggExec and BoundedWindowAggExec reserve no memory, so a memory limit does not bound them #26707

Open

Description

@jayzhan211

Describe the bug

WindowAggExec and BoundedWindowAggExec never reserve memory: there is no MemoryConsumer or MemoryReservation anywhere under datafusion/physical-plan/src/windows/. Both hold input rows for as long as their window needs them. WindowAggExec buffers its whole input before it computes anything, and BoundedWindowAggExec buffers the rows its frames still need.

So a memory limit has no effect on them. A window query neither spills nor fails with ResourcesExhausted; it uses as much memory as its input needs, and in a container with a memory limit the process can be killed instead. The contract in memory_pool/mod.rs asks an operator whose memory grows with its input to reserve that memory before allocating it, and to spill or return an error when the pool refuses.

To Reproduce

Run each command in its own process (/usr/bin/time -l is macOS; use -v on Linux). Drop -m 200m for the "no limit" column.

# Query 1: WindowAggExec, the whole input is one window partition
/usr/bin/time -l datafusion-cli -m 200m \
  -c "SELECT max(s) FROM (SELECT sum(v) OVER () AS s FROM generate_series(1, 50000000) AS t(v));"

# Query 2: BoundedWindowAggExec, a frame that reaches 20M rows ahead
/usr/bin/time -l datafusion-cli -m 200m \
  -c "SET datafusion.execution.batch_size = 1000000;" \
  -c "SELECT max(s) FROM (SELECT sum(v) OVER (ORDER BY v ROWS BETWEEN CURRENT ROW AND 20000000 FOLLOWING) AS s FROM generate_series(1, 40000000) AS t(v));"

Query 2 uses a large batch size only to keep the run short: BoundedWindowAggExec re-concatenates its buffered rows for every input batch.

Expected behavior

Query Plan Peak RSS, no limit Peak RSS, -m 200m Expected with -m 200m
1 WindowAggExec 1186 MiB (3 runs) 1193 to 1226 MiB, succeeds (6 runs) Fails with ResourcesExhausted once the buffered rows exceed the limit (or spills, after #22946)
2 BoundedWindowAggExec 2.8 to 3.2 GiB (5 runs) 2.9 to 3.7 GiB, succeeds (10 runs) Same as query 1

Measured with datafusion-cli built from main at b93ef03 (release profile without LTO) on macOS arm64.

Additional context

Willingness to contribute

I'm not planning to fix this myself, so anyone is welcome to pick it up. Feel free to ping me with questions or when you'd like a review.

Activity

  1. Kristofferjoha commented on Oct 25, 2026

    @Kristofferjoha
    Contributor

    take

  2. Kristofferjoha commented on Oct 27, 2026

    @Kristofferjoha
    Contributor

    @jayzhan211 I started working on this, but accounting for shared buffers, temporary copies, and window state got more complicated than I expected. My patch has grown quite large, and I could use some guidance.

    Would accounting for buffered input and concatenation copies be a reasonable first PR? Or should this be approached differently?

    EDIT: after discussion on #26592 i will unassign myself from this one. Worked on it for a couple of days and realized it was a larger change than anticipated. Anyone is free to pick this up.

  3. Kristofferjoha commented on Oct 30, 2026

    @Kristofferjoha
    Contributor

    untake

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions