Skip to content

feat(gofeatureflag): Add support of flag change cache removal #821

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jun 19, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions providers/go-feature-flag/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
<version>2.0.13</version>
</dependency>

<dependency>
<groupId>io.reactivex.rxjava3</groupId>
<artifactId>rxjava</artifactId>
<version>3.1.8</version>
</dependency>

<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j2-impl</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,5 @@
@Getter
public class EvaluationResponse<T> {
private ProviderEvaluation<T> providerEvaluation;
private Boolean cachable;
private Boolean cacheable;
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,11 @@ public class GoFeatureFlagProviderOptions {
/**
* (optional) If cache custom configuration is wanted, you should provide
* a cache builder.
* Default: null
* Default:
* CACHE_TTL_MS: 5min
* CACHE_CONCURRENCY_LEVEL: 1
* CACHE_INITIAL_CAPACITY: 100
* CACHE_MAXIMUM_SIZE: 100000
*/
private CacheBuilder<String, ProviderEvaluation<?>> cacheBuilder;

Expand All @@ -70,8 +74,23 @@ public class GoFeatureFlagProviderOptions {

/**
* (optional) max pending events aggregated before publishing for collection data to the proxy.
* When event is added while events collection is full, event is omitted.
* When an event is added while an events collection is full, the event is omitted.
* default: 10000
*/
private Integer maxPendingEvents;

/**
* (optional) interval time we poll the proxy to check if the configuration has changed.
* If the cache is enabled, we will poll the relay-proxy every X milliseconds
* to check if the configuration has changed.
* default: 120000
*/
private Long flagChangePollingIntervalMs;

/**
* (optional) disableDataCollection set to true if you don't want to collect the usage of
* flags retrieved in the cache.
* default: false
*/
private boolean disableDataCollection;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import dev.openfeature.sdk.EvaluationContext;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

Expand All @@ -13,7 +14,7 @@ public class BeanUtils {

private static final ObjectMapper objectMapper = new ObjectMapper();

public static String buildKey(GoFeatureFlagUser goFeatureFlagUser) throws JsonProcessingException {
return objectMapper.writeValueAsString(goFeatureFlagUser);
public static String buildKey(EvaluationContext evaluationContext) throws JsonProcessingException {
return objectMapper.writeValueAsString(evaluationContext);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dev.openfeature.contrib.providers.gofeatureflag.bean;

/**
* ConfigurationChange is an enum to represent the change of the configuration.
*/
public enum ConfigurationChange {
FLAG_CONFIGURATION_INITIALIZED,
FLAG_CONFIGURATION_UPDATED,
FLAG_CONFIGURATION_NOT_CHANGED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package dev.openfeature.contrib.providers.gofeatureflag.controller;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import dev.openfeature.contrib.providers.gofeatureflag.GoFeatureFlagProviderOptions;
import dev.openfeature.contrib.providers.gofeatureflag.bean.BeanUtils;
import dev.openfeature.sdk.EvaluationContext;
import dev.openfeature.sdk.ProviderEvaluation;
import lombok.Builder;

import java.time.Duration;

/**
* CacheController is a controller to manage the cache of the provider.
*/
public class CacheController {
public static final long DEFAULT_CACHE_TTL_MS = 5L * 60L * 1000L;
public static final int DEFAULT_CACHE_CONCURRENCY_LEVEL = 1;
public static final int DEFAULT_CACHE_INITIAL_CAPACITY = 100;
public static final int DEFAULT_CACHE_MAXIMUM_SIZE = 100000;
private final Cache<String, ProviderEvaluation<?>> cache;

@Builder
public CacheController(GoFeatureFlagProviderOptions options) {
this.cache = options.getCacheBuilder() != null ? options.getCacheBuilder().build() : buildDefaultCache();
}

private Cache<String, ProviderEvaluation<?>> buildDefaultCache() {
return CacheBuilder.newBuilder()
.concurrencyLevel(DEFAULT_CACHE_CONCURRENCY_LEVEL)
.initialCapacity(DEFAULT_CACHE_INITIAL_CAPACITY)
.maximumSize(DEFAULT_CACHE_MAXIMUM_SIZE)
.expireAfterWrite(Duration.ofMillis(DEFAULT_CACHE_TTL_MS))
.build();
}

public void put(final String key, final EvaluationContext evaluationContext,
final ProviderEvaluation<?> providerEvaluation) throws JsonProcessingException {
this.cache.put(buildCacheKey(key, evaluationContext), providerEvaluation);
}

public ProviderEvaluation<?> getIfPresent(final String key, final EvaluationContext evaluationContext)
throws JsonProcessingException {
return this.cache.getIfPresent(buildCacheKey(key, evaluationContext));
}

public void invalidateAll() {
this.cache.invalidateAll();
}

private String buildCacheKey(String key, EvaluationContext evaluationContext) throws JsonProcessingException {
String originalKey = key + "," + BeanUtils.buildKey(evaluationContext);
int hash = originalKey.hashCode();
return String.valueOf(hash);
}
}
Loading