Skip to content

zu2: map_settled makes one VMA per log page, so the mapping count grows with the database #768

Description

@tamnd

Log::remap_settled maps each settled 4 MiB page with its own mmap and a null address hint, so the kernel picks an address for each one. Nothing ever unmaps a page above the compaction floor: evict_behind steps over a mapped victim by design since #759, and the only unmap paths are reclaim_to, which clears the slots under a punched range, and the drop.

So with map_settled on, the number of mappings the process holds rises with the size of the log and never comes down.

Measured on server1 rather than argued from the manual page. A small C program that maps a file a page at a time the way remap_settled does, counting the lines of /proc/self/maps either side:

64 pages mapped one at a time: 23 map lines before, 87 after, 64 added
512 pages mapped one at a time: 23 map lines before, 535 after, 512 added

One VMA a page, no merging. The merge does not happen because the kernel hands out addresses top down while the log maps ascending file offsets, so the addresses run the opposite way to the offsets and two neighbours in the file are never neighbours in the address space.

Two costs, and the second is the one that matters:

The fix is to reserve the span once and put the pages into it, so the addresses run the way the offsets do. Measured the same way:

512 pages into a reservation: 23 before, 24 reserved, 24 filled, 535 with every other one dropped

One anonymous PROT_NONE MAP_NORESERVE reservation, then each page mmaped over it with MAP_FIXED, and 512 mappings collapse into one VMA. MAP_NORESERVE and PROT_NONE so the reservation is address space and not memory, which is free on a 64 bit host.

The last number in that line is the honest caveat and it is why this needs care rather than a patch. Drop every other page back to PROT_NONE and the single VMA splits into 512 again, so the collapse holds only while the mapped region is a contiguous run. That happens to be what remap_settled produces today, since remap_from is a monotonic cursor and eviction steps over mapped pages rather than taking them, but it is a property of the current policy rather than of the design, and anything that starts unmapping in the middle gets the old behaviour back without a test noticing.

So this issue is three pieces of work:

  • Reserve max_pages of address space at open when map_settled is on, and map into it with MAP_FIXED.
  • A test that asserts the mapping count, which on Linux means reading /proc/self/maps and on macOS means skipping. It is the only way this stays fixed.
  • Decide what a page below the mapped run means. reclaim_to already drops slots under the punched range, and those are at the bottom of the run rather than in the middle of it, so the run stays contiguous; that wants stating in the code rather than being true by accident.

Windows has no map_read at all, so none of this applies there.

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

    bugSomething isn't workingperfPerformance and resource budgetsstorageStorage engines and file formats

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions