osm lance: build the dataset over the slab mapping, then evict it - #132
Conversation
RAM is Railway's dominant line item ($10/GB-month against $0.15 for volume and $0.015 for bucket), and the conversion was the worst offender in the process. **The allocation.** `write_lance` took an owned `Vec<u8>` and `ensure_lance_local` filled it with `std::fs::read(slab_path)` — the WHOLE slab on the heap. 1.4 GB for Berlin, 3.75 GB for Brandenburg. Anonymous memory, so no amount of pressure can reclaim it, and it sat on top of an mmap of the same file taken 80 lines earlier for the digest. That is how 1.4 GB of data became a multi-gigabyte plateau on the memory graph. Arrow never needed to own it. The mapping is now kept (as an `Arc`) and adopted directly by `Buffer::from_custom_allocation`, with the `Arc` handed over as the allocation's owner so the pages cannot be unmapped under Arrow. The existing pointer-identity assertion still guards the zero-copy claim; it now proves the buffer addresses the MAPPING. **The eviction.** Once the dataset is on disk the slab's pages have done their job, and left alone they simply stay: a 24 GB limit over a 1.4 GB file means nothing ever creates the pressure that would reclaim them. `release_after_write` advises `MADV_DONTNEED` — unix-gated, a no-op elsewhere per the cross-platform rule — but only when it holds the last reference. A surviving reference means something downstream is still reading those pages, so it logs and leaves them alone rather than yanking memory out from under a live reader. What the advice does and does not promise is in the doc comment: it stops this process holding the pages, so they can be reclaimed. It does not guarantee they leave the page cache the instant it returns. The test is two-sided on exactly the property that matters: a mapping something else still holds must NOT be released, and a sole holder MUST be. A `release_after_write` that always answered "no" — Lance still holding the buffer, say — would be completely inert and would look identical from outside; that is the case this test exists to catch. 15/15 osm_lance tests, 53/53 across the osm modules, `cargo check` clean. NOT measured: the actual resident-set change on the deploy. The mechanism is right and the numbers above are arithmetic, not observation.
|
Important Review available on request
Reviews should be triggered manually for repositories with fewer than 10 stars. Select Trigger review above or comment ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_b172b134-00bc-4fb6-a1ea-6b6f14a44fdb) |
bake → lance → evict the bake from RAM immediately. One file.
RAM is the dominant line item — $10/GB-month against $0.15 for volume and $0.015 for bucket, so a GB held in memory costs 667× a GB in the bucket. The conversion was the worst offender in the process.
The allocation
write_lancetook an ownedVec<u8>, andensure_lance_localfilled it with:1.4 GB for Berlin, 3.75 GB for Brandenburg. Anonymous memory — no amount of pressure can reclaim it — sitting on top of an mmap of the same file taken 80 lines earlier for the digest. That is how 1.4 GB of data becomes a multi-gigabyte plateau on the memory graph while CPU reads 0.
Arrow never needed to own it. The mapping is now kept as an
Arcand adopted directly:The pre-existing pointer-identity assertion still guards the zero-copy claim — it now proves the buffer addresses the mapping.
The eviction
Once the dataset is on disk, the slab's pages have done their job. Left alone they simply stay: a 24 GB limit over a 1.4 GB file means nothing ever creates the pressure that would reclaim them, so the conversion's footprint stays in the bill until the process exits.
release_after_writeadvisesMADV_DONTNEED—#[cfg(unix)], a documented no-op elsewhere per the cross-platform rule — but only when it holds the last reference. A surviving reference means something downstream is still reading those pages; it logs and leaves them alone rather than yanking memory from under a live reader.The doc comment is explicit about the limit of the guarantee: the advice stops this process holding the pages so they can be reclaimed. It does not promise they leave the page cache the instant it returns.
The test is two-sided on the property that matters
A
release_after_writethat always answered "no" — Lance still holding the buffer, say — would be completely inert and look identical from outside. So the test asserts both directions: a mapping something else still holds must NOT be released, and a sole holder MUST be.Gates
15/15
osm_lancetests, 53/53 across the osm modules,cargo check -p cockpit-serverclean. Scoped to that crate — this touches one leaf binary and nothing depends on it.Not measured: the actual resident-set change on the deploy. The mechanism is right and the figures above are arithmetic, not observation. The honest next number is what the container is charged for while serving tiles after this lands.
Not in this PR
The steady-state ~2 GB baseline is the serving mmap, not this path — it needs either reclaim pressure (lowering the memory limit, which costs nothing and needs no deploy) or the columnar reader through lance-graph. Separate changes.
🤖 Generated with Claude Code
https://claude.ai/code/session_01NMeiLmtDKhomJNSo2ecbJw
Generated by Claude Code