Skip to content

Cache parsed map tiles on disk, and make the map's cost an operator setting #133

Description

@CaYatur

Measured, not guessed

One region of a real Paper 1.21.6 world (r.0.-1.mca, 5.4 MB, 811 generated
chunks, 19 769 sections):

total 180ms | decompress 39ms | nbt parse 141ms

...and that is before the surface extraction on top. The NBT parse is 78% of
it, and all of it is repeated every time the app starts, because #119's
cache is a Map in memory holding twelve regions.

A tile set for that region is about 1 MB raw and gzips to a fraction of it in
2–3 ms, with gunzip at ~1 ms. So the whole cost of a region can go from a few
hundred milliseconds to single-digit milliseconds — but only if the parse
survives a restart.

The disk cache was in #119's scope ("a tile cache on disk, keyed by region file
path + mtime") and I only built the in-memory half. This is the other half.

Scope

Cache parsed tiles on disk, keyed by the region path and its mtime, so a
region is parsed once ever and re-parsed only when the server rewrites it. On by
default — this is the setting that makes the map usable on a big world.

The cache key must carry a format version as well as the mtime. Without it,
changing the colour table or the foliage rule leaves every existing cache
serving the old picture forever, and the operator has no way to know why their
map looks wrong.

Per-server settings, because a 200-server box and a laptop want different
answers:

  • Cache tiles on disk — default on.
  • Regions kept in memory — the working set; more is faster and heavier.
  • Delay between region parses — the brake that keeps the main thread
    responsive; lower is faster and less polite to everything else.
  • Cache size limit — with the oldest evicted, and a way to clear it.

Verify

The codec is pure and round-trips: colours, heights, the transparent columns
that must stay transparent, and structure marks. A cache written by an older
format version is refused rather than decoded into the wrong picture. A
truncated or corrupt file is refused rather than throwing. And the settings are
clamped — a parse gap of zero or a memory limit of a million is a way to hang
the process, and both arrive from a config file an operator can hand-edit.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions