Skip to content

feat: add X-LaunchDarkly-Instance-Id header (SDK-2356)#162

Merged
keelerm84 merged 3 commits into
mainfrom
mk/sdk-2356-instance-id
May 27, 2026
Merged

feat: add X-LaunchDarkly-Instance-Id header (SDK-2356)#162
keelerm84 merged 3 commits into
mainfrom
mk/sdk-2356-instance-id

Conversation

@keelerm84
Copy link
Copy Markdown
Member

@keelerm84 keelerm84 commented May 12, 2026

Summary

Adds the X-LaunchDarkly-Instance-Id header to every outbound polling, streaming, and event request. Value is a v4 UUID generated once in HttpConfigurationBuilderImpl.build() (via UUID.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 HttpConfiguration to 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

  • `./gradlew test --rerun-tasks` — BUILD SUCCESSFUL, full server SDK suite green
  • `./gradlew build -x test` — BUILD SUCCESSFUL (checkstyle, javadoc, shadowJar clean)
  • `make build-contract-tests` — BUILD SUCCESSFUL with `instance-id` capability registered
  • CI green (incl. contract tests across the matrix)

Note

Low Risk
Outbound HTTP header and context API extension for server-connection-minutes; no auth or data-path changes, covered by unit and contract tests.

Overview
Adds a per-LDClient v4 UUID on ClientContext (getInstanceId()), generated once in ClientContextImpl.fromConfig and reused across logging/HTTP bootstrap contexts so polling, streaming, and events share one stable id.

HttpConfigurationBuilderImpl sets default header X-LaunchDarkly-Instance-Id from that context value (before custom headers, which may override). Fixes new ClientContext(String) so default HTTP headers and getInstanceId() match. Contract harness registers capability instance-id. Tests assert UUID presence/stability/uniqueness instead of exact header maps where values differ per build.

Reviewed by Cursor Bugbot for commit 44af5fd. Bugbot is set up for automated code reviews on this repo. Configure here.

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.
@keelerm84 keelerm84 requested a review from a team as a code owner May 12, 2026 20:59
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.
Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using default mode and found 1 potential issue.

Fix All in Cursor

❌ 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.

…SDK-2356)

Previously the public single-arg `ClientContext(String sdkKey)` test-convenience
constructor produced an internally inconsistent state: `defaultHttp(sdkKey)`
built an HttpConfiguration whose default headers embedded one auto-generated
UUID (via the inner minimalContext), and the outer delegated 8-arg constructor
auto-generated a separate UUID for `this.instanceId`. As a result,
`context.getInstanceId()` and `context.getHttp().getDefaultHeaders()` returned
different values for `X-LaunchDarkly-Instance-Id` -- so consumer code that
read the instance id via the getter would see a different identifier than the
SDK actually placed on outbound requests.

Generate the UUID once in a new private delegating constructor and thread it
both into `defaultHttp(sdkKey, instanceId)` (so the headers carry it) and
into the explicit instanceId argument of the 9-arg constructor (so the
accessor returns the same value).

Added a regression test in ClientContextImplTest that asserts these two
accessors agree.
@keelerm84 keelerm84 merged commit a363777 into main May 27, 2026
21 checks passed
@keelerm84 keelerm84 deleted the mk/sdk-2356-instance-id branch May 27, 2026 20:52
tanderson-ld pushed a commit that referenced this pull request May 28, 2026
🤖 I have created a release *beep* *boop*
---


##
[7.14.0](launchdarkly-java-server-sdk-7.13.4...launchdarkly-java-server-sdk-7.14.0)
(2026-05-28)


### Features

* add X-LaunchDarkly-Instance-Id header (SDK-2356)
([#162](#162))
([a363777](a363777))
* Drop persistent-store cache after FDv2 in-memory store init
([#167](#167))
([90f14f1](90f14f1))


### Bug Fixes

* honor FDv1 fallback directive during initializer phase
([#158](#158))
([b0a3957](b0a3957))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> No runtime code in the diff—only version strings and changelog—so
review risk is limited to verifying the tagged commits match the release
notes.
> 
> **Overview**
> **Release Please** bumps **launchdarkly-java-server-sdk** from
**7.13.4** to **7.14.0** in `.release-please-manifest.json`,
`lib/sdk/server/gradle.properties`, `Version.SDK_VERSION`, and adds the
**7.14.0** section to `CHANGELOG.md`.
> 
> The diff is **version and changelog metadata only**; the listed
product changes ship in the already-merged commits this release tags:
**`X-LaunchDarkly-Instance-Id`** on outbound HTTP, dropping the
persistent-store cache after FDv2 in-memory init, and honoring **FDv1
fallback** during the initializer phase.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
594728e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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.

3 participants