Propagate correlation ID from Geo secondary to primary site

What does this MR do and why?

This MR adds correlation ID propagation to Geo requests from secondary to primary sites. When a request originates on a Geo secondary (e.g., git clone via HTTP, web UI requests), the same correlation ID is now forwarded to the primary site, enabling end-to-end request tracing across Geo sites.

Changes

  • Workhorse: Added WithCorrelationID() proxy option that forwards the correlation ID via X-Request-ID header when proxying requests to the primary.
  • Rails BaseRequest: Added X-Request-ID header to Geo request headers, ensuring all Geo::RequestService requests include the correlation ID.
  • Rails git_http_controller: Updated to read correlation ID from incoming X-Request-ID header for Geo-proxied requests.

Why

Currently, when troubleshooting issues on Geo setups, it's difficult to correlate logs between secondary and primary sites because each site generates its own correlation ID. This change enables operators to trace a single request across both sites using the same correlation ID.

References

Related to #513034 (closed)

Example

Note that the correlation ID set remains the same throughout the sites.

# Fetch the secondary with a custom Request-ID
curl -v -H "X-Request-ID: my-custom-corr-id-789" "http://127.0.0.1:3001/root/test"

# Check secondary for custom correlation ID
echo "=== Secondary Workhorse ===" && grep "my-custom-corr-id-789" ~/code/gdk2/log/gitlab-workhorse/current 2>/dev/null | tail -3
=== Secondary Workhorse ===
2026-01-22_18:37:42.39960 gitlab-workhorse      : {"backend_id":"geo_primary_site","body_limit":104857600,"content_type":"text/html;
charset=utf-8","correlation_id":"my-custom-corr-id-789","duration_ms":27,"host":"127.0.0.1:3001","level":"info","method":"GET","msg":
"access","proto":"HTTP/1.1","read_bytes":123,"referrer":"","remote_addr":"127.0.0.1:60887","remote_ip":"127.0.0.1","route":"",
"route_id":"proxy","status":302,"system":"http","time":"2026-01-22T13:37:42-05:00","ttfb_ms":27,"uri":"/root/test","user_agent":"curl/
8.7.1","written_bytes":101}

# Check primary for custom correlation ID
echo "=== Primary Workhorse ===" && grep "my-custom-corr-id-789" ~/code/gdk1/log/gitlab-workhorse/current 2>/dev/null | tail -3
=== Primary Workhorse ===
2026-01-22_18:37:42.39865 gitlab-workhorse        : {"backend_id":"rails","body_limit":104857600,"content_type":"text/html;
charset=utf-8","correlation_id":"my-custom-corr-id-789","duration_ms":25,"host":"127.0.0.1:3333","level":"info","method":"GET","msg":
"access","proto":"HTTP/1.1","read_bytes":716,"referrer":"","remote_addr":"127.0.0.1:60877","remote_ip":"127.0.0.1","route":"",
"route_id":"default","status":302,"system":"http","time":"2026-01-22T13:37:42-05:00","ttfb_ms":25,"uri":"/root/test","user_agent":
"curl/8.7.1","written_bytes":101}

# Check primary Rails logs
echo "=== Primary Rails ===" && grep "my-custom-corr-id-789" ~/code/gdk1/gitlab/log/*.log 2>/dev/null | head -3
=== Primary Rails ===
{"method":"GET","path":"/root/test","format":"*/*","controller":"ProjectsController","action":"show","status":302,"location":"http://127.0.0.1:3000/users/sign_in","time":"2026-01-22T18:37:42.397Z",
"params":[{"key":"namespace_id","value":"root"},{"key":"id","value":"test"}],"correlation_id":"my-custom-corr-id-789","meta.
caller_id":"ProjectsController#show","meta.feature_category":"groups_and_projects","meta.organization_id":1,"meta.remote_ip":"127.0.0.
1","meta.http_router_rule_action":"proxy","meta.client_id":"ip/127.0.0.1","remote_ip":"127.0.0.1","ua":"curl/8.7.1",

How to set up and validate locally

The primary site's Workhorse must be configured to accept incoming correlation IDs. This requires the -propagateCorrelationID flag.

For GDK (manual workaround - gets reset on gdk reconfigure):

# On the primary GDK
sv stop primary_gdk/services/gitlab-workhorse sed -i '' 's/-apiCiLongPollingDuration "0s"$/-apiCiLongPollingDuration "0s" -propagateCorrelationID/' primary_gdkservices/gitlab-workhorse/run sv start primary_gdk/services/gitlab-workhorse 

For Omnibus (production):

# In /etc/gitlab/gitlab.rb on the primary 
gitlab_workhorse['propagate_correlation_id'] = true
   gitlab_workhorse['trusted_cidrs_for_propagation'] = %w(<secondary-site-ip>/32) 

Then run gitlab-ctl reconfigure.

For Helm charts:

gitlab:
 webservice:
   workhorse:
     extraArgs: "-propagateCorrelationID"
     trustedCIDRsForPropagation: ["<secondary-site-ip>/32"]

Validation steps

  1. Rebuild Workhorse on the secondary: cd gitlab/workhorse && make gdk restart gitlab-workhorse
  2. Perform a git clone from the secondary: git clone http://127.0.0.1:3001/root/test.git
  3. Check the secondary Workhorse logs for the correlation ID: tail -10 ~gitlab-workhorse/current | grep "info_refs" Note the correlation_id value (e.g., 01KFED3JBW2MJERK4HEEHWDD79)
  4. Verify the same correlation ID appears on the primary: grep "01KFED3JBW2MJERK4HEEHWDD79" ~log/gitlab-workhorse/current
  5. For web UI requests, navigate to a project page on the secondary (e.g., http://127.0.0.1:3001/root/test) and verify matching correlation IDs in:
    1. Secondary Workhorse: log/gitlab-workhorse/current
    2. Primary Workhorse: log/gitlab-workhorse/current
    3. Primary Rails: log/graphql_json.log

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.

Edited by Victor Prêté

Merge request reports

Loading