diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Config.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Config.java index e22060e16..1422c5d6a 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Config.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/Config.java @@ -1,5 +1,7 @@ package dev.openfeature.contrib.providers.flagd; +import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType; + /** * Helper class to hold configuration default values. */ @@ -34,7 +36,7 @@ public final class Config { public static final String REASON_FIELD = "reason"; public static final String METADATA_FIELD = "metadata"; - public static final String LRU_CACHE = "lru"; + public static final String LRU_CACHE = CacheType.LRU.getValue(); static final String DEFAULT_CACHE = LRU_CACHE; static final int DEFAULT_MAX_EVENT_STREAM_RETRIES = 5; diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java index 418c515d4..5805dba9f 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/FlagdProvider.java @@ -2,7 +2,7 @@ import dev.openfeature.contrib.providers.flagd.resolver.Resolver; import dev.openfeature.contrib.providers.flagd.resolver.grpc.GrpcResolver; -import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheFactory; +import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.Cache; import dev.openfeature.contrib.providers.flagd.resolver.process.InProcessResolver; import dev.openfeature.sdk.EvaluationContext; import dev.openfeature.sdk.EventProvider; @@ -51,7 +51,10 @@ public FlagdProvider(final FlagdOptions options) { break; case RPC: this.flagResolver = - new GrpcResolver(options, CacheFactory.getCache(options), this::getState, this::setState); + new GrpcResolver(options, + new Cache(options.getCacheType(), options.getMaxCacheSize()), + this::getState, + this::setState); break; default: throw new IllegalStateException( diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/Cache.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/Cache.java index 057582f63..d9c8b732c 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/Cache.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/Cache.java @@ -1,41 +1,42 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc.cache; import dev.openfeature.sdk.ProviderEvaluation; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.collections4.map.LRUMap; import java.util.Collections; import java.util.Map; +import static dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType.DISABLED; +import static dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType.LRU; + /** * Exposes caching mechanism for flag evaluations. */ +@Slf4j public class Cache { - private Map> store; - private Boolean enabled = false; + private Map> store; + + @Getter + private final Boolean enabled; /** * Initialize the cache. - * @param type of cache. + * + * @param forType type of the cache. * @param maxCacheSize max amount of element to keep. */ - public Cache(CacheType type, int maxCacheSize) { - if (type == null) { - return; - } - - switch (type) { - case DISABLED: - return; - case LRU: - default: - this.store = Collections.synchronizedMap(new LRUMap<>(maxCacheSize)); + public Cache(final String forType, int maxCacheSize) { + if (DISABLED.getValue().equals(forType)) { + enabled = false; + } else if (LRU.getValue().equals(forType)) { + enabled = true; + this.store = Collections.synchronizedMap(new LRUMap<>(maxCacheSize)); + } else { + enabled = false; + log.warn(String.format("Unsupported cache type %s, continuing without cache", forType)); } - - this.enabled = true; - } - - public Boolean getEnabled() { - return this.enabled; } public void put(String key, ProviderEvaluation value) { diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheFactory.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheFactory.java deleted file mode 100644 index 1c9cc305a..000000000 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheFactory.java +++ /dev/null @@ -1,23 +0,0 @@ -package dev.openfeature.contrib.providers.flagd.resolver.grpc.cache; - -import dev.openfeature.contrib.providers.flagd.FlagdOptions; - -/** - * Factory to create a Cache. - */ -public class CacheFactory { - /** - * Factory method to initialize the cache strategy. - * @param options Options. - * @return the Cache based on the provided options. - */ - public static Cache getCache(FlagdOptions options) { - CacheType cacheType; - try { - cacheType = CacheType.valueOf(options.getCacheType()); - } catch (IllegalArgumentException e) { - cacheType = CacheType.DISABLED; - } - return new Cache(cacheType, options.getMaxCacheSize()); - } -} diff --git a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheType.java b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheType.java index 5390b089e..7c73afb95 100644 --- a/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheType.java +++ b/providers/flagd/src/main/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheType.java @@ -1,22 +1,18 @@ package dev.openfeature.contrib.providers.flagd.resolver.grpc.cache; -import static dev.openfeature.contrib.providers.flagd.Config.LRU_CACHE; +import lombok.Getter; /** * Defines which type of cache to use. */ +@Getter public enum CacheType { DISABLED("disabled"), - LRU(LRU_CACHE); + LRU("lru"); - private final String type; + private final String value; - CacheType(String type) { - this.type = type; - } - - @Override - public String toString() { - return this.type; + CacheType(String value) { + this.value = value; } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java index a5e6f3138..0f9102b13 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/FlagdProviderTest.java @@ -482,7 +482,7 @@ void invalidate_cache() { mockStaticService.when(() -> ServiceGrpc.newStub(any())) .thenReturn(serviceStubMock); - final Cache cache = new Cache(CacheType.LRU, 5); + final Cache cache = new Cache("lru", 5); grpc = new GrpcConnector(FlagdOptions.builder().build(), cache, state -> { }); } @@ -694,7 +694,7 @@ void disabled_cache() { .resolveObject(argThat(x -> FLAG_KEY_OBJECT.equals(x.getFlagKey())))).thenReturn(objectResponse); // disabled cache - final Cache cache = new Cache(CacheType.DISABLED, 0); + final Cache cache = new Cache("disabled", 0); GrpcConnector grpc; try (MockedStatic mockStaticService = mockStatic(ServiceGrpc.class)) { @@ -808,7 +808,7 @@ private FlagdProvider createProvider(GrpcConnector grpc) { // create provider with given grpc provider and state supplier private FlagdProvider createProvider(GrpcConnector grpc, Supplier getState) { - final Cache cache = new Cache(CacheType.LRU, 5); + final Cache cache = new Cache("lru", 5); return createProvider(grpc, cache, getState); } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/rpc/FlagdRpcSetup.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/rpc/FlagdRpcSetup.java index 99d40e783..44af51b2d 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/rpc/FlagdRpcSetup.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/e2e/rpc/FlagdRpcSetup.java @@ -1,5 +1,6 @@ package dev.openfeature.contrib.providers.flagd.e2e.rpc; +import dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.parallel.Isolated; @@ -19,10 +20,11 @@ public class FlagdRpcSetup { @BeforeAll() public static void setup() { FlagdRpcSetup.provider = new FlagdProvider(FlagdOptions.builder() - .resolverType(Config.Evaluator.RPC) - // set a generous deadline, to prevent timeouts in actions - .deadline(3000) - .build()); + .resolverType(Config.Evaluator.RPC) + // set a generous deadline, to prevent timeouts in actions + .deadline(3000) + .cacheType(CacheType.DISABLED.getValue()) + .build()); StepDefinitions.setProvider(provider); } } diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnectorTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnectorTest.java index 24ef799ac..dabedd875 100644 --- a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnectorTest.java +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/GrpcConnectorTest.java @@ -51,7 +51,7 @@ void validate_retry_calls(int retries) throws NoSuchFieldException, IllegalAcces .maxEventStreamRetries(retries) .build(); - final Cache cache = new Cache(CacheType.DISABLED, 0); + final Cache cache = new Cache("disabled", 0); final ServiceGrpc.ServiceStub mockStub = mock(ServiceGrpc.ServiceStub.class); doAnswer(invocation -> null).when(mockStub).eventStream(any(), any()); @@ -87,7 +87,7 @@ void validate_retry_calls(int retries) throws NoSuchFieldException, IllegalAcces @Test void initialization_succeed_with_connected_status() throws NoSuchFieldException, IllegalAccessException { - final Cache cache = new Cache(CacheType.DISABLED, 0); + final Cache cache = new Cache("disabled", 0); final ServiceGrpc.ServiceStub mockStub = mock(ServiceGrpc.ServiceStub.class); doAnswer(invocation -> null).when(mockStub).eventStream(any(), any()); @@ -111,7 +111,7 @@ void initialization_succeed_with_connected_status() throws NoSuchFieldException, @Test void initialization_fail_with_timeout() throws Exception { - final Cache cache = new Cache(CacheType.DISABLED, 0); + final Cache cache = new Cache("disabled", 0); final ServiceGrpc.ServiceStub mockStub = mock(ServiceGrpc.ServiceStub.class); doAnswer(invocation -> null).when(mockStub).eventStream(any(), any()); diff --git a/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheTest.java b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheTest.java new file mode 100644 index 000000000..26e3ea616 --- /dev/null +++ b/providers/flagd/src/test/java/dev/openfeature/contrib/providers/flagd/resolver/grpc/cache/CacheTest.java @@ -0,0 +1,50 @@ +package dev.openfeature.contrib.providers.flagd.resolver.grpc.cache; + +import dev.openfeature.sdk.ProviderEvaluation; +import org.junit.jupiter.api.Test; + +import static dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType.DISABLED; +import static dev.openfeature.contrib.providers.flagd.resolver.grpc.cache.CacheType.LRU; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class CacheTest { + + @Test + void cacheTypeTest() { + // given + final Cache disabled = new Cache(DISABLED.getValue(), 0); + final Cache lru = new Cache(LRU.getValue(), 10); + final Cache undefined = new Cache("invalid", 10); + + // then + assertTrue(lru.getEnabled()); + assertFalse(disabled.getEnabled()); + assertFalse(undefined.getEnabled()); + } + + + @Test + void lruOperationValidation() { + // given + final Cache lru = new Cache(LRU.getValue(), 1); + + // when + final ProviderEvaluation evaluation = ProviderEvaluation.builder() + .value("value") + .variant("one") + .build(); + lru.put("key", evaluation); + + // then + assertEquals(evaluation, lru.get("key")); + + // when + lru.clear(); + + // then + assertNull(lru.get("key")); + } +} \ No newline at end of file diff --git a/providers/flagd/test-harness b/providers/flagd/test-harness index 0710b7a8c..5b428c951 160000 --- a/providers/flagd/test-harness +++ b/providers/flagd/test-harness @@ -1 +1 @@ -Subproject commit 0710b7a8c8867b3976a1fad5b84da43b814d0d7b +Subproject commit 5b428c951bbcfb7aa041f195ac38400cbc6ccd75