GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — perf(thrift): return inline Arrow result memory to the OS when freed by vuanhphung · Pull Request #1036 · databricks/databricks-sql-python

Conversation

@vuanhphung

@vuanhphung vuanhphung commented Nov 12, 2026 •
edited
Loading

Uh oh!

There was an error while loading. Please reload this page.

Copy link
Copy Markdown
Collaborator

What type of PR is this?

  • Other (performance)

Description

After a large inline Arrow result on Thrift is released (for example with use_cloud_fetch=False), the process keeps almost all of its memory. A 1.7 GB fetchall_arrow() left about 1.6 GB resident on the live warehouse. It is not a leak: after freeing a 562 MB result, glibc's malloc_stats() shows 590 MB of arena memory with 25 MB in use, and malloc_trim(0) (as a diagnostic only) brings RSS back to baseline. Each FetchResults row set (25 batches, ~3.7 MB here) was concatenated into a bytearray grown on the malloc heap. The Arrow table reads it zero-copy, so these buffers live as long as the result, interleaved with short-lived reply buffers, and glibc can't give the space back once something above them stays allocated.

This PR builds each row set's IPC stream in its own private anonymous mmap, which goes back to the OS when the table is released. The map is reserved from the compressed size (4x estimate; untouched pages cost no memory) and filled batch by batch as each batch is decompressed, so peak memory per row set is the same as before. If the estimate is too small, the map grows via mremap; once the stream is complete it is shrunk to its exact size. The map is used on Linux only, where glibc showed the retention. Each live map is one VMA, so a stream moves to a map only once it holds at least 1 MiB (it starts on the heap, or is mapped up front when its compressed size already reaches 1 MiB); reaching the default vm.max_map_count takes over 64 GB of inline results held at once. Smaller streams, mmap failures, and other platforms use the old bytearray. Decompression, the IPC read and errors are unchanged. Only convert_arrow_based_set_to_arrow_table in utils.py changes. #1032 uses the same technique for CloudFetch files; the two are independent, and this PR carries its own small mmap helper because cloudfetch/downloader.py can't import utils.py.

How is this tested?

  • Unit tests
  • E2E Tests
  • Manually

New unit tests cover large sets (lz4 on and off) read from a private map, growth when the estimate is too small, the final shrink, the heap fallback when the map can't grow or mmap fails, non-Linux platforms, small sets staying on the heap, schema-only sets, and the table keeping its map alive after the inputs are gone. They also pass on pyarrow 14.0.2. The full unit suite passes. E2E: the TestPySQLCoreSuite fetch and Arrow tests (inline and CloudFetch) and test_complex_types.py pass. Manually, inline results (lz4 on and off) matched CloudFetch results for 2M rows of mixed types (nulls, decimals, timestamps, dates, arrays, structs, maps) via fetchall_arrow, fetchmany_arrow and fetchall.

Live Thrift warehouse, use_cloud_fetch=False, 3 fresh processes per cell interleaved with main (final head), median RSS increase over the pre-query baseline:

Workload Peak, main Peak, this PR After free, main After free, this PR Fetch time, main → PR
fetchall_arrow() 110 MB 126 MB 118 MB 104 MB 6 MB 2.90 → 2.72 s
fetchall_arrow() 562 MB 601 MB 583 MB 475 MB 12 MB 14.34 → 15.27 s
fetchall_arrow() 1.7 GB 1811 MB 1758 MB 1679 MB 26 MB 44.63 → 44.51 s
fetchall_arrow() 573 MB, highly compressible 612 MB 590 MB 540 MB 11 MB 8.20 → 8.61 s
fetchmany_arrow(100k) loop, 562 MB 31 MB 17 MB 15 MB 5 MB 14.35 → 14.72 s
fetchall() rows, 110 MB 1662 MB 1654 MB 162 MB 74 MB 8.43 → 8.61 s

Fetch time is dominated by one ~0.1 s FetchResults round trip per 100k rows, and the differences above go both ways within noise. The conversion itself, timed in-process during the same runs, changes by at most ~0.7 ms per round trip (2.34 s → 2.29 s for the 1.7 GB result; 0.62 s → 0.73 s for the streaming loop). A single 51 MB row set (wide rows) converts with no transient memory on both main and this PR.

