Skip to content

Share one LMDB environment per shard per process - #23746

Merged
jgranstrom merged 1 commit into
mainfrom
jg/lmdb-shared-env
Nov 5, 2026
Merged

jgranstrom merged 1 commit into
mainfrom
jg/lmdb-shared-env

Conversation

@jgranstrom

Copy link
Copy Markdown
Contributor

A pants process that opened the local store more than once and dropped one copy lost its LMDB locks while the other copy kept writing. So another process could write the same shard at the same time and corrupt it on disk.

--no-pantsd with a bootstrap scheduler and test processes with one RuleRunner per test both did this.

LMDB says to not open the store more than once in the same process for this reason

With this PR:

  • Stores in one process now share each shard's LMDB environment through a registry keyed by the
    lock file's device and inode. An environment is closed once no store holds it.
  • A process forked from the one that owns the registry cannot open a store. Nothing does this today.
  • A shared environment keeps its map size, so opening the store at a larger size in the same
    process is an error, and local_store_shard_count and the local_store_*_max_size_bytes
    options now restart pantsd. This is needed because the new scheduler is built before the old is released, and currently the old scheduler is also leaked anyways. So it's a larger change to first close it to open it with a new shard count or similar.

Tests added to cover the new path, and one of the tests fail on main by dropping one of the stores leaving the other without locks. Also tested end-to-end on MacOS with a debug-engine where a process that dropped a second store, writing beside a process with one RuleRunner per test, damaged a shard within seconds on main and ran 60 s without damage with this change.

Since #23720, each process that opens the store runs mdb_reader_check, which frees the reader
slots of any process that holds no liveness lock, so a process that lost its locks also loses the
protection of its open read transactions. This change removes the lock loss.

Disclosure: Implemented and verified with Claude with human direction

@jgranstrom
jgranstrom marked this pull request as ready for review November 2, 2026 16:51
@jgranstrom
jgranstrom requested review from benjyw, tdyas and tobni November 2, 2026 16:51

@benjyw benjyw left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

LMDB forbids opening an environment twice in one process (lmdb.h, Caveats). A second open resets
the shard's writer lock under the first when no other process has the shard open, and closing
either copy drops the process's fcntl locks on lock.mdb, so the next opener resets it. Either way,
two writers can then write the shard at once and damage it. A `--no-pantsd` run that garbage
collects its bootstrap scheduler, and a test process with one RuleRunner per test, both did this.

Stores now share each shard's environment through a process-wide registry keyed by the lock
file's device and inode, and an environment is closed once no store holds it. A process forked
from the one that owns the registry cannot open a store.

A shared environment keeps its map size, and requesting a larger one is an error, so the options
that size the store are now daemon options: changing one restarts pantsd.
@jgranstrom
jgranstrom merged commit 21913fe into main Nov 5, 2026
28 checks passed
@jgranstrom
jgranstrom deleted the jg/lmdb-shared-env branch November 5, 2026 19:20
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.

2 participants