Skip to content

Document client assignment request reliability - #39560

Open
leoromanovsky wants to merge 13 commits into
masterfrom
leo.romanovsky/document-flag-assignment-reliability
Open

Document client assignment request reliability#39560
leoromanovsky wants to merge 13 commits into
masterfrom
leo.romanovsky/document-flag-assignment-reliability

Conversation

@leoromanovsky

@leoromanovsky leoromanovsky commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

Motivation

Client applications need bounded assignment initialization without changing default SDK behavior. The guides must describe one consistent cross-SDK contract.

Changes and Decisions

@leoromanovsky leoromanovsky changed the title Document client assignment request timeouts and retries Document client assignment request reliability Aug 28, 2026
Document zero-default assignment timeout and retry controls for supported client SDKs, including composable transport examples. Defer React Native until released native dependencies are available.

Environment: Datadog workspace
@leoromanovsky
leoromanovsky force-pushed the leo.romanovsky/document-flag-assignment-reliability branch from bb55615 to a1b96ad Compare August 28, 2026 23:20
@leoromanovsky
leoromanovsky marked this pull request as ready for review September 1, 2026 17:24
@leoromanovsky
leoromanovsky requested a review from a team as a code owner September 1, 2026 17:24
@leoromanovsky

Copy link
Copy Markdown
Contributor Author

/review

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 Automated review by Claude. AI-generated; verify before acting.

Content is accurate and complete. Two blocker-adjacent findings on the new snippets/alert copy, plus one consistency nit.

Reviewed 030aba59578b1c918e736d9f81fb2957934d7f5bworkflow run

Comment thread hugo/content/en/feature_flags/client/flutter.md
Comment thread hugo/content/en/getting_started/feature_flags/_index.md Outdated
Comment thread hugo/content/en/feature_flags/client/javascript.md Outdated

@aarsilv aarsilv left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice! Thanks for getting this out so fast. Approving assuming the code issues caught by GitHub fixed up.

Comment thread hugo/content/en/feature_flags/client/android.md
Comment thread hugo/content/en/feature_flags/client/android.md
Comment thread hugo/content/en/feature_flags/client/flutter.md
Comment thread hugo/content/en/feature_flags/client/ios.md
Comment thread hugo/content/en/feature_flags/client/ios.md Outdated
Comment thread hugo/content/en/feature_flags/client/javascript.md Outdated
Comment thread hugo/content/en/feature_flags/client/unity.md
@hestonhoffman hestonhoffman added the editorial review Waiting on a more in-depth review label Sep 1, 2026

@clreaume clreaume left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

first round of suggestions/comments. please reach out if you need help deciphering any of this!

: Timeout in milliseconds for each flag assignment request, including the complete response-body download. The SDK does not add a timeout by default. Set a positive value to enable the timeout; `0` leaves it disabled. Negative values are coerced to `0`. When the HTTP call already has a nonzero timeout, the shorter timeout applies.

`assignmentRequestRetryCount(retryCount)`
: Number of retries after the initial flag assignment request. The default is `0`, so the SDK makes only the initial request unless you opt in to retries. Values outside the range from `0` to `10` are coerced to the nearest bound. Retries cover selected transient network errors, timeouts, HTTP 408, and HTTP 5xx responses. Canceled calls, HTTP 429, generic I/O errors, permanent protocol errors, and TLS failures are not retried. Retries use randomized exponential backoff capped at 30 seconds. The SDK reads `Retry-After` only for HTTP 503. A valid value up to 30 seconds is a minimum delay before the backoff. A response that requests a longer delay is not retried.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

what does it mean for values outside 0 to 10 to be "coerced to the nearest bound"?


Choose the SDK that matches where the flag is evaluated and initialize the Datadog Feature Flags provider.

<div class="alert alert-warning">Client SDKs do not add a flag assignment request timeout or retry by default, so the underlying platform transport remains authoritative. Configure a timeout of at most 1,500 milliseconds when initialization must finish within a known period. Retries cover transient network errors, timeouts, HTTP 408, and HTTP 5xx responses. They use randomized exponential backoff capped at 30 seconds. For HTTP 503, a valid <code>Retry-After</code> value up to 30 seconds is a minimum delay before the backoff. A response that requests a longer delay is not retried. Mobile SDKs do not retry cancellation, HTTP 429, generic I/O errors, permanent protocol errors, or TLS failures. Browser Fetch reports several failures as <code>TypeError</code> and cannot separate these causes. See the <a href="/feature_flags/client/">client SDK guides</a> for platform-specific timeout, retry, and transport APIs.</div>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
<div class="alert alert-warning">Client SDKs do not add a flag assignment request timeout or retry by default, so the underlying platform transport remains authoritative. Configure a timeout of at most 1,500 milliseconds when initialization must finish within a known period. Retries cover transient network errors, timeouts, HTTP 408, and HTTP 5xx responses. They use randomized exponential backoff capped at 30 seconds. For HTTP 503, a valid <code>Retry-After</code> value up to 30 seconds is a minimum delay before the backoff. A response that requests a longer delay is not retried. Mobile SDKs do not retry cancellation, HTTP 429, generic I/O errors, permanent protocol errors, or TLS failures. Browser Fetch reports several failures as <code>TypeError</code> and cannot separate these causes. See the <a href="/feature_flags/client/">client SDK guides</a> for platform-specific timeout, retry, and transport APIs.</div>

I think this level of explanation, and its prominence, are overkill for a "getting started" guide. I'm suggesting removing this callout, and extracting the essence into a code comment below—just above the new line bounding the request to 1,500 milliseconds. see that separate suggestion below

service: '<SERVICE_NAME>',
version: '1.0.0'
version: '1.0.0',
// Bound each configuration request to 1,500 milliseconds.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
// Bound each configuration request to 1,500 milliseconds.
// Client SDKs do not set a timeout or retry flag requests by default; the platform's HTTP transport decides how long a request can take. If initialization must finish within a known period, configure a timeout of at most 1,500 milliseconds. See the client SDK guides (/feature_flags/client/) for platform-specific timeout, retry, and transport options.

see comment above for explanation

val config = FlagsConfiguration.Builder()
// configure options here
.assignmentRequestTimeout(1_500L)
.assignmentRequestRetryCount(2)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
.assignmentRequestRetryCount(2)
.assignmentRequestRetryCount(2)
// configure additional options here

as I suggested here, I think it's nice to have this placeholder for the other options, to not imply these are the only ones

Flags.enable(config)
{{< /code-block >}}

`assignmentRequestTimeout(timeoutMs)`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

suggesting to move this info above the settings it references

Suggested change
`assignmentRequestTimeout(timeoutMs)`
<div class="alert alert-info">The assignment request timeout and retry settings below apply only to requests that fetch flag assignments. They do not affect exposure, aggregated flag evaluation, or RUM telemetry requests.</div>
`assignmentRequestTimeout(timeoutMs)`

Flags.enable(with: config)
{{< /code-block >}}

`assignmentRequestTimeout`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
`assignmentRequestTimeout`
<div class="alert alert-info">Assignment request timeout and retry settings apply only to requests that fetch flag assignments. They do not affect exposure, aggregated flag evaluation, or RUM telemetry requests.</div>
`assignmentRequestTimeout`

moving callout from below

Comment on lines +466 to +497
For lower-level transport control, compose an assignment-only fetch implementation:

{{< code-block lang="swift" >}}
let assignmentFetch = Flags.AssignmentRequestFetch
.urlSession()
.withTimeout(1.5)
.withRetry(2)

var config = Flags.Configuration()
config.assignmentRequestFetch = assignmentFetch
Flags.enable(with: config)
{{< /code-block >}}

The SDK still constructs the URL, body, authentication, and custom headers. A supplied `assignmentRequestFetch` replaces the scalar timeout and retry settings. The SDK accepts at most one completion from the supplied fetch for each request and validates the HTTP response status. The custom transport applies only to assignment requests. The caller retains ownership of a supplied `URLSession` and other custom transport resources. The SDK does not invalidate or close them.

`withTimeout(timeout)`
: Adds a timeout that includes the complete response-body download. A positive, finite value enables the timeout. A nonpositive or non-finite value leaves the transport unchanged. Values greater than `2_147_483.647` seconds are reduced to this maximum.

`withRetry(retryCount)`
: Adds SDK-managed retries after the initial attempt. Values outside the range from `0` to `10` are reduced to the nearest bound. The retry policy matches `assignmentRequestRetryCount`. The SDK reads `Retry-After` only for HTTP 503.

In the example, `withTimeout` is inside `withRetry`. Therefore, each attempt has its own 1.5-second timeout. Reverse the wrappers to use one 1.5-second timeout for the initial request and all retries:

{{< code-block lang="swift" >}}
let assignmentFetch = Flags.AssignmentRequestFetch
.urlSession()
.withRetry(2)
.withTimeout(1.5)
{{< /code-block >}}

With this reversed order, one timeout covers all attempts and retry delays. With the original order, total duration includes each attempt timeout and all retry delays.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

suggesting to move this into a new H3 (titled ### Supply a custom assignment transport) after line 525 (customFlagsHeaders entry), before ## Testing (527)


### Bound flag configuration requests

The browser provider does not add a timeout or retries by default. Use `withTimeout` and `withRetry` to bound each request attempt and retry transient failures:

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
The browser provider does not add a timeout or retries by default. Use `withTimeout` and `withRetry` to bound each request attempt and retry transient failures:
The browser provider does not add a timeout or retries by default. Use `withTimeout` and `withRetry` to limit each request attempt and retry transient failures:
<div class="alert alert-info">The `flagConfigurationFetch` option applies only to flag configuration requests. It does not affect exposure, aggregated flag evaluation, or RUM telemetry requests.</div>

suggesting moving alert from below up here. also updating "bound" to "limit"


In the example, `withTimeout` is inside `withRetry`. Therefore, each attempt has its own 1,500-millisecond timeout.

<div class="alert alert-info">The `flagConfigurationFetch` option applies only to flag configuration requests. It does not affect exposure, aggregated flag evaluation, or RUM telemetry requests.</div>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
<div class="alert alert-info">The `flagConfigurationFetch` option applies only to flag configuration requests. It does not affect exposure, aggregated flag evaluation, or RUM telemetry requests.</div>

suggesting moving this above

| `overwriteRequestHeaders` | `false` | Replace default request headers with `customHeaders`. |
| `flagConfigurationFetch` | `globalThis.fetch` | Provide a Fetch-compatible implementation for flag configuration requests. |

### Bound flag configuration requests

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Suggested change
### Bound flag configuration requests
### Set a timeout and retries for flag configuration requests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

editorial review Waiting on a more in-depth review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants