Skip to content

Serve offline map tiles ourselves, not through MapLibre's PMTiles sources - fixes memory leak - #1182

Merged
davecraig merged 1 commit into
Scottish-Tech-Army:mainfrom
davecraig:main
Oct 24, 2026
Merged

davecraig merged 1 commit into
Scottish-Tech-Army:mainfrom
davecraig:main

Conversation

@davecraig

Copy link
Copy Markdown
Contributor

MapLibre Native's PMTilesFileSource never removes an entry from its map of in-flight requests: not when a tile has been answered, and not when the request is cancelled, as it registers no onCancel. So every tile it reads leaves two file requests alive for good, each holding a mailbox whose queue keeps the first 4 KB block it allocated. On the test farm that showed up as the A54's native heap growing by tens of megabytes over two hours, nearly all of it held from mln::Mailbox::push. The same code is built for iOS.

  • The style's offline source is now a soundscape-extract:// URL, served by OfflineExtractTiles, a MapResourceProvider on the default map runtime. It builds the TileJSON from the extract's header and metadata as PMTilesFileSource did, and reads and un-gzips tiles with PmTilesReader. Up to four extracts are kept open, one read at a time each; a file that has changed is opened again, and one that has gone is dropped.
  • PmTilesReader also reads the header's zoom range, bounds and centre, and can return a tile already decompressed.
  • Tiles served this way go into MapLibre's ambient cache, keyed by a URL naming the extract's path, which is now why each download still gets a unique filename. The comments saying so are updated.

Network tiles are unaffected: they don't go through PMTilesFileSource.

MapLibre Native's PMTilesFileSource never removes an entry from its map of
in-flight requests: not when a tile has been answered, and not when the
request is cancelled, as it registers no onCancel. So every tile it reads
leaves two file requests alive for good, each holding a mailbox whose queue
keeps the first 4 KB block it allocated. On the test farm that showed up as
the A54's native heap growing by tens of megabytes over two hours, nearly
all of it held from mln::Mailbox::push. The same code is built for iOS.

- The style's offline source is now a soundscape-extract:// URL, served by
  OfflineExtractTiles, a MapResourceProvider on the default map runtime. It
  builds the TileJSON from the extract's header and metadata as
  PMTilesFileSource did, and reads and un-gzips tiles with PmTilesReader. Up
  to four extracts are kept open, one read at a time each; a file that has
  changed is opened again, and one that has gone is dropped.
- PmTilesReader also reads the header's zoom range, bounds and centre, and
  can return a tile already decompressed.
- Tiles served this way go into MapLibre's ambient cache, keyed by a URL
  naming the extract's path, which is now why each download still gets a
  unique filename. The comments saying so are updated.

Network tiles are unaffected: they don't go through PMTilesFileSource.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@davecraig
davecraig merged commit c3ab2ab into Scottish-Tech-Army:main Oct 24, 2026
5 checks passed

This branch was previously deployed

1 inactive deployment
development — 9e772d2a Deployed Oct 24, 2026 by davecraig via Run instrumentation tests #1633
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant