You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/multiprovider/README.md
+15-4Lines changed: 15 additions & 4 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -3,13 +3,15 @@
3
3
Combine multiple `FeatureProvider`s into a single provider with deterministic ordering, pluggable evaluation strategies, and unified status/event handling.
4
4
5
5
### Why use MultiProvider?
6
+
6
7
-**Layer providers**: fall back from an in-memory or experiment provider to a remote provider.
7
8
-**Migrate safely**: put the new provider first, retain the old as fallback.
8
9
-**Handle errors predictably**: choose whether errors should short-circuit or be skipped.
9
10
10
11
This implementation is adapted for Kotlin coroutines, flows, and OpenFeature error types.
- Returns the first child result that is not "flag not found".
47
50
- If a child returns an error other than `FLAG_NOT_FOUND`, that error is returned immediately.
48
51
- If all children report `FLAG_NOT_FOUND`, the default value is returned with reason `DEFAULT`.
49
-
50
52
-**FirstSuccessfulStrategy**
51
53
- Skips over errors from children and continues to the next provider.
52
54
- Returns the first successful evaluation (no error code).
53
55
- If no provider succeeds, the default value is returned with `FLAG_NOT_FOUND`.
54
56
55
57
Pick the strategy that best matches your failure-policy:
58
+
56
59
- Prefer early, explicit error surfacing: use `FirstMatchStrategy`.
57
60
- Prefer resilience and best-effort success: use `FirstSuccessfulStrategy`.
58
61
59
62
### Evaluation order matters
63
+
60
64
Children are evaluated in the order provided. Put the most authoritative or fastest provider first. For example, place a small in-memory override provider before a remote provider to reduce latency.
61
65
62
66
### Events and status aggregation
63
-
`MultiProvider` listens to child provider events and emits a single, aggregate status via `OpenFeatureAPI.statusFlow`. The highest-precedence status among children wins:
67
+
68
+
`MultiProvider` listens to child provider events and emits a single, aggregate status via `OpenFeatureAPI.statusFlow`. Per the OpenFeature specification: a child stays `NOT_READY` until it emits `PROVIDER_READY`, `PROVIDER_ERROR`, or `PROVIDER_STALE` (or only `PROVIDER_CONFIGURATION_CHANGED`, which does not change readiness). The highest-precedence status among children wins:
64
69
65
70
1. Fatal
66
71
2. NotReady
@@ -71,27 +76,34 @@ Children are evaluated in the order provided. Put the most authoritative or fast
71
76
`ProviderConfigurationChanged` is re-emitted as-is. When the aggregate status changes due to a child event, the original triggering event is also emitted.
72
77
73
78
### Context propagation
79
+
74
80
When the evaluation context changes, `MultiProvider` calls `onContextSet` on all child providers concurrently. Aggregate status transitions to Reconciling and then back to Ready (or Error) in line with SDK behavior.
75
81
76
82
### Provider metadata
83
+
77
84
`MultiProvider.metadata` exposes:
85
+
78
86
-`name = "multiprovider"`
79
87
-`originalMetadata`: a map of child-name → child `ProviderMetadata`
80
88
81
89
Child names are derived from each provider’s `metadata.name`. If duplicates occur, stable suffixes are applied (e.g., `myProvider_1`, `myProvider_2`).
82
90
83
91
Example: inspect provider metadata
92
+
84
93
```kotlin
85
94
val meta =OpenFeatureAPI.getProviderMetadata()
86
95
println(meta?.name) // "multiprovider"
87
96
println(meta?.originalMetadata) // map of child names to their metadata
88
97
```
89
98
90
99
### Shutdown behavior
100
+
91
101
`shutdown()` is invoked on all children. If any child fails to shut down, an aggregated error is thrown that includes all individual failures. Resources should be released in child providers even if peers fail.
92
102
93
103
### Custom strategies
104
+
94
105
You can provide your own composition policy by implementing `MultiProvider.Strategy`:
Copy file name to clipboardExpand all lines: kotlin-sdk/api/android/kotlin-sdk.api
+4-15Lines changed: 4 additions & 15 deletions
Original file line number
Diff line number
Diff line change
@@ -719,28 +719,17 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P
719
719
720
720
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
721
721
public fun <init> ()V
722
-
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)V
723
-
public synthetic fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
722
+
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V
723
+
public synthetic fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
724
724
public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
725
-
public final fun component2 ()Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;
726
-
public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;
727
-
public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;
725
+
public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;
726
+
public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;
728
727
public fun equals (Ljava/lang/Object;)Z
729
-
public final fun getError ()Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;
730
728
public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
731
729
public fun hashCode ()I
732
730
public fun toString ()Ljava/lang/String;
733
731
}
734
732
735
-
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
736
-
public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady;
737
-
public fun equals (Ljava/lang/Object;)Z
738
-
public synthetic fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
739
-
public fun getEventDetails ()Ljava/lang/Void;
740
-
public fun hashCode ()I
741
-
public fun toString ()Ljava/lang/String;
742
-
}
743
-
744
733
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
745
734
public fun <init> ()V
746
735
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V
Copy file name to clipboardExpand all lines: kotlin-sdk/api/jvm/kotlin-sdk.api
+4-15Lines changed: 4 additions & 15 deletions
Original file line number
Diff line number
Diff line change
@@ -719,28 +719,17 @@ public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$P
719
719
720
720
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
721
721
public fun <init> ()V
722
-
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)V
723
-
public synthetic fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
722
+
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V
723
+
public synthetic fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
724
724
public final fun component1 ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
725
-
public final fun component2 ()Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;
726
-
public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;
727
-
public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;
725
+
public final fun copy (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;
726
+
public static synthetic fun copy$default (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;ILjava/lang/Object;)Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderError;
728
727
public fun equals (Ljava/lang/Object;)Z
729
-
public final fun getError ()Ldev/openfeature/kotlin/sdk/exceptions/OpenFeatureError;
730
728
public fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
731
729
public fun hashCode ()I
732
730
public fun toString ()Ljava/lang/String;
733
731
}
734
732
735
-
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
736
-
public static final field INSTANCE Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderNotReady;
737
-
public fun equals (Ljava/lang/Object;)Z
738
-
public synthetic fun getEventDetails ()Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;
739
-
public fun getEventDetails ()Ljava/lang/Void;
740
-
public fun hashCode ()I
741
-
public fun toString ()Ljava/lang/String;
742
-
}
743
-
744
733
public final class dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$ProviderReady : dev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents {
745
734
public fun <init> ()V
746
735
public fun <init> (Ldev/openfeature/kotlin/sdk/events/OpenFeatureProviderEvents$EventDetails;)V
@@ -17,58 +16,48 @@ sealed class OpenFeatureProviderEvents {
17
16
18
17
/**
19
18
* The provider is ready to perform flag evaluations.
19
+
* [eventDetails] may supply [EventDetails.flagsChanged], [EventDetails.message], [EventDetails.errorCode], and [EventDetails.eventMetadata] as applicable.
20
20
*/
21
21
data classProviderReady(
22
22
overridevaleventDetails:EventDetails? = null
23
23
) : OpenFeatureProviderEvents()
24
24
25
25
/**
26
26
* The provider signaled an error.
27
+
* [eventDetails] may supply [EventDetails.flagsChanged], [EventDetails.message], [EventDetails.errorCode], and [EventDetails.eventMetadata] as applicable.
27
28
*/
28
29
data classProviderError(
29
-
overridevaleventDetails:EventDetails? = null,
30
-
@Deprecated("Please use eventDetails instead.") valerror:OpenFeatureError? = null
30
+
overridevaleventDetails:EventDetails? = null
31
31
) : OpenFeatureProviderEvents()
32
32
33
+
/**
34
+
* Configuration or flag definitions changed.
35
+
* [eventDetails] may supply [EventDetails.flagsChanged], [EventDetails.message], [EventDetails.errorCode], and [EventDetails.eventMetadata] as applicable.
36
+
*/
33
37
data classProviderConfigurationChanged(
34
38
overridevaleventDetails:EventDetails? = null
35
39
) : OpenFeatureProviderEvents()
36
40
37
41
/**
38
42
* The provider's cached state is no longer valid and may not be up-to-date with the source of truth.
43
+
* [eventDetails] may supply [EventDetails.flagsChanged], [EventDetails.message], [EventDetails.errorCode], and [EventDetails.eventMetadata] as applicable.
0 commit comments