SubhamSinghal opened a new issue, #26381:
URL: https://github.com/apache/datafusion/issues/26381

   ### Describe the bug
   
   With `datafusion.optimizer.enable_window_topn = true`, `PartitionedTopKExec` 
keeps the whole data buffers of
   `Utf8View` / `BinaryView` input columns alive for the few rows it retains, 
and charges them to the memory pool.
   
   Each input batch's admitted rows are gathered into the operator's 
`RecordBatchStore` with `take`. For view
   types `take` copies the views but shares every one of the input batch's data 
buffers, so retaining 10 rows of an
   8192-row batch keeps that batch's whole string buffer resident. Store 
compaction can't release it:
   - it is triggered by a row ratio (`total_rows > 2 × live rows`), and the 
store already holds about one row
     per live row;
   - when it does run, it rewrites with `interleave`, which shares the same 
buffers again.
   
   Parquet scans produce `Utf8View` by default (`schema_force_view_types = 
true`), so string payloads read from
   Parquet hit this without any extra configuration.
   
   ### To Reproduce
   
   In `datafusion-cli`, create 4.1M rows in which each 8192-row batch 
introduces 10 new partitions, with a
   114-byte string payload:
   
       COPY (
         SELECT CAST((i / 8192) * 10 + (i % 10) AS BIGINT) AS pk,
                CAST(i AS BIGINT) AS v,
                concat(md5(CAST(i AS VARCHAR)), md5(CAST(i + 1 AS VARCHAR)),
                       md5(CAST(i + 2 AS VARCHAR)), substr(md5(CAST(i + 3 AS 
VARCHAR)), 1, 18)) AS s
         FROM generate_series(0, 4095999) AS t(i)
         ORDER BY i
       ) TO '/tmp/ptk.parquet' STORED AS PARQUET;
   
       CREATE EXTERNAL TABLE t STORED AS PARQUET LOCATION '/tmp/ptk.parquet';
       SET datafusion.execution.target_partitions = 1;
       SET datafusion.optimizer.enable_window_topn = true;
       SELECT count(*), sum(length(s)), sum(pk), sum(v), sum(rn)
       FROM (SELECT pk, v, s, row_number() OVER (PARTITION BY pk ORDER BY v) AS 
rn FROM t)
       WHERE rn <= 1;
   
   The query retains 5,000 rows (~570 KB of payload). Smallest `--memory-limit` 
it passes with:
   
   | | memory limit |
   |---|---|
   | `enable_window_topn = true` | **920M** (fails at 919M: `Resources 
exhausted ... PartitionedTopK[0]`) |
   | `enable_window_topn = true`, payload written as `Utf8` | 3M |
   | `enable_window_topn = false` (sort plan, spills) | 17M |
   
   The memory needed grows with the input, not with the rows retained: 409K 
rows of the same shape need 92M.
   
   With more retained rows the over-charging gets worse. For `rn <= 10` over 
100K partitions (4.1M rows,
   `pk = i % 100000`), the reservation asks for 36.7 GB in a single allocation 
while peak RSS is 729 MB, so the
   query cannot run under any memory limit. `rank()` behaves the same way.
   
   
   ### Expected behavior
   
   Retained memory proportional to the retained rows, as with `Utf8`. Enabling 
the optimization shouldn't need
   more memory than the sort plan it replaces.
   
   ### Additional context
   
   _No response_
   
   ### Willingness to contribute
   
   None


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to