Related Tickets & Documents

Same technique as #1032 (CloudFetch). Both edit CHANGELOG.md under Unreleased, so whichever merges second has a one-line conflict there.

This pull request and its description were written by Isaac.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vuanhphung Isaac
perf(thrift): return inline Arrow result memory to the OS when freed
4089075
Each inline FetchResults row set was decompressed into a bytearray grown
on the malloc heap. The Arrow table reads it zero-copy, so ~3.7 MB per
round trip stayed live there, mixed with short-lived reply buffers; after
a large result was released glibc kept almost all of it resident (a
1.7 GB inline fetchall_arrow left ~1.6 GB). Copy each set's stream into
a private anonymous mmap instead (pre-faulted with MAP_POPULATE where
available), which goes back to the OS when the table is freed. Sets under
128 KiB and mmap failures keep the heap path.

Signed-off-by: Vu Anh Phung <[email protected]>
Co-authored-by: Isaac <[email protected]>
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 10:10 — with GitHub Actions Inactive
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 10:10 — with GitHub Actions Inactive
peco-review-bot[bot]
peco-review-bot Bot approved these changes Nov 12, 2026
View reviewed changes

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good. There is one low note: the lz4 path now briefly needs about twice the row set's memory while the batches are joined. The mmap handling is otherwise correct: anonymous MAP_PRIVATE with MAP_ANONYMOUS added implicitly by CPython, py_buffer keeps the mapping alive, and failures fall back to the heap. The tests cover the large, small, failure and schema-only paths.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/databricks/sql/utils.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@peco-review-bot
peco-review-bot Bot temporarily deployed to azure-prod November 12, 2026 10:13 Inactive
@vuanhphung Isaac
fix(thrift): fill the inline Arrow map batch by batch
98c65bb
Holding every decompressed batch until the copy needed about twice the
row set's memory, and releasing the parts did not help because glibc
keeps freed chunks resident. Reserve the map from the compressed size
instead (untouched pages cost nothing), copy each batch in as it is
decompressed, and grow the map with mremap on overflow. Platforms
without mremap continue on the heap. A 51 MB row set now converts with
no transient, as on main.

Signed-off-by: Vu Anh Phung <[email protected]>
Co-authored-by: Isaac <[email protected]>
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 10:31 — with GitHub Actions Inactive
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 10:31 — with GitHub Actions Inactive
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 10:31 — with GitHub Actions Inactive
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 10:31 — with GitHub Actions Inactive
@peco-review-bot
peco-review-bot Bot had a problem deploying to azure-prod November 12, 2026 10:34 Error
peco-review-bot[bot]
peco-review-bot Bot approved these changes Nov 12, 2026
View reviewed changes

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Verdict: 2 Low

Looks good: the change is correct and well contained. My two notes are both low severity: the tests for the grow and fallback paths don't check that those paths actually run, and the 4x over-reservation is never trimmed after the map is filled.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread tests/unit/test_util.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/databricks/sql/utils.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@peco-review-bot
peco-review-bot Bot temporarily deployed to azure-prod November 12, 2026 10:34 Inactive
@peco-review-bot
peco-review-bot Bot had a problem deploying to azure-prod November 12, 2026 10:34 Error
@vuanhphung Isaac
fix(thrift): shrink the inline Arrow map; keep non-POSIX on the heap
ac8fb87
Release the unused part of the size estimate once the stream is
complete, so it no longer counts against a strict overcommit limit
while the result is alive. Use the map only where MAP_PRIVATE exists:
the heap retention is a glibc behaviour, and Windows commits anonymous
maps up front and copies on resize. Tests now assert the growth,
shrink, and heap-fallback paths are actually taken.

Signed-off-by: Vu Anh Phung <[email protected]>
Co-authored-by: Isaac <[email protected]>
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 11:15 — with GitHub Actions Error
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 11:15 — with GitHub Actions Inactive
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 11:15 — with GitHub Actions Inactive
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 11:15 — with GitHub Actions Error
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 11:15 — with GitHub Actions Error
@peco-review-bot
peco-review-bot Bot had a problem deploying to azure-prod November 12, 2026 11:16 Error
@peco-review-bot
peco-review-bot Bot temporarily deployed to azure-prod November 12, 2026 11:16 Inactive
peco-review-bot[bot]
peco-review-bot Bot approved these changes Nov 12, 2026
View reviewed changes

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Verdict: 2 Low

