Document client assignment request reliability - #39560
Conversation
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
bb55615 to
a1b96ad
Compare
|
/review |
There was a problem hiding this comment.
🤖 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 030aba59578b1c918e736d9f81fb2957934d7f5b — workflow run
aarsilv
left a comment
There was a problem hiding this comment.
Nice! Thanks for getting this out so fast. Approving assuming the code issues caught by GitHub fixed up.
…ment-flag-assignment-reliability
clreaume
left a comment
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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> |
There was a problem hiding this comment.
| <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. |
There was a problem hiding this comment.
| // 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) |
There was a problem hiding this comment.
| .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)` |
There was a problem hiding this comment.
suggesting to move this info above the settings it references
| `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` |
There was a problem hiding this comment.
| `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
| 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. | ||
|
|
There was a problem hiding this comment.
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: |
There was a problem hiding this comment.
| 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> |
There was a problem hiding this comment.
| <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 |
There was a problem hiding this comment.
| ### Bound flag configuration requests | |
| ### Set a timeout and retries for flag configuration requests |
Motivation
Client applications need bounded assignment initialization without changing default SDK behavior. The guides must describe one consistent cross-SDK contract.
Changes and Decisions
Retry-Aftervalues. Do not retry cancellation, HTTP 429, permanent failures, or other HTTP responses.