22
33import dev .openfeature .contrib .providers .flagd .resolver .Resolver ;
44import dev .openfeature .contrib .providers .flagd .resolver .common .FlagdProviderEvent ;
5- import dev .openfeature .contrib .providers .flagd .resolver .common .Util ;
65import dev .openfeature .contrib .providers .flagd .resolver .grpc .GrpcResolver ;
76import dev .openfeature .contrib .providers .flagd .resolver .grpc .cache .Cache ;
87import dev .openfeature .contrib .providers .flagd .resolver .process .InProcessResolver ;
98import dev .openfeature .sdk .EvaluationContext ;
109import dev .openfeature .sdk .EventProvider ;
1110import dev .openfeature .sdk .Hook ;
12- import dev .openfeature .sdk .ImmutableContext ;
1311import dev .openfeature .sdk .Metadata ;
1412import dev .openfeature .sdk .ProviderEvaluation ;
1513import dev .openfeature .sdk .ProviderEvent ;
@@ -36,7 +34,7 @@ public class FlagdProvider extends EventProvider {
3634 private static final String FLAGD_PROVIDER = "flagd" ;
3735 private final Resolver flagResolver ;
3836 private final List <Hook > hooks = new ArrayList <>();
39- private final EventsLock eventsLock = new EventsLock ();
37+ private final FlagdProviderSyncResources syncResources = new FlagdProviderSyncResources ();
4038
4139 /**
4240 * An executor service responsible for emitting
@@ -108,7 +106,9 @@ public FlagdProvider(final FlagdOptions options) {
108106 gracePeriod = Config .DEFAULT_STREAM_RETRY_GRACE_PERIOD ;
109107 hooks .add (new SyncMetadataHook (this ::getEnrichedContext ));
110108 errorExecutor = Executors .newSingleThreadScheduledExecutor ();
111- this .eventsLock .initialized = initialized ;
109+ if (initialized ) {
110+ this .syncResources .initialize ();
111+ }
112112 }
113113
114114 @ Override
@@ -118,28 +118,27 @@ public List<Hook> getProviderHooks() {
118118
119119 @ Override
120120 public void initialize (EvaluationContext evaluationContext ) throws Exception {
121- synchronized (eventsLock ) {
122- if (eventsLock . initialized ) {
121+ synchronized (syncResources ) {
122+ if (syncResources . isInitialized () ) {
123123 return ;
124124 }
125125
126126 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 );
127131 }
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 );
134132 }
135133
136134 @ Override
137135 public void shutdown () {
138- synchronized (eventsLock ) {
139- if (!eventsLock .initialized ) {
140- return ;
141- }
136+ synchronized (syncResources ) {
142137 try {
138+ if (!syncResources .isInitialized () || syncResources .isShutDown ()) {
139+ return ;
140+ }
141+
143142 this .flagResolver .shutdown ();
144143 if (errorExecutor != null ) {
145144 errorExecutor .shutdownNow ();
@@ -148,7 +147,7 @@ public void shutdown() {
148147 } catch (Exception e ) {
149148 log .error ("Error during shutdown {}" , FLAGD_PROVIDER , e );
150149 } finally {
151- eventsLock . initialized = false ;
150+ syncResources . shutdown () ;
152151 }
153152 }
154153 }
@@ -189,15 +188,13 @@ public ProviderEvaluation<Value> getObjectEvaluation(String key, Value defaultVa
189188 * @return context
190189 */
191190 EvaluationContext getEnrichedContext () {
192- return eventsLock . enrichedContext ;
191+ return syncResources . getEnrichedContext () ;
193192 }
194193
195194 @ SuppressWarnings ("checkstyle:fallthrough" )
196195 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 ) {
201198 /*
202199 * We only use Error and Ready as previous states.
203200 * 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) {
209206 */
210207 switch (flagdProviderEvent .getEvent ()) {
211208 case PROVIDER_CONFIGURATION_CHANGED :
212- if (eventsLock . previousEvent == ProviderEvent .PROVIDER_READY ) {
209+ if (syncResources . getPreviousEvent () == ProviderEvent .PROVIDER_READY ) {
213210 onConfigurationChanged (flagdProviderEvent );
214211 break ;
215212 }
216- // intentional fall through, a not-ready change will trigger a ready.
213+ // intentional fall through
217214 case PROVIDER_READY :
218215 /*
219216 * Sync metadata is used to enrich the context, and is immutable in flagd,
220217 * so we only need it to be fetched once at READY.
221218 */
222219 if (flagdProviderEvent .getSyncMetadata () != null ) {
223- eventsLock . enrichedContext = contextEnricher .apply (flagdProviderEvent .getSyncMetadata ());
220+ syncResources . setEnrichedContext ( contextEnricher .apply (flagdProviderEvent .getSyncMetadata () ));
224221 }
225222 onReady ();
226- eventsLock . previousEvent = ProviderEvent .PROVIDER_READY ;
223+ syncResources . setPreviousEvent ( ProviderEvent .PROVIDER_READY ) ;
227224 break ;
228225
229226 case PROVIDER_ERROR :
230- if (eventsLock . previousEvent != ProviderEvent .PROVIDER_ERROR ) {
227+ if (syncResources . getPreviousEvent () != ProviderEvent .PROVIDER_ERROR ) {
231228 onError ();
229+ syncResources .setPreviousEvent (ProviderEvent .PROVIDER_ERROR );
232230 }
233- eventsLock .previousEvent = ProviderEvent .PROVIDER_ERROR ;
234231 break ;
232+
235233 default :
236234 log .info ("Unknown event {}" , flagdProviderEvent .getEvent ());
237235 }
@@ -246,8 +244,7 @@ private void onConfigurationChanged(FlagdProviderEvent flagdProviderEvent) {
246244 }
247245
248246 private void onReady () {
249- if (!eventsLock .initialized ) {
250- eventsLock .initialized = true ;
247+ if (syncResources .initialize ()) {
251248 log .info ("initialized FlagdProvider" );
252249 }
253250 if (errorTask != null && !errorTask .isCancelled ()) {
@@ -272,7 +269,7 @@ private void onError() {
272269 if (!errorExecutor .isShutdown ()) {
273270 errorTask = errorExecutor .schedule (
274271 () -> {
275- if (eventsLock . previousEvent == ProviderEvent .PROVIDER_ERROR ) {
272+ if (syncResources . getPreviousEvent () == ProviderEvent .PROVIDER_ERROR ) {
276273 log .debug (
277274 "Provider did not reconnect successfully within {}s. Emit ERROR event..." ,
278275 gracePeriod );
@@ -286,14 +283,4 @@ private void onError() {
286283 TimeUnit .SECONDS );
287284 }
288285 }
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- }
299286}
0 commit comments