Looks good: the mmap-backed buffer is correct. Buffer lifetime is tied to the Arrow table through py_buffer, the fallbacks are complete, and the tests are thorough. Two low-severity concerns: each live row set holds its own mapping (mapping-count limit), and the heap fallback when the map can't grow makes extra transient copies.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/databricks/sql/utils.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

Comment thread src/databricks/sql/utils.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@peco-review-bot
peco-review-bot Bot had a problem deploying to azure-prod November 12, 2026 11:17 Error
@peco-review-bot
peco-review-bot Bot temporarily deployed to azure-prod November 12, 2026 11:17 Inactive
@vuanhphung Isaac
fix(thrift): map inline Arrow streams on Linux only; copy fallback vi…
7f874a7
…a memoryview

The heap retention this works around is glibc's, so other platforms keep
the previous bytearray. When a map can't grow, copy what was written via
a memoryview instead of a temporary bytes slice.

Signed-off-by: Vu Anh Phung <[email protected]>
Co-authored-by: Isaac <[email protected]>
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 11:19 — with GitHub Actions Inactive
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 11:19 — with GitHub Actions Inactive
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 11:20 — with GitHub Actions Error
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 11:20 — with GitHub Actions Error
@peco-review-bot
peco-review-bot Bot had a problem deploying to azure-prod November 12, 2026 13:06 Error
@peco-review-bot
peco-review-bot Bot temporarily deployed to azure-prod November 12, 2026 13:06 Inactive
@vuanhphung Isaac
test(thrift): patch only utils' mmap name in the inline Arrow tests
66c40ed
The tests replaced mmap.mmap on the stdlib module for the whole process.
Swap the mmap module seen by utils instead, and give the large-set
fixture an explicit check that it stays above the mmap floor.

Signed-off-by: Vu Anh Phung <[email protected]>
Co-authored-by: Isaac <[email protected]>
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 13:10 — with GitHub Actions Inactive
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 13:10 — with GitHub Actions Inactive
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 13:10 — with GitHub Actions Error
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 13:10 — with GitHub Actions Error
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 13:10 — with GitHub Actions Error
peco-review-bot[bot]
peco-review-bot Bot approved these changes Nov 12, 2026
View reviewed changes

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Verdict: 1 Nit

Looks good. No correctness concerns. _ArrowStreamBuffer falls back cleanly to the heap when mmap creation, growth or shrinking fails. The pyarrow.py_buffer export keeps the map alive for as long as the table lives. The unit tests cover the growth, shrink, fallback, platform and lifetime paths.

Other findings

  • ⚪ Nit — test_non_posix_platform_stays_on_the_heap only reaches the hasattr(mmap, "MAP_PRIVATE") guard on Linux. On other platforms _INLINE_ARROW_USE_MMAP already short-circuits first, so the test passes without exercising that guard. Adding @linux_only or renaming the test would make its intent clearer.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@peco-review-bot
peco-review-bot Bot had a problem deploying to azure-prod November 12, 2026 13:12 Error
@peco-review-bot
peco-review-bot Bot temporarily deployed to azure-prod November 12, 2026 13:12 Inactive
@vuanhphung Isaac
test(thrift): mark the no-MAP_PRIVATE inline test Linux-only
e397064
It only reaches the MAP_PRIVATE guard where the map path is enabled.

Signed-off-by: Vu Anh Phung <[email protected]>
Co-authored-by: Isaac <[email protected]>
@vuanhphung

vuanhphung commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator Author

