feat: add X-LaunchDarkly-Instance-Id header (SDK-2356)#162
Conversation
Generate a v4 UUID once per SDK instance in HttpConfigurationBuilderImpl.build and stamp it on the default headers map. Because the default headers are shared by the stream, poll, and event paths, every outbound request carries the same stable per-instance identifier without per-channel plumbing. Registers the "instance-id" capability with the contract test service so the cross-SDK harness can verify the header on stream, poll, and event requests. Updates header-comparison tests that constructed a fresh HttpConfiguration side-by-side with the one under test; the instance ID is per-build so those tests now compare other headers exactly and only assert presence of the instance ID header.
Generating X-LaunchDarkly-Instance-Id inside HttpConfigurationBuilderImpl.build() tied the value to a specific subsystem builder. Any code path that goes through HttpConfiguration would see a value, but other subsystems built from ClientContext (which is the canonical per-LDClient state holder) had no way to read the same id. Add an instanceId field, getter, and a nine-argument constructor variant to ClientContext. The eight-argument constructor auto-generates a v4 UUID for any caller that does not need to pass an explicit value; the copy constructor propagates the existing value, so derived ClientContext instances stay in sync. ClientContextImpl.fromConfig now generates the UUID once and threads it through each of the three ClientContext variants it constructs during LDClient init. HttpConfigurationBuilderImpl reads from ClientContext.getInstanceId() instead of generating its own. Existing tests in DefaultFeatureRequestorTest / LDConfigTest / StreamProcessorTest that already accounted for per-build randomness continue to work because each `clientContext(...)` they construct gets its own auto-generated id. Update HttpConfigurationBuilderTest to assert the new contract: the builder mirrors whatever id the context provides; two distinct ClientContexts produce distinct ids; one context produces a stable id across multiple builds. Verified locally with `./gradlew test` against lib/sdk/server: all 17 of 17 test classes pass, including 15 of 15 HttpConfigurationBuilderTest cases.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default mode and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 30cf64b. Configure here.
| this(copyFrom.sdkKey, copyFrom.applicationInfo, copyFrom.http, copyFrom.logging, | ||
| copyFrom.offline, copyFrom.serviceEndpoints, copyFrom.threadPriority, copyFrom.wrapperInfo); | ||
| copyFrom.offline, copyFrom.serviceEndpoints, copyFrom.threadPriority, copyFrom.wrapperInfo, | ||
| copyFrom.instanceId); |
There was a problem hiding this comment.
Test constructor produces inconsistent instance ID state
Low Severity
The single-arg ClientContext(String sdkKey) constructor calls defaultHttp(sdkKey), which internally creates a temporary ClientContext (auto-generating UUID-A) and builds an HttpConfiguration embedding UUID-A in headers. Then the main constructor delegates to the 8-arg version, which auto-generates a different UUID-B for this.instanceId. This means getInstanceId() returns UUID-B while getHttp().getDefaultHeaders()["X-LaunchDarkly-Instance-Id"] contains UUID-A — an internal inconsistency that could confuse future test authors.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 30cf64b. Configure here.


Summary
Adds the
X-LaunchDarkly-Instance-Idheader to every outbound polling, streaming, and event request. Value is a v4 UUID generated once inHttpConfigurationBuilderImpl.build()(viaUUID.randomUUID()) and applied before user-supplied custom headers, matching the User-Agent / Authorization override convention.Three pre-existing tests outside the http-config test class were rebuilding
HttpConfigurationto derive an "expected" header set for exact-equality comparisons against outbound requests; with per-build UUIDs those assertions were updated to check presence rather than exact equality.Test plan
Note
Medium Risk
Touches core request header construction and expands
ClientContextAPI, which could affect all outbound polling/streaming/events requests and any custom integrations relying on context constructors/equals semantics.Overview
Adds SDK instance identification on requests. The SDK now generates a per-
LDClientUUID and threads it throughClientContext, soComponents.httpConfiguration()includesX-LaunchDarkly-Instance-Idon outbound requests (while still allowing custom headers to override defaults).Plumbs the ID through construction and tests.
ClientContextImpl.fromConfigensures the same instance ID is reused across the staged context builds during client initialization, contract-test capabilities advertiseinstance-id, and several tests were adjusted to assert presence of the header rather than exact header-map equality;HttpConfigurationBuilderTestadds coverage for UUID v4 format, stability within a context, and uniqueness across contexts.Reviewed by Cursor Bugbot for commit 30cf64b. Bugbot is set up for automated code reviews on this repo. Configure here.