feat: add X-LaunchDarkly-Instance-Id header (SDK-2359)#170
Merged
Conversation
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.
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.
kinyoklion
approved these changes
May 29, 2026
…t-server-sdk into mk/sdk-2359-instance-id
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds the
X-LaunchDarkly-Instance-Idheader to every outbound polling, streaming, and event request. Value is a v4 UUID generated once per SDK instance inConfigBuilder::buildand stable for that instance's lifetime.The UUID is stored on
Config(exposed viaConfig::instance_id()for diagnostics) and threaded into LD's own builders via a newset_instance_idsetter on theDataSourceFactoryandEventProcessorFactorytraits. 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
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 asConfig::instance_id(), and sent on outbound traffic asX-LaunchDarkly-Instance-Idfor streaming, polling, and event posts.Client::buildclones the data source and event processor factories, calls the newset_instance_idhook (default no-op on custom trait impls), then builds them so all paths share one id. Polling usesHttpFeatureRequesterBuilder::with_instance_id; streaming adds the header on the SSE client when an id is present.Contract tests advertise the
instance-idcapability. Unit tests cover UUID v4 shape, uniqueness per config, and mockito checks for the header on stream, poll, and/bulkrequests.Reviewed by Cursor Bugbot for commit a9ec2a2. Bugbot is set up for automated code reviews on this repo. Configure here.