Skip to content

perf(registry): index snapshot entries for GetEntry - #1052

Merged
wolfy-j merged 2 commits into
mainfrom
perf/registry-snapshot-get
Nov 6, 2026
Merged

wolfy-j merged 2 commits into
mainfrom
perf/registry-snapshot-get

Conversation

@wolfy-j

@wolfy-j wolfy-j commented Nov 6, 2026

Copy link
Copy Markdown
Contributor

A Lua registry snapshot answers get by scanning every entry and canonicalizing both IDs per comparison, so a snapshot read costs time proportional to the whole registry. Under real application load this scan was 47% of CPU samples during bursts.

  • GetEntry uses a lazy index on canonical (namespace, name), built once per snapshot. The first duplicate still wins, and the not-found error is unchanged. The key is not the joined string, since ID.Equal treats ("a","b:c") and ("a:b","c") as different.
  • ID.Canonical no longer allocates for already-canonical IDs over 32 bytes, as it documents.
7,000 entries Before After
get, last entry 86.1 us 0.04 us
get, missing 80.1 us 0.24 us
fresh snapshot + 1 get 40.7 us 497 us
fresh snapshot + 25 gets 1,049 us 569 us
fresh snapshot + 100 gets 3,758 us 690 us

The index belongs to each snapshot, so a snapshot with fewer than about 15 gets costs more than before. Moving the index to the published registry state would remove that cost; it is left for a separate change.

Tests compare results against the old scan across canonical, name-only, duplicate, colliding, empty and missing IDs, the Lua path, and concurrent readers under -race.

Snapshot.GetEntry scanned every entry and canonicalized both IDs per
comparison, so each Lua snapshot:get cost O(entries) on a large registry.

Build a map of canonical ID parts to the first entry position on first
lookup, once per snapshot under sync.Once. Snapshots that only list, find or
plan changes never pay for it. Results match ID.Equal: canonical parts are the
key, the earliest duplicate wins, and the not-found error is unchanged.
Canonical joined namespace and name to compare against the cached string, and
the join allocates once it exceeds the compiler's 32-byte stack buffer. Compare
the cached string in place so already canonical IDs never allocate, as the
method documents. This also removes one allocation per entry from snapshot
index builds and one per registry lookup.
@wolfy-j
wolfy-j merged commit 5275064 into main Nov 6, 2026
39 of 40 checks passed
@wolfy-j
wolfy-j deleted the perf/registry-snapshot-get branch November 6, 2026 19:36
@wolfy-j
wolfy-j requested a review from skhaz November 6, 2026 21:45
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