Round-8 nit, done in the latest commit: test_non_posix_platform_stays_on_the_heap is now @linux_only, since it only reaches the MAP_PRIVATE guard where the map path is enabled.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 13:12 — with GitHub Actions Inactive
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 13:12 — with GitHub Actions Inactive
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 13:13 — with GitHub Actions Error
peco-review-bot[bot]
peco-review-bot Bot approved these changes Nov 12, 2026
View reviewed changes

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good, with one low-severity note: the 1 MiB mmap floor is checked against the 4x-inflated size estimate, so the comment's VMA-count bound is about 4x too generous. The mmap and mremap handling, the heap fallbacks and the lifetime of the buffer handed to pyarrow all look correct, and the unit tests cover each branch well.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/databricks/sql/utils.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@peco-review-bot
peco-review-bot Bot temporarily deployed to azure-prod November 12, 2026 13:16 Inactive
@vuanhphung Isaac
fix(thrift): apply the inline mmap floor to the compressed size
f31e911
The 1 MiB floor was compared with the 4x size estimate, so row sets
with as little as ~256 KiB of compressed data still got their own map.
Compare it with the batches' total size, a lower bound on the stream
size, so every map really is at least ~1 MiB and the VMA bound in the
comment holds.

Signed-off-by: Vu Anh Phung <[email protected]>
Co-authored-by: Isaac <[email protected]>
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 13:18 — with GitHub Actions Inactive
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 13:18 — with GitHub Actions Error
@vuanhphung
vuanhphung had a problem deploying to azure-prod November 12, 2026 13:18 — with GitHub Actions Error
@peco-review-bot
peco-review-bot Bot temporarily deployed to azure-prod November 12, 2026 13:20 Inactive
peco-review-bot[bot]
peco-review-bot Bot approved these changes Nov 12, 2026
View reviewed changes

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Verdict: 1 Low

Looks good, with one low concern. The mmap helper keeps the old bytearray path when map creation, growth or shrinking fails. Private-map flags, lifetime (the map is held by the pyarrow buffer) and the decompression/IPC behavior are correct, and the tests cover each fallback branch. The gap: highly compressible row sets can skip the map path because the 1 MiB floor is checked against the compressed size.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Comment thread src/databricks/sql/utils.py Resolved

Uh oh!

There was an error while loading. Please reload this page.

@peco-review-bot
peco-review-bot Bot temporarily deployed to azure-prod November 12, 2026 13:21 Inactive
@vuanhphung Isaac
fix(thrift): move compressible inline Arrow streams to a map past the…
2e7600a
… floor

Row sets that compress very well could be under 1 MiB on the wire but
much larger decompressed, so they stayed on the heap and kept the
retention. Start such streams on the heap and move them to a map once
the bytes written reach the floor (copying the < 1 MiB written so far).
Every map still holds at least 1 MiB of stream data.

Signed-off-by: Vu Anh Phung <[email protected]>
Co-authored-by: Isaac <[email protected]>
@vuanhphung
vuanhphung temporarily deployed to azure-prod November 12, 2026 13:25 — with GitHub Actions Inactive
peco-review-bot[bot]
peco-review-bot Bot approved these changes Nov 12, 2026
View reviewed changes

@peco-review-bot peco-review-bot Bot left a comment

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

✅ No issues identified by the review bot.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@vuanhphung vuanhphung mentioned this pull request Nov 12, 2026
perf(thrift): decode inline Arrow results in one call, into memory returned on free #1037
Open
4 tasks done
@vuanhphung

vuanhphung commented Nov 12, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #1037, which now contains this PR's memory-map target (commit 8636c7f on top of #1037's single-stream LZ4 decode). The two had to land together: decoding through pyarrow alone made large inline results worse than main (1.7 GB fetchall_arrow: peak 2909 vs 1810 MB, 2859 vs 1637 MB resident after free). Combined, live with use_cloud_fetch=False: 1.7 GB peak 1810 -> 1758 MB and after free 1637 -> 26 MB vs main, with fetch time unchanged. At 64 threads on a 1,000-row inline result: 528 -> 1109 statements/s. Details and the full tables are in #1037's description.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

This branch was previously deployed

1 inactive deployment
azure-prod — 2e7600ad Deployed Nov 12, 2026 by peco-review-bot[bot] via followup #2028
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

@peco-review-bot peco-review-bot[bot] peco-review-bot[bot] approved these changes

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

1 participant

@vuanhphung
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.