Skip to content

Cache merge bases, work on branches in parallel, and fetch in the background - #99

Merged
matt-edmondson merged 2 commits into
mainfrom
perf/cache-merge-bases
Oct 22, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
perf/cache-merge-bases

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Written by Claude (Claude Code) on behalf of Matt Edmondson.

Problem

Against a Knockout-shaped test repository (36,458 commits, 314 branches), a warm POST /state on the deployed 1.0.42 took 4.5 s for one path and 6.5 s with no paths. The diff cache was hitting. The time went on one git merge-base process per branch, run one after another on every request and never cached, on a mirror with no commit-graph. The first request after RefsTtl also waited on a fetch from the forge.

Changes

  • Merge-base cache. MirrorQueries remembers merge bases by mirror, client base and branch tip, least recently used first out, up to MaxCachedMergeBases (default 20,000). Both ids are immutable, so an entry never needs invalidating. Failures are not cached.
  • Parallel branch work. BranchStateHandler finds merge bases and diffs for up to MaxConcurrentBranchQueries (default 4) branches at once, then assembles the answer in matched order, so the response reads the same as before.
  • Commit-graph. Every clone and fetch is followed by git commit-graph write --reachable --split. A failure is logged and otherwise ignored, because a mirror without a graph answers the same questions more slowly.
  • Background fetch. Refs between RefsTtl and RefsTtl + RefsServeStaleFor (default 30 s) are answered from disk while one fetch runs in the background under the caller's already-admitted credential. The window is capped where it would pass AdmissionTtl, so answered refs are never older than the existing rule allows. It is capped rather than refused by the validator, so a deployment that already runs RefsTtl close to AdmissionTtl keeps starting. Setting it to zero restores the old behavior. refsAsOf still reports the true age.

README, deploy/k8s/configmap.yaml and the tool's appsettings.json list the new settings.

Measurements

A local run of this branch on Windows against the test repository, where starting a process costs more than on the Linux pod:

Request Deployed 1.0.42 This branch
Warm, one path 4.5 s 0.17 s
Warm, no paths (about 2,000 changed paths returned) 6.5 s 0.55 to 0.8 s
40 s after the last fetch waits on a fetch 0.4 s, fetch runs behind it
70 s after the last fetch waits on a fetch 0.77 s, including the fetch and the admission probe

Tests

  • 275 passed, 5 skipped (Linux and macOS only).
  • New tests cover:
    • the merge-base cache: hit, miss on a different tip or mirror, no caching of failures, and eviction
    • the parallel branch work and the order of the answer
    • commit-graph writes against a real repository after a clone and after a fetch, and a failed write still answering
    • the background fetch in the request flow and against a real repository
    • validation of the new settings

🤖 Generated with Claude Code

…kground [minor]

A state request naming a few hundred branches ran one git merge-base per branch, one after another,
on every request. Merge bases are now cached by mirror, client base and branch tip, uncached
branches are worked on several at a time, and each clone and fetch writes a commit-graph so an
uncached merge base stays quick on a long history. Refs a little past RefsTtl are answered from
while one fetch runs in the background, capped so they are never older than AdmissionTtl.

New settings: RefsServeStaleFor (30 seconds), MaxCachedMergeBases (20,000) and
MaxConcurrentBranchQueries (4).
Comment thread GitBranchStateCache/Mirrors/MirrorQueries.cs Fixed
@sonarqubecloud

Copy link
Copy Markdown

@matt-edmondson
matt-edmondson merged commit 2f13782 into main Oct 22, 2026
15 checks passed
@matt-edmondson
matt-edmondson deleted the perf/cache-merge-bases branch October 22, 2026 12:25
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