Route protocol v2 bundle-uri requests to a dedicated Gitaly RPC

What does this MR do and why?

TLDR: Calls a different RPC AdvertiseBundleURI when command=bundle-uri instead of PostUploadPackWithSidechannel so that bundle-uri requests don't get stuck behind slower requests when Gitaly is under load.

Details

Gitaly's concurrency limits are keyed by RPC name. A Git protocol v2 client that supports bundle URIs sends command=bundle-uri as its own --stateless-rpc request before the real command=fetch, so it reaches Workhorse as a separate POST /git-upload-pack. Serving it through PostUploadPackWithSidechannel makes that cheap request share limits and queue with full clones: it can queue behind them, wait out max_queue_wait, or be dropped with ResourceExhausted.

That inverts the point of bundle URIs. When the bundle-uri step fails, the client falls back to a full server-side clone, so the load lands on a node that was already saturated.

Serving bundle-uri only makes git-upload-pack write back the bundle list from the repository config and exit. This MR makes Workhorse peek at the pktlines at the front of the request body and, when it finds command=bundle-uri, call Gitaly's dedicated AdvertiseBundleURI RPC instead. That gives the command its own concurrency-limit entry, its own gitaly_concurrency_limiting_* metrics and its own timeouts. Peeking does not consume the body, so the request still streams to Gitaly whole.

Changes:

  • workhorse/internal/git/upload-pack.go — the routing decision, the pktline scan (isBundleURIRequest), and handleAdvertiseBundleURIWithGitaly.
  • workhorse/internal/gitaly/smarthttp.go — a SmartHTTPClient.BundleURI wrapper that invokes the RPC over a sidechannel, plus a copyOverSidechannel helper now shared with the existing UploadPack wrapper.
  • config/feature_flags/undefined/gitaly_bundle_uri_dedicated_rpc.yml — the flag definition.

This is the first place Workhorse branches on a gitaly-feature-* value rather than just forwarding it to Gitaly. Rails forwards every persisted gitaly_-prefixed flag into GitalyServer.call_metadata (see Feature::Gitaly.server_feature_flags), so Workhorse reads gitaly-feature-bundle-uri-dedicated-rpc with no new Rails plumbing, and one flag gates both the routing and the Gitaly-side behaviour. The alternative was a dedicated api.Response field set in Gitlab::Workhorse.git_http_ok, matching the existing ShowAllRefs and NeedAudit fields. The CallMetadata route was chosen deliberately, to avoid a second flag; this is a decision, not an accident.

MR acceptance checklist

Evaluate this MR against the MR acceptance checklist. It helps you analyze changes to reduce risks in quality, performance, reliability, security, and maintainability.

No changelog entry: the change is entirely behind a feature flag that is off by default. No database or migration changes. No user-facing strings.

🤖 Generated with Claude Code

Edited by John Cai

Merge request reports

Loading
Loading