Skip to content

Commit

Permalink
feat: Reset the state on shutting down the flagd resolver (open-featu…
Browse files Browse the repository at this point in the history
…re#410)

Signed-off-by: Sanket Mehta <mehtasankets@gmail.com>
  • Loading branch information
mehtasankets committed Jun 20, 2024
1 parent 52bfdfe commit 7594a5d
Show file tree
Hide file tree
Showing 6 changed files with 82 additions and 4 deletions.
Empty file modified mvnw
100644 → 100755
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ public synchronized void initialize(EvaluationContext evaluationContext) throws

@Override
public synchronized void shutdown() {
if (!initialized) {
if (!this.initialized) {
return;
}

Expand Down Expand Up @@ -162,6 +162,12 @@ private void handleStateTransition(ProviderState oldState, ProviderState newStat
log.debug("Init completed");
return;
}
// we got shutdown, not checking oldState as behavior remains the same for shutdown
if (ProviderState.NOT_READY.equals(newState)) {
// nothing to do
log.debug("shutdown completed");
return;
}
// configuration changed
if (ProviderState.READY.equals(oldState) && ProviderState.READY.equals(newState)) {
log.debug("Configuration changed");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public void shutdown() throws Exception {
this.channel.awaitTermination(this.deadline, TimeUnit.MILLISECONDS);
log.warn(String.format("Unable to shut down channel by %d deadline", this.deadline));
}
this.stateConsumer.accept(ProviderState.NOT_READY);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ public void init() throws Exception {
public void shutdown() throws InterruptedException {
flagStore.shutdown();
this.connected.set(false);
stateConsumer.accept(ProviderState.NOT_READY);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.mockStatic;
import static org.mockito.Mockito.times;
Expand All @@ -16,22 +17,29 @@

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;
import java.util.concurrent.LinkedBlockingQueue;

import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;

import com.google.protobuf.Struct;

import dev.openfeature.contrib.providers.flagd.resolver.Resolver;
import dev.openfeature.contrib.providers.flagd.resolver.grpc.GrpcConnector;
import dev.openfeature.contrib.providers.flagd.resolver.grpc.GrpcResolver;
import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.Cache;
import dev.openfeature.contrib.providers.flagd.resolver.process.InProcessResolver;
import dev.openfeature.contrib.providers.flagd.resolver.process.MockStorage;
import dev.openfeature.contrib.providers.flagd.resolver.process.model.FeatureFlag;
import dev.openfeature.contrib.providers.flagd.resolver.process.storage.StorageState;
import dev.openfeature.flagd.grpc.evaluation.ServiceGrpc;
import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveBooleanRequest;
import dev.openfeature.flagd.grpc.evaluation.Evaluation.ResolveBooleanResponse;
Expand Down Expand Up @@ -824,6 +832,43 @@ void initializationAndShutdown() throws Exception{
verify(resolverMock, times(1)).shutdown();
}

@Test
void test_state_on_grpc_resolver_shutdown() throws Exception {
// setup mock provider
final FlagdProvider grpcProvider = Mockito.spy(FlagdProvider.class);
try {
doAnswer(invocation -> {
final Field stateField = FlagdProvider.class.getDeclaredField("state");
stateField.setAccessible(true);
stateField.set(grpcProvider, ProviderState.READY);

final Field intializedField = FlagdProvider.class.getDeclaredField("initialized");
intializedField.setAccessible(true);
intializedField.set(grpcProvider, true);

return null;
}).when(grpcProvider).initialize(any());
} catch (Exception e) {
throw new RuntimeException(e);
}

grpcProvider.initialize(new ImmutableContext());
assertEquals(ProviderState.READY, grpcProvider.getState());
grpcProvider.shutdown();
assertEquals(ProviderState.NOT_READY, grpcProvider.getState());
}

@Test
void test_state_on_in_process_resolver_shutdown() throws Exception {
// setup mock in-process provider
FlagdProvider inProcessProvider = createInProcessProvider();

inProcessProvider.initialize(new ImmutableContext());
assertEquals(ProviderState.READY, inProcessProvider.getState());
inProcessProvider.shutdown();
assertEquals(ProviderState.NOT_READY, inProcessProvider.getState());
}


// test helper

Expand Down Expand Up @@ -863,4 +908,29 @@ private FlagdProvider createProvider(GrpcConnector grpc, Cache cache, Supplier<P
return provider;
}

// Create an in process provider
private FlagdProvider createInProcessProvider() {

final FlagdOptions flagdOptions = FlagdOptions.builder()
.resolverType(Config.Resolver.IN_PROCESS)
.deadline(1000)
.build();
final FlagdProvider provider = new FlagdProvider(flagdOptions);
final MockStorage mockStorage = new MockStorage(new HashMap<String, FeatureFlag>(), new LinkedBlockingQueue<StorageState>(Arrays.asList(StorageState.OK)));

try {
final Field flagResolver = FlagdProvider.class.getDeclaredField("flagResolver");
flagResolver.setAccessible(true);
final Resolver resolver = (Resolver) flagResolver.get(provider);

final Field flagStore = InProcessResolver.class.getDeclaredField("flagStore");
flagStore.setAccessible(true);
flagStore.set(resolver, mockStorage);
} catch (NoSuchFieldException | IllegalAccessException e) {
throw new RuntimeException(e);
}

return provider;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,17 @@
import java.util.Map;
import java.util.concurrent.BlockingQueue;

class MockStorage implements Storage {
public class MockStorage implements Storage {

private final Map<String, FeatureFlag> mockFlags;
private final BlockingQueue<StorageState> mockQueue;

MockStorage(Map<String, FeatureFlag> mockFlags, BlockingQueue<StorageState> mockQueue) {
public MockStorage(Map<String, FeatureFlag> mockFlags, BlockingQueue<StorageState> mockQueue) {
this.mockFlags = mockFlags;
this.mockQueue = mockQueue;
}

MockStorage(Map<String, FeatureFlag> flagMap) {
public MockStorage(Map<String, FeatureFlag> flagMap) {
this.mockFlags = flagMap;
this.mockQueue = null;
}
Expand Down

0 comments on commit 7594a5d

Please sign in to comment.