---
summary: "Streams the `setup()` CSV parse one row at a time into a reused cell buffer and loads the six source files one at a time — cutting the setup-time RSS peak ~40% (~871 → ~525 MiB) and lowering startup OOM risk on memory-constrained (sub-1 GiB) deployments, with steady-state footprint unchanged (#9)."
breaking: false
security: false
---

# 0.2.1 — 2026-07-18

Streaming the `setup()` CSV parse cuts the transient RSS peak, lowering startup OOM risk on memory-constrained deployments. Parse output is byte-for-byte identical — the win is memory, not behavior.

## Fixed

- **Setup-time memory peak** — `setup()` streams the CSV parse instead of materializing the whole parsed corpus first. A new `forEachRow` visitor feeds each row to the index builders through a single reused cell buffer, replacing the `parseCsv` array-of-records parse that held every row of all six files live at once; on the ~85K-row airports file the transient parse garbage drops from the full row array to a single row. ([#9](https://github.com/cyanheads/ourairports-mcp-server/issues/9))
- **One file at a time** — `load()` reads, parses, and drops each of the six source CSVs sequentially rather than `Promise.all`-buffering all six raw strings at once, so the resident high-water mark is one file's parse.
- **Prompt reclaim** — a guarded `Bun.gc(true)` at the end of `load()` returns the transient parse pages to the OS immediately rather than lazily; a no-op under Node and other runtimes, where the streaming parser is the portable win.
- **Measured impact** — setup-time RSS peak down ~40% (~871 → ~525 MiB on the test machine), so the server starts comfortably within a 1 GiB container. Steady-state/idle footprint is unchanged (~55 MiB) — the win is the transient peak during `setup()`, not the resident index.
