Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 11 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ This matrix mirrors the [feature matrix of the OpenFeature SDK for .NET](https:/
| ✅ | Logging | The provider logs through the logging configuration of the `Configuration` it is given. |
| ✅ | Domains | Domains bind clients to providers in the OpenFeature SDK; a separate provider instance may be registered per domain. |
| ✅ | Eventing | LaunchDarkly data source status changes are emitted as `PROVIDER_READY`, `PROVIDER_STALE`, and `PROVIDER_ERROR`. Flag changes are emitted as `PROVIDER_CONFIGURATION_CHANGED` with the changed flag key. |
| ⚠️ | Initialization | `InitializeAsync` waits for the LaunchDarkly client to become ready or to shut down, and stops waiting if the cancellation token given to `SetProviderAsync` is canceled. It has no timeout of its own; `StartWaitTime` applies to the client constructor. Since LaunchDarkly .NET SDK 8.17.0 a rejected SDK key is retried rather than failing permanently, so without a cancellation token `InitializeAsync` can wait indefinitely. Provider gap: [#71](https://github.com/launchdarkly/openfeature-dotnet-server/pull/71). |
| ✅ | Initialization | `InitializeAsync` reports whether the LaunchDarkly client became ready within the start wait time, which is `StartWaitTime` or the start wait time given to the provider constructor. `Timeout.InfiniteTimeSpan` waits indefinitely, until the cancellation token given to `SetProviderAsync` is canceled. Since LaunchDarkly .NET SDK 8.17.0 a rejected SDK key is retried rather than failing permanently, so `Timeout.InfiniteTimeSpan` keeps waiting while the key is rejected. |
| ✅ | Shutdown | `ShutdownAsync` closes the LaunchDarkly client. A closed client cannot be restarted, so a new provider instance is required afterward. |
| ✅ | Transaction Context Propagation | Provided by the OpenFeature SDK, which merges the transaction context into the evaluation context before the provider is called; no provider support is required. |
| ✅ | Extending | The underlying LaunchDarkly client is available through `GetClient()` for functionality with no OpenFeature equivalent. |
Expand Down Expand Up @@ -192,12 +192,18 @@ var inExperiment = details.FlagMetadata.GetBool("inExperiment") ?? false;

#### Asynchronous Initialization

The LaunchDarkly SDK by default blocks on construction for up to 10 seconds for initialization. If you require construction to be non-blocking, then you can adjust the `startWaitTime` to `TimeSpan.Zero`. Initialization will be completed asynchronously and OpenFeature will emit a ready event when the provider has initialized. The `SetProviderAsync` method can be awaited to wait for the SDK to finish initialization.
The LaunchDarkly SDK by default blocks on construction for up to 10 seconds for initialization, and `SetProviderAsync` then reports whether the client became ready in that time.

If you require construction to be non-blocking, then pass `TimeSpan.Zero` as the start wait time. Nothing is waited for, so `SetProviderAsync` reports a failed initialization, and OpenFeature emits a ready event once the provider becomes usable.

```csharp
var config = Configuration.Builder("my-sdk-key")
.StartWaitTime(TimeSpan.Zero)
.Build();
var provider = new Provider(config, TimeSpan.Zero);
```

To wait for the provider to become ready without a deadline, pass `Timeout.InfiniteTimeSpan`. Construction does not block, and `SetProviderAsync` completes once the data source becomes valid or fails permanently. Since LaunchDarkly .NET SDK 8.17.0 a rejected SDK key is retried rather than failing permanently, so pass a cancellation token to `SetProviderAsync` to bound the wait.

```csharp
var provider = new Provider(config, Timeout.InfiniteTimeSpan);
```

#### Provider Shutdown
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System;
using System.Threading.Channels;
using System.Threading.Tasks;
using LaunchDarkly.Logging;
Expand All @@ -12,17 +13,20 @@ private sealed class StatusProvider
{
private ProviderStatus _providerStatus = ProviderStatus.NotReady;
private string _statusMessage;
private bool _firstEvent = true;
private bool _startupWaitCanceled;
private readonly object _statusLock = new object();
private readonly Channel<object> _eventChannel;
private readonly string _providerName;
private readonly Logger _logger;
private readonly Func<bool> _initializationCompleted;

public StatusProvider(Channel<object> eventChannel, string providerName, Logger logger)
public StatusProvider(Channel<object> eventChannel, string providerName, Logger logger,
Func<bool> initializationCompleted)
{
_eventChannel = eventChannel;
_providerName = providerName;
_logger = logger;
_initializationCompleted = initializationCompleted;
}

private void EmitProviderEvent(ProviderEventTypes type, string message)
Expand Down Expand Up @@ -68,7 +72,7 @@ public void StartupWaitCanceled()
{
lock (_statusLock)
{
_firstEvent = false;
_startupWaitCanceled = true;
if (_providerStatus != ProviderStatus.NotReady)
{
EmitStatusEvent(_providerStatus, _statusMessage);
Expand Down Expand Up @@ -108,10 +112,11 @@ public void SetStatus(ProviderStatus status, string message = null)
_providerStatus = status;
_statusMessage = message;
// The OpenFeature client will emit a ready or error event when initialization completes.
// We want to avoid duplicating that event.
if (_firstEvent)
// The status change which completes initialization is therefore not emitted here, to avoid
// duplicating that event. Status changes after initialization has completed, including after it
// has failed, or after the startup wait was canceled, are emitted.
if (!_startupWaitCanceled && !_initializationCompleted())
{
_firstEvent = false;
return;
}

Expand Down
Loading
Loading