docs(core): document row_group_max_size in-memory buffering for streaming/mobile - #82
Closed
sundapeng wants to merge 1 commit into
Closed
docs(core): document row_group_max_size in-memory buffering for streaming/mobile#82sundapeng wants to merge 1 commit into
sundapeng wants to merge 1 commit into
Conversation
A customer reported apparent "high CPU + memory leak" when writing sensor
data to mosaic on Android (ARMv8-A, Android 16). Root cause is not a leak:
WriterOptions.row_group_max_size defaults to 256 MB (DEFAULT_ROW_GROUP_MAX_SIZE),
so a writer buffers every row of the in-progress row group in memory and flushes
only once 256 MB has accumulated. For a long-lived streaming writer (continuous
ingestion, mobile/embedded sensors) this holds an unbounded amount of data in
RAM — RSS grows ~1:1 with buffered rows until the cap, and a session shorter
than the cap flushes nothing until close(), so the entire session lives in
memory. On a phone this looks and behaves like a leak and risks OOM.
Reproduced on an Android 16 (API 36) emulator with the FFI/C++ writer:
- 256 MB default: RSS 134 -> 330 MB, native heap 15 -> 278 MB, 1 row group
- 8 MB cap: RSS plateaus ~174 MB, native heap ~31 MB, 23 row groups
Both files verify PASS and read back correctly with the `mosaic` CLI.
The 256 MB default is intentional for batch/server wide-table workloads
(large row groups maximize compression ratio and amortize seek cost), so this
does not change the default. It documents the tradeoff and recommends a smaller
row_group_max_size (e.g. 8 MB) for long-lived streaming/mobile writers in:
- core rustdoc (DEFAULT_ROW_GROUP_MAX_SIZE, WriterOptions + field)
- include/mosaic.hpp (C++ WriterOptions inline comment)
- docs/{cpp,java,python}-api.html (table cells + warning callouts)
Co-Authored-By: Claude Code <noreply@anthropic.com>
Member
Author
|
Closing this. On closer review this isn't a defect in mosaic — the 256 MB |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Problem
A customer reported apparent "high CPU + memory leak" when writing sensor data to mosaic on Android (ARMv8-A, Android 16), via the FFI/C++ writer. Process RSS climbed steadily through a long write session and never came back down, eventually risking OOM on the device.
Root cause: buffering, not a leak
This is not a memory leak.
WriterOptions::row_group_max_sizedefaults to 256 MB (DEFAULT_ROW_GROUP_MAX_SIZE). A writer buffers every row of the in-progress row group in memory and flushes (compresses + writes) only once that much data has accumulated:For a long-lived streaming writer — continuous ingestion, mobile/embedded sensors, anything that writes small batches over a long session — this default holds an unbounded amount of data in RAM:
close(), so the entire session lives in memory.On a phone this looks and behaves like a leak and can trigger OOM. The high-CPU observation is the sustained Arrow-FFI import + bucket append, plus one giant zstd burst when the cap (or
close()) is finally hit — an ANR risk if the write runs on the UI thread.Reproduction (Android 16, API 36 emulator, ARMv8-A target)
Two runs of the same workload (3000 batches × 1024 rows = 3,072,000 rows, 10 motion columns +
device_id), one with the 256 MB default, one with an 8 MB cap:Both files read back correctly with the upstream
mosaicCLI (schema/count/cat). The "leak" disappears entirely with a smaller cap: memory is bounded and the native heap stays flat at ~31 MB. macOSleaks(host harness) independently reported 0 leaked bytes — the write/close path frees correctly; the RSS growth is by-design buffering.The 16 KB page-alignment requirement of Android 15+/16 was ruled out (all 4 ABI
.soare0x4000-aligned). The behavior is OS-independent — it is the Rust core's buffering policy.Fix: documentation + recommendation (default unchanged)
The 256 MB default is intentional for batch/server wide-table workloads, where a large row group maximizes compression ratio and amortizes seek cost. Changing the global default would risk regressing that primary use case, so this PR leaves it unchanged and instead documents the tradeoff and recommends a smaller
row_group_max_size(e.g. 8 MB) for long-lived streaming/mobile writers:core/src/spec.rs— rustdoc onDEFAULT_ROW_GROUP_MAX_SIZEcore/src/writer.rs— rustdoc onWriterOptions+ therow_group_max_sizefieldinclude/mosaic.hpp— inline comment on the C++WriterOptions::row_group_max_sizedocs/cpp-api.html,docs/java-api.html,docs/python-api.html— table cells + "Memory & long-lived writers" warning callouts with per-language examplesNo behavior change, no default change — docs only.
Alternatives considered
🤖 Generated with Claude Code