@@ -2,6 +2,7 @@ package openfeature
22
33import (
44 "errors"
5+ "fmt"
56 "sync"
67
78 "github.com/go-logr/logr"
@@ -14,7 +15,7 @@ type evaluationAPI struct {
1415 defaultProvider FeatureProvider
1516 namedProviders map [string ]FeatureProvider
1617 hks []Hook
17- evalCtx EvaluationContext
18+ apiCtx EvaluationContext
1819 logger logr.Logger
1920 mu sync.RWMutex
2021 eventExecutor * eventExecutor
@@ -28,29 +29,32 @@ func newEvaluationAPI() evaluationAPI {
2829 defaultProvider : NoopProvider {},
2930 namedProviders : map [string ]FeatureProvider {},
3031 hks : []Hook {},
31- evalCtx : EvaluationContext {},
32+ apiCtx : EvaluationContext {},
3233 logger : logger ,
3334 mu : sync.RWMutex {},
3435 eventExecutor : newEventExecutor (logger ),
3536 }
3637}
3738
38- // setProvider sets the default FeatureProvider of the evaluationAPI. Returns an error if FeatureProvider is nil
39- func (api * evaluationAPI ) setProvider (provider FeatureProvider ) error {
39+ // setProvider sets the default FeatureProvider of the evaluationAPI.
40+ // Returns an error if provider registration cause an error
41+ func (api * evaluationAPI ) setProvider (provider FeatureProvider , async bool ) error {
4042 api .mu .Lock ()
4143 defer api .mu .Unlock ()
4244
4345 if provider == nil {
4446 return errors .New ("default provider cannot be set to nil" )
4547 }
4648
47- // Initialize new default provider and shutdown the old one
48- // Provider update must be non-blocking, hence initialization & shutdown happens concurrently
4949 oldProvider := api .defaultProvider
5050 api .defaultProvider = provider
5151
52- api .initNewAndShutdownOld (provider , oldProvider )
53- err := api .eventExecutor .registerDefaultProvider (provider )
52+ err := api .initNewAndShutdownOld (provider , oldProvider , async )
53+ if err != nil {
54+ return err
55+ }
56+
57+ err = api .eventExecutor .registerDefaultProvider (provider )
5458 if err != nil {
5559 return err
5660 }
@@ -67,7 +71,7 @@ func (api *evaluationAPI) getProvider() FeatureProvider {
6771}
6872
6973// setProvider sets a provider with client name. Returns an error if FeatureProvider is nil
70- func (api * evaluationAPI ) setNamedProvider (clientName string , provider FeatureProvider ) error {
74+ func (api * evaluationAPI ) setNamedProvider (clientName string , provider FeatureProvider , async bool ) error {
7175 api .mu .Lock ()
7276 defer api .mu .Unlock ()
7377
@@ -80,8 +84,12 @@ func (api *evaluationAPI) setNamedProvider(clientName string, provider FeaturePr
8084 oldProvider := api .namedProviders [clientName ]
8185 api .namedProviders [clientName ] = provider
8286
83- api .initNewAndShutdownOld (provider , oldProvider )
84- err := api .eventExecutor .registerNamedEventingProvider (clientName , provider )
87+ err := api .initNewAndShutdownOld (provider , oldProvider , async )
88+ if err != nil {
89+ return err
90+ }
91+
92+ err = api .eventExecutor .registerNamedEventingProvider (clientName , provider )
8593 if err != nil {
8694 return err
8795 }
@@ -97,11 +105,11 @@ func (api *evaluationAPI) getNamedProviders() map[string]FeatureProvider {
97105 return api .namedProviders
98106}
99107
100- func (api * evaluationAPI ) setEvaluationContext (evalCtx EvaluationContext ) {
108+ func (api * evaluationAPI ) setEvaluationContext (apiCtx EvaluationContext ) {
101109 api .mu .Lock ()
102110 defer api .mu .Unlock ()
103111
104- api .evalCtx = evalCtx
112+ api .apiCtx = apiCtx
105113}
106114
107115func (api * evaluationAPI ) setLogger (l logr.Logger ) {
@@ -163,52 +171,68 @@ func (api *evaluationAPI) forTransaction(clientName string) (FeatureProvider, []
163171 provider = api .defaultProvider
164172 }
165173
166- return provider , api .hks , api .evalCtx
174+ return provider , api .hks , api .apiCtx
167175}
168176
169177// initNewAndShutdownOld is a helper to initialise new FeatureProvider and shutdown the old FeatureProvider.
170- // Operations happen concurrently.
171- func (api * evaluationAPI ) initNewAndShutdownOld (newProvider FeatureProvider , oldProvider FeatureProvider ) {
172- v , ok := newProvider .(StateHandler )
173- if ok && v .Status () == NotReadyState {
174- go func (provider FeatureProvider , stateHandler StateHandler , evalCtx EvaluationContext , eventChan chan eventPayload ) {
175- err := stateHandler .Init (evalCtx )
176- // emit ready/error event once initialization is complete
177- if err != nil {
178- eventChan <- eventPayload {
179- Event {
180- ProviderName : provider .Metadata ().Name ,
181- EventType : ProviderError ,
182- ProviderEventDetails : ProviderEventDetails {},
183- }, provider ,
184- }
185- } else {
186- eventChan <- eventPayload {
187- Event {
188- ProviderName : provider .Metadata ().Name ,
189- EventType : ProviderReady ,
190- ProviderEventDetails : ProviderEventDetails {},
191- }, provider ,
192- }
193- }
194- }(newProvider , v , api .evalCtx , api .eventExecutor .eventChan )
195- }
196-
197- v , ok = oldProvider .(StateHandler )
178+ func (api * evaluationAPI ) initNewAndShutdownOld (newProvider FeatureProvider , oldProvider FeatureProvider , async bool ) error {
179+ if async {
180+ go func (executor * eventExecutor , ctx EvaluationContext ) {
181+ // for async initialization, error is conveyed as an event
182+ event , _ := initializer (newProvider , ctx )
183+ executor .triggerEvent (event , newProvider )
184+ }(api .eventExecutor , api .apiCtx )
185+ } else {
186+ event , err := initializer (newProvider , api .apiCtx )
187+ api .eventExecutor .triggerEvent (event , newProvider )
188+ if err != nil {
189+ return err
190+ }
191+ }
192+
193+ v , ok := oldProvider .(StateHandler )
198194
199195 // oldProvider can be nil or without state handling capability
200196 if oldProvider == nil || ! ok {
201- return
197+ return nil
202198 }
203199
204200 // check for multiple bindings
205201 if oldProvider == api .defaultProvider || contains (oldProvider , maps .Values (api .namedProviders )) {
206- return
202+ return nil
207203 }
208204
209205 go func (forShutdown StateHandler ) {
210206 forShutdown .Shutdown ()
211207 }(v )
208+
209+ return nil
210+ }
211+
212+ // initializer is a helper to execute provider initialization and generate appropriate event for the initialization
213+ // It also returns an error if the initialization resulted in an error
214+ func initializer (provider FeatureProvider , apiCtx EvaluationContext ) (Event , error ) {
215+ var event = Event {
216+ ProviderName : provider .Metadata ().Name ,
217+ EventType : ProviderReady ,
218+ ProviderEventDetails : ProviderEventDetails {
219+ Message : "Provider initialization successful" ,
220+ },
221+ }
222+
223+ handler , ok := provider .(StateHandler )
224+ if ! ok {
225+ // Note - a provider without state handling capability can be assumed to be ready immediately.
226+ return event , nil
227+ }
228+
229+ err := handler .Init (apiCtx )
230+ if err != nil {
231+ event .EventType = ProviderError
232+ event .Message = fmt .Sprintf ("Provider initialization error, %v" , err )
233+ }
234+
235+ return event , err
212236}
213237
214238func contains (provider FeatureProvider , in []FeatureProvider ) bool {
0 commit comments