diff --git a/hugo/content/en/feature_flags/client/android.md b/hugo/content/en/feature_flags/client/android.md index d7d7981c013..16e8f5e68ec 100644 --- a/hugo/content/en/feature_flags/client/android.md +++ b/hugo/content/en/feature_flags/client/android.md @@ -40,8 +40,12 @@ val configuration = Configuration.Builder( .build() Datadog.initialize(this, configuration, TrackingConsent.GRANTED) -// 3. Enable Feature Flags -Flags.enable() +// 3. Enable Feature Flags with a bounded assignment request timeout +Flags.enable( + FlagsConfiguration.Builder() + .assignmentRequestTimeout(1_500L) + .build() +) // 4. Create and set up the OpenFeature provider val provider = FlagsClient.Builder().build().asOpenFeatureProvider() @@ -98,8 +102,13 @@ After initializing Datadog, enable `Flags` to attach it to the current Datadog A {{< code-block lang="kotlin" >}} import com.datadog.android.flags.Flags +import com.datadog.android.flags.FlagsConfiguration + +val flagsConfiguration = FlagsConfiguration.Builder() + .assignmentRequestTimeout(1_500L) + .build() -Flags.enable() +Flags.enable(flagsConfiguration) {{< /code-block >}} You can also pass a configuration object; see [Advanced configuration](#advanced-configuration). @@ -300,12 +309,47 @@ The `Flags.enable()` API accepts optional configuration with the options listed {{< code-block lang="kotlin" >}} val config = FlagsConfiguration.Builder() - // configure options here + .assignmentRequestTimeout(1_500L) + .assignmentRequestRetryCount(2) .build() Flags.enable(config) {{< /code-block >}} +`assignmentRequestTimeout(timeoutMs)` +: 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. + +The SDK manages the configured retry count and creates a new HTTP call for each attempt. The timeout applies to each attempt. Total network duration can reach `(retryCount + 1) * timeoutMs`, plus retry delays. When the timeout is `0`, the HTTP transport supplies the time bound and may allow an unlimited duration. + +
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.
+ +For lower-level transport control, supply an assignment-only OkHttp call factory. Add OkHttp as a direct application dependency when you use this option: + +{{< code-block lang="groovy" filename="build.gradle" >}} +dependencies { + implementation "com.squareup.okhttp3:okhttp:4.12.0" +} +{{< /code-block >}} + +{{< code-block lang="kotlin" >}} +import okhttp3.OkHttpClient + +val assignmentClient = OkHttpClient.Builder() + // Add assignment-specific proxy, TLS, or interceptors here. + .build() + +val config = FlagsConfiguration.Builder() + .assignmentRequestCallFactory(assignmentClient) + .assignmentRequestTimeout(1_500L) + .assignmentRequestRetryCount(2) + .build() +{{< /code-block >}} + +The SDK still constructs the URL, method, body, and authentication headers. The scalar timeout and retry policies compose on top of calls created by the supplied factory. When the assignment timeout is positive, the factory must return calls that provide and honor a configurable `Call.timeout()`. A call that returns `Timeout.NONE` fails before execution. Exposure and evaluation uploads continue to use the SDK transport. The application retains ownership of the supplied factory and its resources. + `trackExposures()` : When `true` (default), the SDK automatically records an _exposure event_ when a flag is evaluated. These events contain metadata about which flag was accessed, which variant was served, and under what context. They are sent to Datadog so you can later analyze feature adoption. If you only need local evaluation without telemetry, you can disable it with: `trackExposures(false)`. diff --git a/hugo/content/en/feature_flags/client/flutter.md b/hugo/content/en/feature_flags/client/flutter.md index 0e06d30b1b1..7b59fe63f7f 100644 --- a/hugo/content/en/feature_flags/client/flutter.md +++ b/hugo/content/en/feature_flags/client/flutter.md @@ -86,7 +86,13 @@ final configuration = DatadogConfiguration( rumConfiguration: DatadogRumConfiguration( applicationId: '', ), -)..addPlugin(const DatadogFlagsPluginConfiguration()); +)..addPlugin( + const DatadogFlagsPluginConfiguration( + flagsConfiguration: DatadogFlagsConfiguration( + assignmentRequestTimeout: Duration(milliseconds: 1500), + ), + ), + ); await DatadogSdk.instance.initialize(configuration, TrackingConsent.granted); {{< /code-block >}} @@ -126,6 +132,7 @@ final datadogFlags = DatadogFlags.instance; await datadogFlags.enable( configuration: DatadogFlagsConfiguration( + assignmentRequestTimeout: const Duration(milliseconds: 1500), datadogConfig: const DatadogFlagsConfig( clientToken: '', env: '', @@ -289,6 +296,8 @@ print(details.error?.code); {{< code-block lang="dart" >}} DatadogFlagsConfiguration( datadogConfig: datadogConfig, + assignmentRequestTimeout: const Duration(milliseconds: 1500), + assignmentRequestRetryCount: 2, trackExposures: true, trackEvaluations: true, evaluationFlushInterval: const Duration(seconds: 10), @@ -296,6 +305,36 @@ DatadogFlagsConfiguration( ); {{< /code-block >}} +`assignmentRequestTimeout` +: Timeout for each flag assignment request, including the complete response-body download. The SDK does not add a timeout by default. Set a positive duration to enable the timeout; `Duration.zero` leaves it disabled. + +`assignmentRequestRetryCount` +: 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. Accepted values are from `0` to `10`. Retries cover selected transient network errors, timeouts, HTTP 408, and HTTP 5xx responses. Cancellation, HTTP 429, generic I/O errors, permanent protocol errors, and TLS failures are not retried. Retries use randomized exponential backoff capped at 30 seconds. For HTTP 503, a valid `Retry-After` value up to 30 seconds is a minimum delay before the backoff. A response that requests a longer delay is not retried. + +
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.
+ +For lower-level transport control, compose an assignment-only HTTP client: + +{{< code-block lang="dart" >}} +import 'package:datadog_flags/datadog_flags.dart'; +import 'package:http/http.dart' as http; + +final assignmentClient = withAssignmentRequestRetry( + withAssignmentRequestTimeout( + http.Client(), + const Duration(milliseconds: 1500), + ), + 2, +); + +final config = DatadogFlagsConfiguration( + datadogConfig: datadogConfig, + assignmentRequestHttpClient: assignmentClient, +); +{{< /code-block >}} + +A supplied `assignmentRequestHttpClient` is used verbatim and replaces the scalar timeout and retry settings. The helpers buffer the complete response, create a fresh request for each retry, and apply only to assignment requests. The application owns and closes the supplied client after disabling Feature Flags. + `trackExposures` : When `true` (default), the SDK records exposure events for successful evaluations whose assignments are marked for logging. Set to `false` to disable exposure tracking. @@ -326,6 +365,8 @@ final configuration = DatadogConfiguration( )..addPlugin( const DatadogFlagsPluginConfiguration( flagsConfiguration: DatadogFlagsConfiguration( + assignmentRequestTimeout: Duration(milliseconds: 1500), + assignmentRequestRetryCount: 2, trackExposures: true, trackEvaluations: true, ), @@ -391,6 +432,7 @@ Future initializeFlags() async { await datadogFlags.enable( configuration: DatadogFlagsConfiguration( + assignmentRequestTimeout: const Duration(milliseconds: 1500), datadogConfig: const DatadogFlagsConfig( clientToken: '', env: '', diff --git a/hugo/content/en/feature_flags/client/ios.md b/hugo/content/en/feature_flags/client/ios.md index 88a9cb6bc83..6d56e8a7163 100644 --- a/hugo/content/en/feature_flags/client/ios.md +++ b/hugo/content/en/feature_flags/client/ios.md @@ -99,7 +99,9 @@ After initializing Datadog, enable `Flags` to attach it to the current Datadog i {{< code-block lang="swift" >}} import DatadogFlags -Flags.enable() +var flagsConfiguration = Flags.Configuration() +flagsConfiguration.assignmentRequestTimeout = 1.5 +Flags.enable(with: flagsConfiguration) {{< /code-block >}} You can also pass a configuration object; see [Advanced configuration](#advanced-configuration). @@ -328,7 +330,9 @@ Datadog.initialize( trackingConsent: .granted ) -Flags.enable() +var flagsConfiguration = Flags.Configuration() +flagsConfiguration.assignmentRequestTimeout = 1.5 +Flags.enable(with: flagsConfiguration) let context = MutableContext(targetingKey: "user-123") let provider = DatadogProvider() @@ -444,9 +448,53 @@ The `Flags.enable()` API accepts optional configuration with options listed belo {{< code-block lang="swift" >}} var config = Flags.Configuration() +config.assignmentRequestTimeout = 1.5 +config.assignmentRequestRetryCount = 2 Flags.enable(with: config) {{< /code-block >}} +`assignmentRequestTimeout` +: Timeout in seconds for each flag assignment request, including the complete response-body download. The SDK does not add a timeout by default. Set a positive, finite value to enable the timeout. A value of `0`, a negative value, or a non-finite value disables it. Values greater than `2_147_483.647` seconds are reduced to this maximum. + +`assignmentRequestRetryCount` +: 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 reduced to the nearest bound. Retries cover selected transient network errors, timeouts, HTTP 408, and HTTP 5xx responses. Cancellation, HTTP 429, unknown URL errors, permanent URL 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. + +The scalar timeout applies to each attempt. Total duration includes all attempts and all retry delays. + +
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.
+ +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. + `trackExposures` : When `true` (default), the SDK automatically records an _exposure event_ when a flag is evaluated. These events contain metadata about which flag was accessed, which variant was served, and under what context. They are sent to Datadog so you can later analyze feature adoption. If you only need local evaluation without telemetry, you can disable this option. diff --git a/hugo/content/en/feature_flags/client/javascript.md b/hugo/content/en/feature_flags/client/javascript.md index f6876136dd6..e350d2c5a10 100644 --- a/hugo/content/en/feature_flags/client/javascript.md +++ b/hugo/content/en/feature_flags/client/javascript.md @@ -53,7 +53,7 @@ Create a `DatadogProvider` instance with your Datadog credentials. For live Brow {{< site-region region="gov,gov2" >}}
Browser Feature Flags are not supported for the selected Datadog site ({{< region-param key="dd_site_name" >}}).
{{< /site-region >}} ```javascript -import { DatadogProvider } from '@datadog/openfeature-browser'; +import { DatadogProvider, withTimeout } from '@datadog/openfeature-browser'; import { OpenFeature } from '@openfeature/web-sdk'; const provider = new DatadogProvider({ @@ -65,6 +65,7 @@ const provider = new DatadogProvider({ clientToken: '', site: '{{< region-param key="dd_site" code="true" >}}', env: '', + flagConfigurationFetch: withTimeout(globalThis.fetch, 1_500), }); ``` @@ -170,7 +171,7 @@ console.log(details.errorCode); // Error code, if evaluation failed Here's a complete example showing how to set up and use Datadog Feature Flags in a JavaScript application: ```javascript -import { DatadogProvider } from '@datadog/openfeature-browser'; +import { DatadogProvider, withTimeout } from '@datadog/openfeature-browser'; import { OpenFeature } from '@openfeature/web-sdk'; // Initialize the Datadog provider @@ -179,6 +180,7 @@ const provider = new DatadogProvider({ clientToken: '', site: '{{< region-param key="dd_site" code="true" >}}', env: '', + flagConfigurationFetch: withTimeout(globalThis.fetch, 1_500), }); // Set the evaluation context @@ -226,6 +228,30 @@ The web provider also supports these optional settings: | `flaggingProxy` | unset | Fetch flags through a proxy instead of `site`. | | `customHeaders` | unset | Add headers to flag-fetch requests. | | `overwriteRequestHeaders` | `false` | Replace default request headers with `customHeaders`. | +| `flagConfigurationFetch` | `globalThis.fetch` | Provide a Fetch-compatible implementation for flag configuration requests. | + +### 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: + +{{< code-block lang="javascript" >}} +import { DatadogProvider, withRetry, withTimeout } from '@datadog/openfeature-browser'; + +const provider = new DatadogProvider({ + // Other provider options... + flagConfigurationFetch: withRetry(withTimeout(globalThis.fetch, 1_500), 2), +}); +{{< /code-block >}} + +`withTimeout(fetch, timeoutMs)` +: Sets the timeout in milliseconds for each request attempt, including the complete response-body download. Set the timeout to `0` to disable the timer. Accepted values are non-negative integers up to `2_147_483_647`. + +`withRetry(fetch, retryCount)` +: Sets the number of retries after the initial request. Set the retry count to `0` to disable retries. Accepted values are integers from `0` to `10`. Retries cover Fetch `TypeError` failures, timeout errors, HTTP 408, and HTTP 5xx responses. Caller cancellation and HTTP 429 responses are not retried. Retries use randomized exponential backoff capped at 30 seconds. For HTTP 503, a valid `Retry-After` value up to 30 seconds is a minimum delay before the backoff. A response that requests a longer delay is not retried. Browsers report network, CORS, and CSP failures as `TypeError`, so the wrapper cannot separate these causes. + +In the example, `withTimeout` is inside `withRetry`. Therefore, each attempt has its own 1,500-millisecond timeout. + +
The `flagConfigurationFetch` option applies only to flag configuration requests. It does not affect exposure, aggregated flag evaluation, or RUM telemetry requests.
## Override flags in your browser diff --git a/hugo/content/en/feature_flags/client/unity.md b/hugo/content/en/feature_flags/client/unity.md index 270aa8ec788..591176981c9 100644 --- a/hugo/content/en/feature_flags/client/unity.md +++ b/hugo/content/en/feature_flags/client/unity.md @@ -52,7 +52,9 @@ After initializing Datadog, enable `Flags` to attach it to the current Datadog U {{< code-block lang="csharp" >}} using Datadog.Unity.Flags; -DdFlags.Enable(); +DdFlags.Enable(new FlagsConfiguration( + assignmentRequestTimeoutSeconds: 1, + assignmentRequestRetryCount: 0)); {{< /code-block >}} You can also pass a configuration object; see [Advanced configuration](#advanced-configuration). @@ -203,12 +205,35 @@ The `DdFlags.Enable()` API accepts optional configuration with options listed be {{< code-block lang="csharp" >}} DdFlags.Enable(new FlagsConfiguration( + assignmentRequestTimeoutSeconds: 1, + assignmentRequestRetryCount: 2, trackExposures: true, trackEvaluations: true, evaluationFlushIntervalSeconds: 10.0f )); {{< /code-block >}} +`assignmentRequestTimeoutSeconds` +: Timeout in seconds 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. + +`assignmentRequestRetryCount` +: 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. Accepted values are from `0` to `10`. Retries cover selected transient network errors, timeouts, HTTP 408, and HTTP 5xx responses. Cancellation, HTTP 429, generic I/O errors, permanent protocol errors, and TLS failures are not retried. Retries use randomized exponential backoff capped at 30 seconds. For HTTP 503, a valid `Retry-After` value up to 30 seconds is a minimum delay before the backoff. A response that requests a longer delay is not retried. + +
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.
+ +For lower-level transport control, compose an assignment-only transport: + +{{< code-block lang="csharp" >}} +var assignmentTransport = AssignmentRequestTransports.Default + .WithTimeout(1) + .WithRetry(2); + +DdFlags.Enable(new FlagsConfiguration( + assignmentRequestTransport: assignmentTransport)); +{{< /code-block >}} + +A supplied `assignmentRequestTransport` is used verbatim and replaces the scalar timeout and retry settings. The helpers use fully buffered immutable responses, create a fresh native request for each retry, and apply only to assignment requests. The SDK owns its native requests; the application retains ownership of a custom transport and its resources. + `trackExposures` : When `true` (default), the SDK automatically records an _exposure event_ when a flag is evaluated. These events contain metadata about which flag was accessed, which variant was served, and under what context. They are sent to Datadog so you can later analyze feature adoption. Set to `false` to disable exposure tracking. diff --git a/hugo/content/en/getting_started/feature_flags/_index.md b/hugo/content/en/getting_started/feature_flags/_index.md index b5ad7b7b206..846204dbf91 100644 --- a/hugo/content/en/getting_started/feature_flags/_index.md +++ b/hugo/content/en/getting_started/feature_flags/_index.md @@ -86,6 +86,8 @@ You can set up Feature Flags automatically with the 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 Retry-After 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 TypeError and cannot separate these causes. See the client SDK guides for platform-specific timeout, retry, and transport APIs. + {{< tabs >}} {{% tab "JavaScript browser" %}} @@ -100,7 +102,7 @@ Then, add the following to your project to initialize the SDK: {{< site-region region="gov,gov2" >}}
Browser Feature Flags are not supported for the selected Datadog site ({{< region-param key="dd_site_name" >}}).
{{< /site-region >}} {{< code-block lang="javascript" >}} -import { DatadogProvider } from '@datadog/openfeature-browser'; +import { DatadogProvider, withTimeout } from '@datadog/openfeature-browser'; import { OpenFeature } from '@openfeature/web-sdk'; // Initialize the provider @@ -111,7 +113,9 @@ const provider = new DatadogProvider({ site: '{{< region-param key="dd_site" code="true" >}}', env: '', // Same environment normally passed to the RUM SDK service: '', - version: '1.0.0' + version: '1.0.0', + // Bound each configuration request to 1,500 milliseconds. + flagConfigurationFetch: withTimeout(globalThis.fetch, 1_500) }); // Set the provider