2
2
3
3
import dev .openfeature .contrib .providers .flagd .resolver .Resolver ;
4
4
import dev .openfeature .contrib .providers .flagd .resolver .common .FlagdProviderEvent ;
5
- import dev .openfeature .contrib .providers .flagd .resolver .common .Util ;
6
5
import dev .openfeature .contrib .providers .flagd .resolver .grpc .GrpcResolver ;
7
6
import dev .openfeature .contrib .providers .flagd .resolver .grpc .cache .Cache ;
8
7
import dev .openfeature .contrib .providers .flagd .resolver .process .InProcessResolver ;
9
8
import dev .openfeature .sdk .EvaluationContext ;
10
9
import dev .openfeature .sdk .EventProvider ;
11
10
import dev .openfeature .sdk .Hook ;
12
- import dev .openfeature .sdk .ImmutableContext ;
13
11
import dev .openfeature .sdk .Metadata ;
14
12
import dev .openfeature .sdk .ProviderEvaluation ;
15
13
import dev .openfeature .sdk .ProviderEvent ;
@@ -36,7 +34,7 @@ public class FlagdProvider extends EventProvider {
36
34
private static final String FLAGD_PROVIDER = "flagd" ;
37
35
private final Resolver flagResolver ;
38
36
private final List <Hook > hooks = new ArrayList <>();
39
- private final EventsLock eventsLock = new EventsLock ();
37
+ private final FlagdProviderSyncResources syncResources = new FlagdProviderSyncResources ();
40
38
41
39
/**
42
40
* An executor service responsible for emitting
@@ -108,7 +106,9 @@ public FlagdProvider(final FlagdOptions options) {
108
106
gracePeriod = Config .DEFAULT_STREAM_RETRY_GRACE_PERIOD ;
109
107
hooks .add (new SyncMetadataHook (this ::getEnrichedContext ));
110
108
errorExecutor = Executors .newSingleThreadScheduledExecutor ();
111
- this .eventsLock .initialized = initialized ;
109
+ if (initialized ) {
110
+ this .syncResources .initialize ();
111
+ }
112
112
}
113
113
114
114
@ Override
@@ -118,28 +118,27 @@ public List<Hook> getProviderHooks() {
118
118
119
119
@ Override
120
120
public void initialize (EvaluationContext evaluationContext ) throws Exception {
121
- synchronized (eventsLock ) {
122
- if (eventsLock . initialized ) {
121
+ synchronized (syncResources ) {
122
+ if (syncResources . isInitialized () ) {
123
123
return ;
124
124
}
125
125
126
126
flagResolver .init ();
127
+ // block till ready - this works with deadline fine for rpc, but with in_process
128
+ // we also need to take parsing into the equation
129
+ // TODO: evaluate where we are losing time, so we can remove this magic number -
130
+ syncResources .waitForInitialization (this .deadline * 2 );
127
131
}
128
- // block till ready - this works with deadline fine for rpc, but with in_process
129
- // we also need to take parsing into the equation
130
- // TODO: evaluate where we are losing time, so we can remove this magic number -
131
- // follow up
132
- // wait outside of the synchonrization or we'll deadlock
133
- Util .busyWaitAndCheck (this .deadline * 2 , () -> eventsLock .initialized );
134
132
}
135
133
136
134
@ Override
137
135
public void shutdown () {
138
- synchronized (eventsLock ) {
139
- if (!eventsLock .initialized ) {
140
- return ;
141
- }
136
+ synchronized (syncResources ) {
142
137
try {
138
+ if (!syncResources .isInitialized () || syncResources .isShutDown ()) {
139
+ return ;
140
+ }
141
+
143
142
this .flagResolver .shutdown ();
144
143
if (errorExecutor != null ) {
145
144
errorExecutor .shutdownNow ();
@@ -148,7 +147,7 @@ public void shutdown() {
148
147
} catch (Exception e ) {
149
148
log .error ("Error during shutdown {}" , FLAGD_PROVIDER , e );
150
149
} finally {
151
- eventsLock . initialized = false ;
150
+ syncResources . shutdown () ;
152
151
}
153
152
}
154
153
}
@@ -189,15 +188,13 @@ public ProviderEvaluation<Value> getObjectEvaluation(String key, Value defaultVa
189
188
* @return context
190
189
*/
191
190
EvaluationContext getEnrichedContext () {
192
- return eventsLock . enrichedContext ;
191
+ return syncResources . getEnrichedContext () ;
193
192
}
194
193
195
194
@ SuppressWarnings ("checkstyle:fallthrough" )
196
195
private void onProviderEvent (FlagdProviderEvent flagdProviderEvent ) {
197
-
198
- synchronized (eventsLock ) {
199
- log .info ("FlagdProviderEvent: {}" , flagdProviderEvent .getEvent ());
200
-
196
+ log .info ("FlagdProviderEvent event {} " , flagdProviderEvent .getEvent ());
197
+ synchronized (syncResources ) {
201
198
/*
202
199
* We only use Error and Ready as previous states.
203
200
* As error will first be emitted as Stale, and only turns after a while into an
@@ -209,29 +206,30 @@ private void onProviderEvent(FlagdProviderEvent flagdProviderEvent) {
209
206
*/
210
207
switch (flagdProviderEvent .getEvent ()) {
211
208
case PROVIDER_CONFIGURATION_CHANGED :
212
- if (eventsLock . previousEvent == ProviderEvent .PROVIDER_READY ) {
209
+ if (syncResources . getPreviousEvent () == ProviderEvent .PROVIDER_READY ) {
213
210
onConfigurationChanged (flagdProviderEvent );
214
211
break ;
215
212
}
216
- // intentional fall through, a not-ready change will trigger a ready.
213
+ // intentional fall through
217
214
case PROVIDER_READY :
218
215
/*
219
216
* Sync metadata is used to enrich the context, and is immutable in flagd,
220
217
* so we only need it to be fetched once at READY.
221
218
*/
222
219
if (flagdProviderEvent .getSyncMetadata () != null ) {
223
- eventsLock . enrichedContext = contextEnricher .apply (flagdProviderEvent .getSyncMetadata ());
220
+ syncResources . setEnrichedContext ( contextEnricher .apply (flagdProviderEvent .getSyncMetadata () ));
224
221
}
225
222
onReady ();
226
- eventsLock . previousEvent = ProviderEvent .PROVIDER_READY ;
223
+ syncResources . setPreviousEvent ( ProviderEvent .PROVIDER_READY ) ;
227
224
break ;
228
225
229
226
case PROVIDER_ERROR :
230
- if (eventsLock . previousEvent != ProviderEvent .PROVIDER_ERROR ) {
227
+ if (syncResources . getPreviousEvent () != ProviderEvent .PROVIDER_ERROR ) {
231
228
onError ();
229
+ syncResources .setPreviousEvent (ProviderEvent .PROVIDER_ERROR );
232
230
}
233
- eventsLock .previousEvent = ProviderEvent .PROVIDER_ERROR ;
234
231
break ;
232
+
235
233
default :
236
234
log .info ("Unknown event {}" , flagdProviderEvent .getEvent ());
237
235
}
@@ -246,8 +244,7 @@ private void onConfigurationChanged(FlagdProviderEvent flagdProviderEvent) {
246
244
}
247
245
248
246
private void onReady () {
249
- if (!eventsLock .initialized ) {
250
- eventsLock .initialized = true ;
247
+ if (syncResources .initialize ()) {
251
248
log .info ("initialized FlagdProvider" );
252
249
}
253
250
if (errorTask != null && !errorTask .isCancelled ()) {
@@ -272,7 +269,7 @@ private void onError() {
272
269
if (!errorExecutor .isShutdown ()) {
273
270
errorTask = errorExecutor .schedule (
274
271
() -> {
275
- if (eventsLock . previousEvent == ProviderEvent .PROVIDER_ERROR ) {
272
+ if (syncResources . getPreviousEvent () == ProviderEvent .PROVIDER_ERROR ) {
276
273
log .debug (
277
274
"Provider did not reconnect successfully within {}s. Emit ERROR event..." ,
278
275
gracePeriod );
@@ -286,14 +283,4 @@ private void onError() {
286
283
TimeUnit .SECONDS );
287
284
}
288
285
}
289
-
290
- /**
291
- * Contains all fields we need to worry about locking, used as intrinsic lock
292
- * for sync blocks.
293
- */
294
- static class EventsLock {
295
- volatile ProviderEvent previousEvent = null ;
296
- volatile boolean initialized = false ;
297
- volatile EvaluationContext enrichedContext = new ImmutableContext ();
298
- }
299
286
}
0 commit comments