Skip to content

feat: add X-LaunchDarkly-Instance-Id header (SDK-2359)#170

Merged
keelerm84 merged 6 commits into
mainfrom
mk/sdk-2359-instance-id
May 29, 2026
Merged

feat: add X-LaunchDarkly-Instance-Id header (SDK-2359)#170
keelerm84 merged 6 commits into
mainfrom
mk/sdk-2359-instance-id

Conversation

@keelerm84
Copy link
Copy Markdown
Member

@keelerm84 keelerm84 commented May 13, 2026

Summary

Adds the X-LaunchDarkly-Instance-Id header to every outbound polling, streaming, and event request. Value is a v4 UUID generated once per SDK instance in ConfigBuilder::build and stable for that instance's lifetime.

The UUID is stored on Config (exposed via Config::instance_id() for diagnostics) and threaded into LD's own builders via a new set_instance_id setter on the DataSourceFactory and EventProcessorFactory traits. The setter has a default no-op implementation, so adding it is non-breaking for external implementors of those traits — only LD's own builders (Streaming/Polling/Event) override it.

`Client::build` calls `.to_owned().set_instance_id(id)` on each factory before invoking `.build()`. `HttpFeatureRequesterBuilder` likewise keeps its original `::new()` signature; the instance id is applied via a fluent `with_instance_id()` setter.

`StreamingDataSource::new` and the polling feature requester now treat the instance id as `Option<&str>` and simply skip the header when absent — consistent with the no-op default on the trait setter.

Test plan

  • `cargo fmt --check` — clean
  • `cargo clippy --all-targets -- -D warnings` — clean
  • `cargo test` — 304 unit tests + 12 doctests pass; streaming + polling + event-post instance-id assertions all green
  • Registered `instance-id` capability with the contract-test service
  • CI green

Note

Low Risk
Additive HTTP header and config field with default no-op trait hooks; no changes to flag evaluation or auth behavior.

Overview
Each SDK instance now gets a stable v4 UUID at ConfigBuilder::build, exposed as Config::instance_id(), and sent on outbound traffic as X-LaunchDarkly-Instance-Id for streaming, polling, and event posts.

Client::build clones the data source and event processor factories, calls the new set_instance_id hook (default no-op on custom trait impls), then builds them so all paths share one id. Polling uses HttpFeatureRequesterBuilder::with_instance_id; streaming adds the header on the SSE client when an id is present.

Contract tests advertise the instance-id capability. Unit tests cover UUID v4 shape, uniqueness per config, and mockito checks for the header on stream, poll, and /bulk requests.

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

keelerm84 added 2 commits May 12, 2026 16:29
Generate a v4 UUID once per SDK instance in ConfigBuilder::build and
stamp it on the X-LaunchDarkly-Instance-Id header sent by the streaming
data source, polling feature requester, and event processor. The header
value is stable for the lifetime of the SDK instance and unique across
instances, satisfying SCMP-server-connection-minutes-polling section 1.1.

The instance id is plumbed through the existing DataSourceFactory,
FeatureRequesterFactory (via HttpFeatureRequesterBuilder), and
EventProcessorFactory build paths so streaming, polling, and event
requests all carry the same 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 each request type.
DataSourceFactory::build and EventProcessorFactory::build are public
traits; adding an `instance_id` parameter is a breaking change for any
external implementor. This commit reverts that and instead adds a
`set_instance_id(&mut self, String)` method on each trait with a default
no-op implementation, so external implementors are unaffected.

LD-owned builders (StreamingDataSourceBuilder, PollingDataSourceBuilder,
EventProcessorBuilder) override the setter to store the value, and use
it in build() to stamp the `X-LaunchDarkly-Instance-Id` header on
outbound requests. Client::build calls `.to_owned().set_instance_id(id)`
on each builder before invoking build().

HttpFeatureRequesterBuilder::new also reverted to its prior signature;
the instance id is now applied via a fluent with_instance_id() setter.

StreamingDataSource::new and the polling feature requester now treat
the instance id as Option<&str> and skip the header when absent --
consistent with the no-op default on the trait setter.

cargo fmt --check / clippy --all-targets -- -D warnings / cargo test
(304 unit + 12 doctests) all pass.
@keelerm84 keelerm84 requested a review from a team as a code owner May 13, 2026 19:02
keelerm84 added 3 commits May 29, 2026 09:59
Generate a v4 UUID once per SDK instance in ConfigBuilder::build and
stamp it on the X-LaunchDarkly-Instance-Id header sent by the streaming
data source, polling feature requester, and event processor. The header
value is stable for the lifetime of the SDK instance and unique across
instances, satisfying SCMP-server-connection-minutes-polling section 1.1.

The instance id is plumbed through the existing DataSourceFactory,
FeatureRequesterFactory (via HttpFeatureRequesterBuilder), and
EventProcessorFactory build paths so streaming, polling, and event
requests all carry the same 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 each request type.
DataSourceFactory::build and EventProcessorFactory::build are public
traits; adding an `instance_id` parameter is a breaking change for any
external implementor. This commit reverts that and instead adds a
`set_instance_id(&mut self, String)` method on each trait with a default
no-op implementation, so external implementors are unaffected.

LD-owned builders (StreamingDataSourceBuilder, PollingDataSourceBuilder,
EventProcessorBuilder) override the setter to store the value, and use
it in build() to stamp the `X-LaunchDarkly-Instance-Id` header on
outbound requests. Client::build calls `.to_owned().set_instance_id(id)`
on each builder before invoking build().

HttpFeatureRequesterBuilder::new also reverted to its prior signature;
the instance id is now applied via a fluent with_instance_id() setter.

StreamingDataSource::new and the polling feature requester now treat
the instance id as Option<&str> and skip the header when absent --
consistent with the no-op default on the trait setter.

cargo fmt --check / clippy --all-targets -- -D warnings / cargo test
(304 unit + 12 doctests) all pass.
PR #168 (merged to main after this branch was cut) added two streaming
data source tests that call StreamingDataSource::new with the old
5-argument signature. This branch adds an instance_id: Option<&str>
parameter, so after rebasing onto main those call sites must pass the
new argument. They do not exercise the header, so they pass None.
@keelerm84 keelerm84 merged commit 370cb10 into main May 29, 2026
14 checks passed
@keelerm84 keelerm84 deleted the mk/sdk-2359-instance-id branch May 29, 2026 20:28
keelerm84 pushed a commit that referenced this pull request May 29, 2026
🤖 I have created a release *beep* *boop*
---


##
[3.1.0](3.0.3...3.1.0)
(2026-05-29)


### Features

* add X-LaunchDarkly-Instance-Id header (SDK-2359)
([#170](#170))
([370cb10](370cb10))

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

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.

2 participants