diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
new file mode 100644
index 000000000..9e3198100
--- /dev/null
+++ b/.github/CODEOWNERS
@@ -0,0 +1 @@
+* @splitio/sdk
diff --git a/CHANGES.txt b/CHANGES.txt
index 210c2843e..ee78d8ba1 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,8 @@
+4.8.0 (Jul 18, 2023)
+- Improved streaming architecture implementation to apply feature flag updates from the notification received which is now enhanced, improving efficiency and reliability of the whole update system.
+- Updated `com.google.guava` dependence to 32.0.1 for fixing a vulnerability.
+- Updated SegmentFetcher for better readability.
+
4.7.2 (May 16, 2023)
- Updated default treatment to be control for yaml and json localhost.
- Updated terminology on the SDKs codebase to be more aligned with current standard without causing a breaking change. The core change is the term split for feature flag on things like logs and javadoc comments.
diff --git a/client/pom.xml b/client/pom.xml
index 5913349c0..4c1afd402 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -5,7 +5,7 @@
io.split.client
java-client-parent
- 4.7.2
+ 4.8.0
java-client
jar
@@ -155,7 +155,7 @@
com.google.guava
guava
- 30.0-jre
+ 32.0.1-jre
org.slf4j
diff --git a/client/src/main/java/io/split/client/SplitFactoryImpl.java b/client/src/main/java/io/split/client/SplitFactoryImpl.java
index c0c2c0dbb..ab83f8e82 100644
--- a/client/src/main/java/io/split/client/SplitFactoryImpl.java
+++ b/client/src/main/java/io/split/client/SplitFactoryImpl.java
@@ -104,7 +104,6 @@
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
-import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.stream.Collectors;
@@ -115,8 +114,6 @@ public class SplitFactoryImpl implements SplitFactory {
private final static long SSE_CONNECT_TIMEOUT = 30000;
private final static long SSE_SOCKET_TIMEOUT = 70000;
- private static Random RANDOM = new Random();
-
private final SDKReadinessGates _gates;
private final ImpressionsManager _impressionsManager;
private final Evaluator _evaluator;
@@ -152,7 +149,6 @@ public class SplitFactoryImpl implements SplitFactory {
private final URI _eventsRootTarget;
private final UniqueKeysTracker _uniqueKeysTracker;
-
//Constructor for standalone mode
public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyntaxException {
_userStorageWrapper = null;
@@ -188,13 +184,14 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
ImpressionsStorage impressionsStorage = new InMemoryImpressionsStorage(config.impressionsQueueSize());
_splitCache = splitCache;
_segmentCache = segmentCache;
- _telemetrySynchronizer = new TelemetryInMemorySubmitter(_httpclient, URI.create(config.telemetryURL()), telemetryStorage, splitCache, segmentCache, telemetryStorage, _startTime);
+ _telemetrySynchronizer = new TelemetryInMemorySubmitter(_httpclient, URI.create(config.telemetryURL()), telemetryStorage, splitCache, _segmentCache, telemetryStorage, _startTime);
// Segments
_segmentSynchronizationTaskImp = buildSegments(config, segmentCache, splitCache);
+ SplitParser splitParser = new SplitParser();
// SplitFetcher
- _splitFetcher = buildSplitFetcher(splitCache, splitCache);
+ _splitFetcher = buildSplitFetcher(splitCache, splitParser);
// SplitSynchronizationTask
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher,
@@ -241,7 +238,7 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
SplitAPI splitAPI = SplitAPI.build(_httpclient, buildSSEdHttpClient(apiToken, config, _sdkMetadata));
_syncManager = SyncManagerImp.build(splitTasks, _splitFetcher, splitCache, splitAPI,
- segmentCache, _gates, _telemetryStorageProducer, _telemetrySynchronizer, config);
+ segmentCache, _gates, _telemetryStorageProducer, _telemetrySynchronizer, config, splitParser);
_syncManager.start();
// DestroyOnShutDown
@@ -255,7 +252,6 @@ public SplitFactoryImpl(String apiToken, SplitClientConfig config) throws URISyn
}
}
-
//Constructor for consumer mode
protected SplitFactoryImpl(String apiToken, SplitClientConfig config, CustomStorageWrapper customStorageWrapper) throws URISyntaxException {
//Variables that are not used in Consumer mode.
@@ -378,7 +374,7 @@ protected SplitFactoryImpl(SplitClientConfig config) {
SplitParser splitParser = new SplitParser();
- _splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, _splitCache, splitCache, _telemetryStorageProducer);
+ _splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCache, _telemetryStorageProducer);
// SplitSynchronizationTask
_splitSynchronizationTask = new SplitSynchronizationTask(_splitFetcher, splitCache, config.featuresRefreshRate(), config.getThreadFactory());
@@ -559,11 +555,10 @@ private SegmentSynchronizationTaskImp buildSegments(SplitClientConfig config, Se
config.getThreadFactory());
}
- private SplitFetcher buildSplitFetcher(SplitCacheConsumer splitCacheConsumer, SplitCacheProducer splitCacheProducer) throws URISyntaxException {
+ private SplitFetcher buildSplitFetcher(SplitCacheProducer splitCacheProducer, SplitParser splitParser) throws URISyntaxException {
SplitChangeFetcher splitChangeFetcher = HttpSplitChangeFetcher.create(_httpclient, _rootTarget, _telemetryStorageProducer);
- SplitParser splitParser = new SplitParser();
- return new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheConsumer, splitCacheProducer, _telemetryStorageProducer);
+ return new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, _telemetryStorageProducer);
}
private ImpressionsManagerImpl buildImpressionsManager(SplitClientConfig config, ImpressionsStorageConsumer impressionsStorageConsumer, ImpressionsStorageProducer impressionsStorageProducer) throws URISyntaxException {
diff --git a/client/src/main/java/io/split/client/jmx/JmxMonitor.java b/client/src/main/java/io/split/client/jmx/JmxMonitor.java
index ce2e3ff48..0acd7deda 100644
--- a/client/src/main/java/io/split/client/jmx/JmxMonitor.java
+++ b/client/src/main/java/io/split/client/jmx/JmxMonitor.java
@@ -130,4 +130,4 @@ private String getContextPath() {
return null;
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/client/jmx/SplitJmxMonitor.java b/client/src/main/java/io/split/client/jmx/SplitJmxMonitor.java
index 052d726ad..6f4e0522c 100644
--- a/client/src/main/java/io/split/client/jmx/SplitJmxMonitor.java
+++ b/client/src/main/java/io/split/client/jmx/SplitJmxMonitor.java
@@ -69,4 +69,4 @@ public String fetchDefinition(String featureName) {
public boolean isKeyInSegment(String key, String segmentName) {
return segmentCacheConsumer.isInSegment(segmentName, key);
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/client/jmx/SplitJmxMonitorMBean.java b/client/src/main/java/io/split/client/jmx/SplitJmxMonitorMBean.java
index 6b492043d..94d1728bf 100644
--- a/client/src/main/java/io/split/client/jmx/SplitJmxMonitorMBean.java
+++ b/client/src/main/java/io/split/client/jmx/SplitJmxMonitorMBean.java
@@ -36,4 +36,4 @@ public interface SplitJmxMonitorMBean {
*/
boolean isKeyInSegment(String key, String segmentName);
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/client/utils/FeatureFlagProcessor.java b/client/src/main/java/io/split/client/utils/FeatureFlagProcessor.java
new file mode 100644
index 000000000..2234f0a20
--- /dev/null
+++ b/client/src/main/java/io/split/client/utils/FeatureFlagProcessor.java
@@ -0,0 +1,38 @@
+package io.split.client.utils;
+
+import io.split.client.dtos.Split;
+import io.split.client.dtos.Status;
+import io.split.engine.experiments.ParsedSplit;
+import io.split.engine.experiments.SplitParser;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+public class FeatureFlagProcessor {
+ private static final Logger _log = LoggerFactory.getLogger(FeatureFlagProcessor.class);
+
+ public static FeatureFlagsToUpdate processFeatureFlagChanges(SplitParser splitParser, List splits) {
+ List toAdd = new ArrayList<>();
+ List toRemove = new ArrayList<>();
+ Set segments = new HashSet<>();
+ for (Split split : splits) {
+ if (split.status != Status.ACTIVE) {
+ // archive.
+ toRemove.add(split.name);
+ continue;
+ }
+ ParsedSplit parsedSplit = splitParser.parse(split);
+ if (parsedSplit == null) {
+ _log.debug(String.format("We could not parse the feature flag definition for: %s", split.name));
+ continue;
+ }
+ segments.addAll(parsedSplit.getSegmentsNames());
+ toAdd.add(parsedSplit);
+ }
+ return new FeatureFlagsToUpdate(toAdd, toRemove, segments);
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/client/utils/FeatureFlagsToUpdate.java b/client/src/main/java/io/split/client/utils/FeatureFlagsToUpdate.java
new file mode 100644
index 000000000..3eb03c7b5
--- /dev/null
+++ b/client/src/main/java/io/split/client/utils/FeatureFlagsToUpdate.java
@@ -0,0 +1,30 @@
+package io.split.client.utils;
+
+import io.split.engine.experiments.ParsedSplit;
+
+import java.util.List;
+import java.util.Set;
+
+public class FeatureFlagsToUpdate {
+ List toAdd;
+ List toRemove;
+ Set segments;
+
+ public FeatureFlagsToUpdate(List toAdd, List toRemove, Set segments) {
+ this.toAdd = toAdd;
+ this.toRemove = toRemove;
+ this.segments = segments;
+ }
+
+ public List getToAdd() {
+ return toAdd;
+ }
+
+ public List getToRemove() {
+ return toRemove;
+ }
+
+ public Set getSegments() {
+ return segments;
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/common/ConsumerSynchronizer.java b/client/src/main/java/io/split/engine/common/ConsumerSynchronizer.java
index 6b4e6b9f3..7bcee43a5 100644
--- a/client/src/main/java/io/split/engine/common/ConsumerSynchronizer.java
+++ b/client/src/main/java/io/split/engine/common/ConsumerSynchronizer.java
@@ -2,6 +2,7 @@
import io.split.client.impressions.ImpressionsManager;
import io.split.client.impressions.UniqueKeysTracker;
+import io.split.engine.sse.dtos.SplitKillNotification;
import io.split.telemetry.synchronizer.TelemetrySyncTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -39,7 +40,7 @@ public void refreshSplits(Long targetChangeNumber) {
}
@Override
- public void localKillSplit(String featureFlagName, String defaultTreatment, long newChangeNumber) {
+ public void localKillSplit(SplitKillNotification splitKillNotification) {
//No-Op
}
@@ -80,4 +81,9 @@ public void stopPeriodicDataRecording() {
_telemetrySyncTask.stopScheduledTask();
_log.info("Successful shutdown of telemetry sync task");
}
-}
+
+ @Override
+ public void forceRefreshSegment(String segmentName) {
+ //No-Op
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/common/LocalhostSynchronizer.java b/client/src/main/java/io/split/engine/common/LocalhostSynchronizer.java
index a8b4cf312..e92151846 100644
--- a/client/src/main/java/io/split/engine/common/LocalhostSynchronizer.java
+++ b/client/src/main/java/io/split/engine/common/LocalhostSynchronizer.java
@@ -5,6 +5,7 @@
import io.split.engine.experiments.SplitSynchronizationTask;
import io.split.engine.segments.SegmentFetcher;
import io.split.engine.segments.SegmentSynchronizationTask;
+import io.split.engine.sse.dtos.SplitKillNotification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -66,7 +67,7 @@ public void refreshSplits(Long targetChangeNumber) {
}
@Override
- public void localKillSplit(String featureFlagName, String defaultTreatment, long newChangeNumber) {
+ public void localKillSplit(SplitKillNotification splitKillNotification) {
//No-Op
}
@@ -85,4 +86,9 @@ public void startPeriodicDataRecording() {
public void stopPeriodicDataRecording() {
//No-Op
}
+
+ @Override
+ public void forceRefreshSegment(String segmentName) {
+ //No-Op
+ }
}
diff --git a/client/src/main/java/io/split/engine/common/PushManagerImp.java b/client/src/main/java/io/split/engine/common/PushManagerImp.java
index 61fff9a1e..5fb423f0a 100644
--- a/client/src/main/java/io/split/engine/common/PushManagerImp.java
+++ b/client/src/main/java/io/split/engine/common/PushManagerImp.java
@@ -1,6 +1,7 @@
package io.split.engine.common;
import com.google.common.annotations.VisibleForTesting;
+import io.split.engine.experiments.SplitParser;
import io.split.engine.sse.AuthApiClient;
import io.split.engine.sse.AuthApiClientImp;
import io.split.engine.sse.EventSourceClient;
@@ -11,10 +12,11 @@
import io.split.engine.sse.dtos.AuthenticationResponse;
import io.split.engine.sse.dtos.SegmentQueueDto;
import io.split.engine.sse.workers.SegmentsWorkerImp;
-import io.split.engine.sse.workers.SplitsWorker;
-import io.split.engine.sse.workers.SplitsWorkerImp;
+import io.split.engine.sse.workers.FeatureFlagsWorker;
+import io.split.engine.sse.workers.FeatureFlagWorkerImp;
import io.split.engine.sse.workers.Worker;
+import io.split.storages.SplitCacheProducer;
import io.split.telemetry.domain.StreamingEvent;
import io.split.telemetry.domain.enums.StreamEventsEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
@@ -36,7 +38,7 @@ public class PushManagerImp implements PushManager {
private final AuthApiClient _authApiClient;
private final EventSourceClient _eventSourceClient;
- private final SplitsWorker _splitsWorker;
+ private final FeatureFlagsWorker _featureFlagsWorker;
private final Worker _segmentWorker;
private final PushStatusTracker _pushStatusTracker;
@@ -48,7 +50,7 @@ public class PushManagerImp implements PushManager {
@VisibleForTesting
/* package private */ PushManagerImp(AuthApiClient authApiClient,
EventSourceClient eventSourceClient,
- SplitsWorker splitsWorker,
+ FeatureFlagsWorker featureFlagsWorker,
Worker segmentWorker,
PushStatusTracker pushStatusTracker,
TelemetryRuntimeProducer telemetryRuntimeProducer,
@@ -56,7 +58,7 @@ public class PushManagerImp implements PushManager {
_authApiClient = checkNotNull(authApiClient);
_eventSourceClient = checkNotNull(eventSourceClient);
- _splitsWorker = splitsWorker;
+ _featureFlagsWorker = featureFlagsWorker;
_segmentWorker = segmentWorker;
_pushStatusTracker = pushStatusTracker;
_expirationTime = new AtomicLong();
@@ -70,13 +72,15 @@ public static PushManagerImp build(Synchronizer synchronizer,
SplitAPI splitAPI,
LinkedBlockingQueue statusMessages,
TelemetryRuntimeProducer telemetryRuntimeProducer,
- ThreadFactory threadFactory) {
- SplitsWorker splitsWorker = new SplitsWorkerImp(synchronizer);
+ ThreadFactory threadFactory,
+ SplitParser splitParser,
+ SplitCacheProducer splitCacheProducer) {
+ FeatureFlagsWorker featureFlagsWorker = new FeatureFlagWorkerImp(synchronizer, splitParser, splitCacheProducer, telemetryRuntimeProducer);
Worker segmentWorker = new SegmentsWorkerImp(synchronizer);
PushStatusTracker pushStatusTracker = new PushStatusTrackerImp(statusMessages, telemetryRuntimeProducer);
return new PushManagerImp(new AuthApiClientImp(authUrl, splitAPI.getHttpClient(), telemetryRuntimeProducer),
- EventSourceClientImp.build(streamingUrl, splitsWorker, segmentWorker, pushStatusTracker, splitAPI.getSseHttpClient(), telemetryRuntimeProducer, threadFactory),
- splitsWorker,
+ EventSourceClientImp.build(streamingUrl, featureFlagsWorker, segmentWorker, pushStatusTracker, splitAPI.getSseHttpClient(), telemetryRuntimeProducer, threadFactory),
+ featureFlagsWorker,
segmentWorker,
pushStatusTracker,
telemetryRuntimeProducer,
@@ -134,13 +138,13 @@ private boolean startSse(String token, String channels) {
@Override
public synchronized void startWorkers() {
- _splitsWorker.start();
+ _featureFlagsWorker.start();
_segmentWorker.start();
}
@Override
public synchronized void stopWorkers() {
- _splitsWorker.stop();
+ _featureFlagsWorker.stop();
_segmentWorker.stop();
}
}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/common/SyncManagerImp.java b/client/src/main/java/io/split/engine/common/SyncManagerImp.java
index d739d40ab..bc23c96b1 100644
--- a/client/src/main/java/io/split/engine/common/SyncManagerImp.java
+++ b/client/src/main/java/io/split/engine/common/SyncManagerImp.java
@@ -5,6 +5,7 @@
import io.split.client.SplitClientConfig;
import io.split.engine.SDKReadinessGates;
import io.split.engine.experiments.SplitFetcher;
+import io.split.engine.experiments.SplitParser;
import io.split.engine.experiments.SplitSynchronizationTask;
import io.split.engine.segments.SegmentSynchronizationTask;
import io.split.storages.SegmentCacheProducer;
@@ -85,7 +86,8 @@ public static SyncManagerImp build(SplitTasks splitTasks,
SDKReadinessGates gates,
TelemetryRuntimeProducer telemetryRuntimeProducer,
TelemetrySynchronizer telemetrySynchronizer,
- SplitClientConfig config) {
+ SplitClientConfig config,
+ SplitParser splitParser) {
LinkedBlockingQueue pushMessages = new LinkedBlockingQueue<>();
Synchronizer synchronizer = new SynchronizerImp(splitTasks,
splitFetcher,
@@ -102,7 +104,9 @@ public static SyncManagerImp build(SplitTasks splitTasks,
splitAPI,
pushMessages,
telemetryRuntimeProducer,
- config.getThreadFactory());
+ config.getThreadFactory(),
+ splitParser,
+ splitCacheProducer);
return new SyncManagerImp(splitTasks,
config.streamingEnabled(),
diff --git a/client/src/main/java/io/split/engine/common/Synchronizer.java b/client/src/main/java/io/split/engine/common/Synchronizer.java
index 20dcae138..8885e8b16 100644
--- a/client/src/main/java/io/split/engine/common/Synchronizer.java
+++ b/client/src/main/java/io/split/engine/common/Synchronizer.java
@@ -1,12 +1,15 @@
package io.split.engine.common;
+import io.split.engine.sse.dtos.SplitKillNotification;
+
public interface Synchronizer {
boolean syncAll();
void startPeriodicFetching();
void stopPeriodicFetching();
void refreshSplits(Long targetChangeNumber);
- void localKillSplit(String featureFlagName, String defaultTreatment, long newChangeNumber);
+ void localKillSplit(SplitKillNotification splitKillNotification);
void refreshSegment(String segmentName, Long targetChangeNumber);
void startPeriodicDataRecording();
void stopPeriodicDataRecording();
+ void forceRefreshSegment(String segmentName);
}
diff --git a/client/src/main/java/io/split/engine/common/SynchronizerImp.java b/client/src/main/java/io/split/engine/common/SynchronizerImp.java
index 71cad55f1..1e308dccd 100644
--- a/client/src/main/java/io/split/engine/common/SynchronizerImp.java
+++ b/client/src/main/java/io/split/engine/common/SynchronizerImp.java
@@ -9,6 +9,7 @@
import io.split.engine.experiments.SplitSynchronizationTask;
import io.split.engine.segments.SegmentFetcher;
import io.split.engine.segments.SegmentSynchronizationTask;
+import io.split.engine.sse.dtos.SplitKillNotification;
import io.split.storages.SegmentCacheProducer;
import io.split.storages.SplitCacheProducer;
import io.split.telemetry.synchronizer.TelemetrySyncTask;
@@ -183,10 +184,10 @@ public void refreshSplits(Long targetChangeNumber) {
}
@Override
- public void localKillSplit(String featureFlagName, String defaultTreatment, long newChangeNumber) {
- if (newChangeNumber > _splitCacheProducer.getChangeNumber()) {
- _splitCacheProducer.kill(featureFlagName, defaultTreatment, newChangeNumber);
- refreshSplits(newChangeNumber);
+ public void localKillSplit(SplitKillNotification splitKillNotification) {
+ if (splitKillNotification.getChangeNumber() > _splitCacheProducer.getChangeNumber()) {
+ _splitCacheProducer.kill(splitKillNotification.getSplitName(), splitKillNotification.getDefaultTreatment(), splitKillNotification.getChangeNumber());
+ refreshSplits(splitKillNotification.getChangeNumber());
}
}
@@ -303,8 +304,9 @@ public void stopPeriodicDataRecording() {
_log.info("Successful shutdown of telemetry sync task");
}
- private void forceRefreshSegment(String segmentName){
+ @Override
+ public void forceRefreshSegment(String segmentName){
SegmentFetcher segmentFetcher = _segmentSynchronizationTaskImp.getFetcher(segmentName);
segmentFetcher.fetch(new FetchOptions.Builder().build());
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java b/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java
index 0afef5cc1..5eb81bda8 100644
--- a/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java
+++ b/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java
@@ -1,9 +1,7 @@
package io.split.engine.experiments;
-import io.split.client.dtos.Split;
import io.split.client.dtos.SplitChange;
-import io.split.client.dtos.Status;
-import io.split.storages.SplitCacheConsumer;
+import io.split.client.utils.FeatureFlagsToUpdate;
import io.split.storages.SplitCacheProducer;
import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
@@ -11,12 +9,11 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.util.ArrayList;
import java.util.HashSet;
-import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkNotNull;
+import static io.split.client.utils.FeatureFlagProcessor.processFeatureFlagChanges;
/**
* An ExperimentFetcher that refreshes experiment definitions periodically.
@@ -29,7 +26,6 @@ public class SplitFetcherImp implements SplitFetcher {
private final SplitParser _parser;
private final SplitChangeFetcher _splitChangeFetcher;
- private final SplitCacheConsumer _splitCacheConsumer;
private final SplitCacheProducer _splitCacheProducer;
private final Object _lock = new Object();
private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
@@ -44,10 +40,9 @@ public class SplitFetcherImp implements SplitFetcher {
* an ARCHIVED split is received, we know if we need to remove a traffic type from the multiset.
*/
- public SplitFetcherImp(SplitChangeFetcher splitChangeFetcher, SplitParser parser, SplitCacheConsumer splitCacheConsumer, SplitCacheProducer splitCacheProducer, TelemetryRuntimeProducer telemetryRuntimeProducer) {
+ public SplitFetcherImp(SplitChangeFetcher splitChangeFetcher, SplitParser parser, SplitCacheProducer splitCacheProducer, TelemetryRuntimeProducer telemetryRuntimeProducer) {
_splitChangeFetcher = checkNotNull(splitChangeFetcher);
_parser = checkNotNull(parser);
- _splitCacheConsumer = checkNotNull(splitCacheConsumer);
_splitCacheProducer = checkNotNull(splitCacheProducer);
_telemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
}
@@ -119,48 +114,11 @@ private Set runWithoutExceptionHandling(FetchOptions options) throws Int
return segments;
}
- List parsedSplits = new ArrayList<>();
- for (Split split : change.splits) {
- if (Thread.currentThread().isInterrupted()) {
- throw new InterruptedException();
- }
-
- if (split.status != Status.ACTIVE) {
- // archive.
- _splitCacheProducer.remove(split.name);
- continue;
- }
-
- ParsedSplit parsedSplit = _parser.parse(split);
- if (parsedSplit == null) {
- _log.info(String.format("We could not parse the experiment definition for: %s so we are removing it completely to be careful", split.name));
-
- _splitCacheProducer.remove(split.name);
- _log.debug("Deleted feature: " + split.name);
-
- continue;
- }
- segments.addAll(parsedSplit.getSegmentsNames());
-
- // If the split already exists, this is either an update, or the split has been
- // deleted and recreated (possibly with a different traffic type).
- // If it's an update, the traffic type should NOT be increased.
- // If it's deleted & recreated, the old one should be decreased and the new one increased.
- // To handle both cases, we simply delete the old one if the split is present.
- // The new one is always increased.
- ParsedSplit current = _splitCacheConsumer.get(split.name); // TODO (lecheverz): implement UPDATE method at Split Cache
- if (current != null) {
- _splitCacheProducer.remove(split.name);
- }
-
- parsedSplits.add(parsedSplit);
- _log.debug("Updated feature: " + parsedSplit.feature());
- }
-
- _splitCacheProducer.putMany(parsedSplits);
- _splitCacheProducer.setChangeNumber(change.till);
+ FeatureFlagsToUpdate featureFlagsToUpdate = processFeatureFlagChanges(_parser, change.splits);
+ segments = featureFlagsToUpdate.getSegments();
+ _splitCacheProducer.update(featureFlagsToUpdate.getToAdd(), featureFlagsToUpdate.getToRemove(), change.till);
_telemetryRuntimeProducer.recordSuccessfulSync(LastSynchronizationRecordsEnum.SPLITS, System.currentTimeMillis());
}
return segments;
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/experiments/SplitParser.java b/client/src/main/java/io/split/engine/experiments/SplitParser.java
index 7b521175e..eab521212 100644
--- a/client/src/main/java/io/split/engine/experiments/SplitParser.java
+++ b/client/src/main/java/io/split/engine/experiments/SplitParser.java
@@ -6,7 +6,6 @@
import io.split.client.dtos.MatcherGroup;
import io.split.client.dtos.Partition;
import io.split.client.dtos.Split;
-import io.split.client.dtos.Status;
import io.split.engine.matchers.AllKeysMatcher;
import io.split.engine.matchers.AttributeMatcher;
import io.split.engine.matchers.BetweenMatcher;
@@ -56,10 +55,6 @@ public ParsedSplit parse(Split split) {
}
private ParsedSplit parseWithoutExceptionHandling(Split split) {
- if (split.status != Status.ACTIVE) {
- return null;
- }
-
List parsedConditionList = Lists.newArrayList();
for (Condition condition : split.conditions) {
@@ -176,6 +171,4 @@ private AttributeMatcher toMatcher(Matcher matcher) {
return new AttributeMatcher(attribute, delegate, negate);
}
-
-
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/segments/SegmentFetcher.java b/client/src/main/java/io/split/engine/segments/SegmentFetcher.java
index 0c8820b6e..3d5c3a48e 100644
--- a/client/src/main/java/io/split/engine/segments/SegmentFetcher.java
+++ b/client/src/main/java/io/split/engine/segments/SegmentFetcher.java
@@ -9,7 +9,7 @@ public interface SegmentFetcher {
/**
* fetch
*/
- void fetch(FetchOptions opts);
+ boolean fetch(FetchOptions opts);
boolean runWhitCacheHeader();
}
diff --git a/client/src/main/java/io/split/engine/segments/SegmentFetcherImp.java b/client/src/main/java/io/split/engine/segments/SegmentFetcherImp.java
index 1f9d5f91e..466a529a7 100644
--- a/client/src/main/java/io/split/engine/segments/SegmentFetcherImp.java
+++ b/client/src/main/java/io/split/engine/segments/SegmentFetcherImp.java
@@ -1,6 +1,5 @@
package io.split.engine.segments;
-import com.google.common.annotations.VisibleForTesting;
import io.split.client.dtos.SegmentChange;
import io.split.storages.SegmentCacheProducer;
import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
@@ -34,14 +33,27 @@ public SegmentFetcherImp(String segmentName, SegmentChangeFetcher segmentChangeF
}
@Override
- public void fetch(FetchOptions opts){
+ public boolean fetch(FetchOptions opts){
try {
- fetchUntil(opts);
+ final long INITIAL_CN = _segmentCacheProducer.getChangeNumber(_segmentName);
+ while (true) {
+ long start = _segmentCacheProducer.getChangeNumber(_segmentName);
+ runWithoutExceptionHandling(opts);
+ if (INITIAL_CN == start) {
+ opts = new FetchOptions.Builder(opts).targetChangeNumber(FetchOptions.DEFAULT_TARGET_CHANGENUMBER).build();
+ }
+ long end = _segmentCacheProducer.getChangeNumber(_segmentName);
+ if (start >= end) {
+ break;
+ }
+ }
+ return true;
} catch (Exception e){
_log.error("RefreshableSegmentFetcher failed: " + e.getMessage());
if (_log.isDebugEnabled()) {
_log.debug("Reason:", e);
}
+ return false;
}
}
@@ -108,44 +120,8 @@ private String summarize(List changes) {
return bldr.toString();
}
- @VisibleForTesting
- void fetchUntil(FetchOptions opts){
- final long INITIAL_CN = _segmentCacheProducer.getChangeNumber(_segmentName);
- while (true) {
- long start = _segmentCacheProducer.getChangeNumber(_segmentName);
- runWithoutExceptionHandling(opts);
- if (INITIAL_CN == start) {
- opts = new FetchOptions.Builder(opts).targetChangeNumber(FetchOptions.DEFAULT_TARGET_CHANGENUMBER).build();
- }
- long end = _segmentCacheProducer.getChangeNumber(_segmentName);
- if (start >= end) {
- break;
- }
- }
- }
-
@Override
public boolean runWhitCacheHeader(){
- return this.fetchAndUpdate(new FetchOptions.Builder().cacheControlHeaders(true).build());
- }
-
- /**
- * Calls callLoopRun and after fetchs segment.
- * @param opts contains all soft of options used when issuing the fetch request
- */
- @VisibleForTesting
- boolean fetchAndUpdate(FetchOptions opts) {
- try {
- // Do this again in case the previous call errored out.
- fetchUntil(opts);
- return true;
-
- } catch (Exception e){
- _log.error("RefreshableSegmentFetcher failed: " + e.getMessage());
- if (_log.isDebugEnabled()) {
- _log.debug("Reason:", e);
- }
- return false;
- }
+ return this.fetch(new FetchOptions.Builder().cacheControlHeaders(true).build());
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/EventSourceClientImp.java b/client/src/main/java/io/split/engine/sse/EventSourceClientImp.java
index 3791334bb..35d1c05d7 100644
--- a/client/src/main/java/io/split/engine/sse/EventSourceClientImp.java
+++ b/client/src/main/java/io/split/engine/sse/EventSourceClientImp.java
@@ -6,7 +6,7 @@
import io.split.engine.sse.client.SSEClient;
import io.split.engine.sse.dtos.SegmentQueueDto;
import io.split.engine.sse.exceptions.EventParsingException;
-import io.split.engine.sse.workers.SplitsWorker;
+import io.split.engine.sse.workers.FeatureFlagsWorker;
import io.split.engine.sse.workers.Worker;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
@@ -56,7 +56,7 @@ public class EventSourceClientImp implements EventSourceClient {
}
public static EventSourceClientImp build(String baseStreamingUrl,
- SplitsWorker splitsWorker,
+ FeatureFlagsWorker featureFlagsWorker,
Worker segmentWorker,
PushStatusTracker pushStatusTracker,
CloseableHttpClient sseHttpClient,
@@ -64,7 +64,7 @@ public static EventSourceClientImp build(String baseStreamingUrl,
ThreadFactory threadFactory) {
return new EventSourceClientImp(baseStreamingUrl,
new NotificationParserImp(),
- NotificationProcessorImp.build(splitsWorker, segmentWorker, pushStatusTracker),
+ NotificationProcessorImp.build(featureFlagsWorker, segmentWorker, pushStatusTracker),
pushStatusTracker,
sseHttpClient,
telemetryRuntimeProducer,
diff --git a/client/src/main/java/io/split/engine/sse/NotificationParserImp.java b/client/src/main/java/io/split/engine/sse/NotificationParserImp.java
index 802d94b54..e5fee7502 100644
--- a/client/src/main/java/io/split/engine/sse/NotificationParserImp.java
+++ b/client/src/main/java/io/split/engine/sse/NotificationParserImp.java
@@ -1,7 +1,16 @@
package io.split.engine.sse;
import io.split.client.utils.Json;
-import io.split.engine.sse.dtos.*;
+
+import io.split.engine.sse.dtos.ControlNotification;
+import io.split.engine.sse.dtos.ErrorNotification;
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
+import io.split.engine.sse.dtos.GenericNotificationData;
+import io.split.engine.sse.dtos.IncomingNotification;
+import io.split.engine.sse.dtos.OccupancyNotification;
+import io.split.engine.sse.dtos.RawMessageNotification;
+import io.split.engine.sse.dtos.SegmentChangeNotification;
+import io.split.engine.sse.dtos.SplitKillNotification;
import io.split.engine.sse.exceptions.EventParsingException;
public class NotificationParserImp implements NotificationParser {
@@ -13,11 +22,9 @@ public IncomingNotification parseMessage(String payload) throws EventParsingExce
RawMessageNotification rawMessageNotification = Json.fromJson(payload, RawMessageNotification.class);
GenericNotificationData genericNotificationData = Json.fromJson(rawMessageNotification.getData(), GenericNotificationData.class);
genericNotificationData.setChannel(rawMessageNotification.getChannel());
-
if (rawMessageNotification.getChannel().contains(OCCUPANCY_PREFIX)) {
return parseControlChannelMessage(genericNotificationData);
}
-
return parseNotification(genericNotificationData);
} catch (Exception ex) {
throw new EventParsingException("Error parsing event.", ex, payload);
@@ -28,11 +35,9 @@ public IncomingNotification parseMessage(String payload) throws EventParsingExce
public ErrorNotification parseError(String payload) throws EventParsingException {
try {
ErrorNotification messageError = Json.fromJson(payload, ErrorNotification.class);
-
if (messageError.getMessage() == null || messageError.getStatusCode() == null) {
throw new Exception("Wrong notification format.");
}
-
return messageError;
} catch (Exception ex) {
throw new EventParsingException("Error parsing event.", ex, payload);
@@ -42,7 +47,7 @@ public ErrorNotification parseError(String payload) throws EventParsingException
private IncomingNotification parseNotification(GenericNotificationData genericNotificationData) throws Exception {
switch (genericNotificationData.getType()) {
case SPLIT_UPDATE:
- return new SplitChangeNotification(genericNotificationData);
+ return new FeatureFlagChangeNotification(genericNotificationData);
case SPLIT_KILL:
return new SplitKillNotification(genericNotificationData);
case SEGMENT_UPDATE:
@@ -59,7 +64,6 @@ private IncomingNotification parseControlChannelMessage(GenericNotificationData
if (genericNotificationData.getControlType() != null) {
return new ControlNotification(genericNotificationData);
}
-
return new OccupancyNotification(genericNotificationData);
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/NotificationProcessor.java b/client/src/main/java/io/split/engine/sse/NotificationProcessor.java
index 20f8af7fa..bdd842455 100644
--- a/client/src/main/java/io/split/engine/sse/NotificationProcessor.java
+++ b/client/src/main/java/io/split/engine/sse/NotificationProcessor.java
@@ -1,12 +1,14 @@
package io.split.engine.sse;
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
import io.split.engine.sse.dtos.IncomingNotification;
+import io.split.engine.sse.dtos.SplitKillNotification;
import io.split.engine.sse.dtos.StatusNotification;
public interface NotificationProcessor {
void process(IncomingNotification notification);
- void processSplitUpdate(long changeNumber);
- void processSplitKill(long changeNumber, String splitName, String defaultTreatment);
+ void processSplitUpdate(FeatureFlagChangeNotification featureFlagChangeNotification);
+ void processSplitKill(SplitKillNotification splitKillNotification);
void processSegmentUpdate(long changeNumber, String segmentName);
void processStatus(StatusNotification statusNotification);
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/NotificationProcessorImp.java b/client/src/main/java/io/split/engine/sse/NotificationProcessorImp.java
index c8271c9ec..5b8e705b3 100644
--- a/client/src/main/java/io/split/engine/sse/NotificationProcessorImp.java
+++ b/client/src/main/java/io/split/engine/sse/NotificationProcessorImp.java
@@ -1,30 +1,33 @@
package io.split.engine.sse;
import com.google.common.annotations.VisibleForTesting;
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
+import io.split.engine.sse.dtos.GenericNotificationData;
import io.split.engine.sse.dtos.IncomingNotification;
+import io.split.engine.sse.dtos.SplitKillNotification;
import io.split.engine.sse.dtos.StatusNotification;
import io.split.engine.sse.dtos.SegmentQueueDto;
-import io.split.engine.sse.workers.SplitsWorker;
+import io.split.engine.sse.workers.FeatureFlagsWorker;
import io.split.engine.sse.workers.Worker;
import static com.google.common.base.Preconditions.checkNotNull;
public class NotificationProcessorImp implements NotificationProcessor {
- private final SplitsWorker _splitsWorker;
+ private final FeatureFlagsWorker _featureFlagsWorker;
private final Worker _segmentWorker;
private final PushStatusTracker _pushStatusTracker;
@VisibleForTesting
- /* package private */ NotificationProcessorImp(SplitsWorker splitsWorker,
+ /* package private */ NotificationProcessorImp(FeatureFlagsWorker featureFlagsWorker,
Worker segmentWorker,
PushStatusTracker pushStatusTracker) {
- _splitsWorker = checkNotNull(splitsWorker);
+ _featureFlagsWorker = checkNotNull(featureFlagsWorker);
_segmentWorker = checkNotNull(segmentWorker);
_pushStatusTracker = checkNotNull(pushStatusTracker);
}
- public static NotificationProcessorImp build(SplitsWorker splitsWorker, Worker segmentWorker, PushStatusTracker pushStatusTracker) {
- return new NotificationProcessorImp(splitsWorker, segmentWorker, pushStatusTracker);
+ public static NotificationProcessorImp build(FeatureFlagsWorker featureFlagsWorker, Worker segmentWorker, PushStatusTracker pushStatusTracker) {
+ return new NotificationProcessorImp(featureFlagsWorker, segmentWorker, pushStatusTracker);
}
@Override
@@ -33,14 +36,17 @@ public void process(IncomingNotification notification) {
}
@Override
- public void processSplitUpdate(long changeNumber) {
- _splitsWorker.addToQueue(changeNumber);
+ public void processSplitUpdate(FeatureFlagChangeNotification featureFlagChangeNotification) {
+ _featureFlagsWorker.addToQueue(featureFlagChangeNotification);
}
@Override
- public void processSplitKill(long changeNumber, String splitName, String defaultTreatment) {
- _splitsWorker.killSplit(changeNumber, splitName, defaultTreatment);
- _splitsWorker.addToQueue(changeNumber);
+ public void processSplitKill(SplitKillNotification splitKillNotification) {
+ _featureFlagsWorker.kill(splitKillNotification);
+ _featureFlagsWorker.addToQueue(new FeatureFlagChangeNotification(GenericNotificationData.builder()
+ .changeNumber(splitKillNotification.getChangeNumber())
+ .channel(splitKillNotification.getChannel())
+ .build()));
}
@Override
diff --git a/client/src/main/java/io/split/engine/sse/dtos/FeatureFlagChangeNotification.java b/client/src/main/java/io/split/engine/sse/dtos/FeatureFlagChangeNotification.java
new file mode 100644
index 000000000..05f79abec
--- /dev/null
+++ b/client/src/main/java/io/split/engine/sse/dtos/FeatureFlagChangeNotification.java
@@ -0,0 +1,80 @@
+package io.split.engine.sse.dtos;
+
+import io.split.client.dtos.Split;
+import io.split.client.utils.Json;
+import io.split.engine.segments.SegmentSynchronizationTaskImp;
+import io.split.engine.sse.NotificationProcessor;
+import io.split.engine.sse.enums.CompressType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Base64;
+import java.util.zip.DataFormatException;
+
+import static io.split.engine.sse.utils.DecompressionUtil.gZipDecompress;
+import static io.split.engine.sse.utils.DecompressionUtil.zLibDecompress;
+
+public class FeatureFlagChangeNotification extends IncomingNotification {
+ private static final Logger _log = LoggerFactory.getLogger(SegmentSynchronizationTaskImp.class);
+ private final long changeNumber;
+ private long previousChangeNumber;
+ private Split featureFlagDefinition;
+ private CompressType compressType;
+
+ public FeatureFlagChangeNotification(GenericNotificationData genericNotificationData) {
+ super(Type.SPLIT_UPDATE, genericNotificationData.getChannel());
+ changeNumber = genericNotificationData.getChangeNumber();
+ if(genericNotificationData.getPreviousChangeNumber() != null) {
+ previousChangeNumber = genericNotificationData.getPreviousChangeNumber();
+ }
+ compressType = CompressType.from(genericNotificationData.getCompressType());
+ if (compressType == null || genericNotificationData.getFeatureFlagDefinition() == null) {
+ return;
+ }
+ try {
+ byte[] decodedBytes = Base64.getDecoder().decode(genericNotificationData.getFeatureFlagDefinition());
+ switch (compressType) {
+ case GZIP:
+ decodedBytes = gZipDecompress(decodedBytes);
+ break;
+ case ZLIB:
+ decodedBytes = zLibDecompress(decodedBytes);
+ break;
+ }
+ featureFlagDefinition = Json.fromJson(new String(decodedBytes, 0, decodedBytes.length, "UTF-8"), Split.class);
+ } catch (UnsupportedEncodingException | IllegalArgumentException e) {
+ _log.warn("Could not decode base64 data in flag definition", e);
+ } catch (DataFormatException d) {
+ _log.warn("Could not decompress feature flag definition with zlib algorithm", d);
+ } catch (IOException i) {
+ _log.warn("Could not decompress feature flag definition with gzip algorithm", i);
+ }
+ }
+
+ public long getChangeNumber() {
+ return changeNumber;
+ }
+ public long getPreviousChangeNumber() {
+ return previousChangeNumber;
+ }
+
+ public Split getFeatureFlagDefinition() {
+ return featureFlagDefinition;
+ }
+
+ public CompressType getCompressType() {
+ return compressType;
+ }
+
+ @Override
+ public void handler(NotificationProcessor notificationProcessor) {
+ notificationProcessor.processSplitUpdate(this);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Type: %s; Channel: %s; ChangeNumber: %s", getType(), getChannel(), getChangeNumber());
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/dtos/GenericNotificationData.java b/client/src/main/java/io/split/engine/sse/dtos/GenericNotificationData.java
index 416ab8375..7fd3dc1bd 100644
--- a/client/src/main/java/io/split/engine/sse/dtos/GenericNotificationData.java
+++ b/client/src/main/java/io/split/engine/sse/dtos/GenericNotificationData.java
@@ -1,5 +1,7 @@
package io.split.engine.sse.dtos;
+import com.google.gson.annotations.SerializedName;
+
public class GenericNotificationData {
private final Long changeNumber;
private final String defaultTreatment;
@@ -9,15 +11,24 @@ public class GenericNotificationData {
private final String segmentName;
private final IncomingNotification.Type type;
private String channel;
+ @SerializedName("pcn")
+ private Long previousChangeNumber;
+ @SerializedName("d")
+ private String featureFlagDefinition;
+ @SerializedName("c")
+ private Integer compressType;
- public GenericNotificationData (Long changeNumber,
+ private GenericNotificationData (Long changeNumber,
String defaultTreatment,
String splitName,
ControlType controlType,
OccupancyMetrics occupancyMetrics,
String segmentName,
IncomingNotification.Type type,
- String channel) {
+ String channel,
+ Long previousChangeNumber,
+ String data,
+ Integer compressType) {
this.changeNumber = changeNumber;
this.defaultTreatment = defaultTreatment;
this.splitName = splitName;
@@ -26,6 +37,9 @@ public GenericNotificationData (Long changeNumber,
this.segmentName = segmentName;
this.type = type;
this.channel = channel;
+ this.previousChangeNumber = previousChangeNumber;
+ this.featureFlagDefinition = data;
+ this.compressType = compressType;
}
public long getChangeNumber() {
@@ -57,8 +71,100 @@ public IncomingNotification.Type getType() {
}
public String getChannel() { return channel; }
+ public Long getPreviousChangeNumber() {
+ return previousChangeNumber;
+ }
+
+ public String getFeatureFlagDefinition() {
+ return featureFlagDefinition;
+ }
+
+ public Integer getCompressType() {
+ return compressType;
+ }
public void setChannel(String channel) {
this.channel = channel;
}
-}
+
+ public static GenericNotificationData.Builder builder() {
+ return new GenericNotificationData.Builder();
+ }
+
+ public static final class Builder {
+ private Long changeNumber;
+ private String defaultTreatment;
+ private String featureFlagName;
+ private ControlType controlType;
+ private OccupancyMetrics metrics;
+ private String segmentName;
+ private IncomingNotification.Type type;
+ private String channel;
+ private Long previousChangeNumber;
+ private String featureFlagDefinition;
+ private Integer compressType;
+
+ public Builder() {
+ }
+
+ public Builder changeNumber(Long changeNumber) {
+ this.changeNumber = changeNumber;
+ return this;
+ }
+
+ public Builder defaultTreatment(String defaultTreatment) {
+ this.defaultTreatment = defaultTreatment;
+ return this;
+ }
+
+ public Builder featureFlagName(String featureFlagName) {
+ this.featureFlagName = featureFlagName;
+ return this;
+ }
+
+ public Builder controlType(ControlType controlType) {
+ this.controlType = controlType;
+ return this;
+ }
+
+ public Builder metrics(OccupancyMetrics occupancyMetrics) {
+ this.metrics = occupancyMetrics;
+ return this;
+ }
+
+ public Builder segmentName(String segmentName) {
+ this.segmentName = segmentName;
+ return this;
+ }
+
+ public Builder type(IncomingNotification.Type type) {
+ this.type = type;
+ return this;
+ }
+
+ public Builder channel(String channel) {
+ this.channel = channel;
+ return this;
+ }
+
+ public Builder previousChangeNumber(Long previousChangeNumber) {
+ this.previousChangeNumber = previousChangeNumber;
+ return this;
+ }
+
+ public Builder featureFlagDefinition(String featureFlagDefinition) {
+ this.featureFlagDefinition = featureFlagDefinition;
+ return this;
+ }
+
+ public Builder compressType(Integer compressType) {
+ this.compressType = compressType;
+ return this;
+ }
+
+ public GenericNotificationData build() {
+ return new GenericNotificationData(changeNumber, defaultTreatment, featureFlagName, controlType, metrics,
+ segmentName, type, channel, previousChangeNumber, featureFlagDefinition, compressType);
+ }
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/dtos/RawAuthResponse.java b/client/src/main/java/io/split/engine/sse/dtos/RawAuthResponse.java
index 4e0026420..4082aac72 100644
--- a/client/src/main/java/io/split/engine/sse/dtos/RawAuthResponse.java
+++ b/client/src/main/java/io/split/engine/sse/dtos/RawAuthResponse.java
@@ -60,4 +60,4 @@ private String addPrefixControlChannels(String channels) {
.replace("control_pri", "[?occupancy=metrics.publishers]control_pri")
.replace("control_sec", "[?occupancy=metrics.publishers]control_sec");
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/dtos/SplitChangeNotification.java b/client/src/main/java/io/split/engine/sse/dtos/SplitChangeNotification.java
deleted file mode 100644
index 56b8c32c5..000000000
--- a/client/src/main/java/io/split/engine/sse/dtos/SplitChangeNotification.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package io.split.engine.sse.dtos;
-
-import io.split.engine.sse.NotificationProcessor;
-
-public class SplitChangeNotification extends IncomingNotification {
- private final long changeNumber;
-
- public SplitChangeNotification(GenericNotificationData genericNotificationData) {
- super(Type.SPLIT_UPDATE, genericNotificationData.getChannel());
- this.changeNumber = genericNotificationData.getChangeNumber();
- }
-
- public long getChangeNumber() {
- return changeNumber;
- }
-
- @Override
- public void handler(NotificationProcessor notificationProcessor) {
- notificationProcessor.processSplitUpdate(getChangeNumber());
- }
-
- @Override
- public String toString() {
- return String.format("Type: %s; Channel: %s; ChangeNumber: %s", getType(), getChannel(), getChangeNumber());
- }
-}
diff --git a/client/src/main/java/io/split/engine/sse/dtos/SplitKillNotification.java b/client/src/main/java/io/split/engine/sse/dtos/SplitKillNotification.java
index ed4700352..4d47e758b 100644
--- a/client/src/main/java/io/split/engine/sse/dtos/SplitKillNotification.java
+++ b/client/src/main/java/io/split/engine/sse/dtos/SplitKillNotification.java
@@ -28,11 +28,11 @@ public String getSplitName() {
@Override
public void handler(NotificationProcessor notificationProcessor) {
- notificationProcessor.processSplitKill(getChangeNumber(), getSplitName(), getDefaultTreatment());
+ notificationProcessor.processSplitKill(this);
}
@Override
public String toString() {
return String.format("Type: %s; Channel: %s; ChangeNumber: %s; DefaultTreatment: %s; SplitName: %s", getType(), getChannel(), getChangeNumber(), getDefaultTreatment(), getSplitName());
}
-}
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/enums/CompressType.java b/client/src/main/java/io/split/engine/sse/enums/CompressType.java
new file mode 100644
index 000000000..ed78a33b8
--- /dev/null
+++ b/client/src/main/java/io/split/engine/sse/enums/CompressType.java
@@ -0,0 +1,39 @@
+package io.split.engine.sse.enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public enum CompressType {
+ NOT_COMPRESSED(0),
+ GZIP(1),
+ ZLIB(2);
+
+ private final Integer value;
+
+ CompressType(Integer value) {
+ this.value = value;
+ }
+
+ public long getValue() {
+ return value;
+ }
+
+ // Mapping compress type to compress type id
+ private static final Map _map = new HashMap<>();
+ static {
+ for (CompressType compressType : CompressType.values())
+ _map.put(compressType.value, compressType);
+ }
+
+ /**
+ * Get compress type from value
+ * @param value value
+ * @return CompressType
+ */
+ public static CompressType from(Integer value) {
+ if (value == null || _map.size() <= value){
+ return null;
+ }
+ return _map.get(value);
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/utils/DecompressionUtil.java b/client/src/main/java/io/split/engine/sse/utils/DecompressionUtil.java
new file mode 100644
index 000000000..522425e45
--- /dev/null
+++ b/client/src/main/java/io/split/engine/sse/utils/DecompressionUtil.java
@@ -0,0 +1,42 @@
+package io.split.engine.sse.utils;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.zip.DataFormatException;
+import java.util.zip.GZIPInputStream;
+import java.util.zip.Inflater;
+
+public class DecompressionUtil {
+
+ public static byte[] zLibDecompress(byte[] toDecompress) throws DataFormatException {
+ ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(toDecompress.length);
+ Inflater decompressor = new Inflater();
+ try {
+ decompressor.setInput(toDecompress);
+ final byte[] buf = new byte[toDecompress.length];
+ while (!decompressor.finished()) {
+ int count = decompressor.inflate(buf);
+ byteArrayOutputStream.write(buf, 0, count);
+ }
+ } finally {
+ decompressor.end();
+ }
+ return byteArrayOutputStream.toByteArray();
+ }
+
+ public static byte[] gZipDecompress(byte[] toDecompress) throws IOException {
+ ByteArrayOutputStream out = new ByteArrayOutputStream();
+ try (GZIPInputStream gzipInputStream = new GZIPInputStream(new ByteArrayInputStream(toDecompress))){
+ int res = 0;
+ byte buf[] = new byte[toDecompress.length];
+ while (res >= 0) {
+ res = gzipInputStream.read(buf, 0, buf.length);
+ if (res > 0) {
+ out.write(buf, 0, res);
+ }
+ }
+ }
+ return out.toByteArray();
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/workers/FeatureFlagWorkerImp.java b/client/src/main/java/io/split/engine/sse/workers/FeatureFlagWorkerImp.java
new file mode 100644
index 000000000..455986689
--- /dev/null
+++ b/client/src/main/java/io/split/engine/sse/workers/FeatureFlagWorkerImp.java
@@ -0,0 +1,77 @@
+package io.split.engine.sse.workers;
+
+import io.split.client.dtos.Split;
+import io.split.client.utils.FeatureFlagsToUpdate;
+import io.split.engine.common.Synchronizer;
+import io.split.engine.experiments.SplitParser;
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
+import io.split.engine.sse.dtos.SplitKillNotification;
+import io.split.storages.SplitCacheProducer;
+import io.split.telemetry.domain.enums.UpdatesFromSSEEnum;
+import io.split.telemetry.storage.TelemetryRuntimeProducer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Collections;
+import java.util.Set;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static io.split.client.utils.FeatureFlagProcessor.processFeatureFlagChanges;
+
+public class FeatureFlagWorkerImp extends Worker implements FeatureFlagsWorker {
+ private static final Logger _log = LoggerFactory.getLogger(FeatureFlagWorkerImp.class);
+ private final Synchronizer _synchronizer;
+ private final SplitParser _splitParser;
+ private final SplitCacheProducer _splitCacheProducer;
+ private final TelemetryRuntimeProducer _telemetryRuntimeProducer;
+
+ public FeatureFlagWorkerImp(Synchronizer synchronizer, SplitParser splitParser, SplitCacheProducer splitCacheProducer, TelemetryRuntimeProducer telemetryRuntimeProducer) {
+ super("Feature flags");
+ _synchronizer = checkNotNull(synchronizer);
+ _splitParser = splitParser;
+ _splitCacheProducer = splitCacheProducer;
+ _telemetryRuntimeProducer = telemetryRuntimeProducer;
+ }
+
+ @Override
+ public void kill(SplitKillNotification splitKillNotification) {
+ try {
+ _synchronizer.localKillSplit(splitKillNotification);
+ _log.debug(String.format("Kill feature flag: %s, changeNumber: %s, defaultTreatment: %s", splitKillNotification.getSplitName(), splitKillNotification.getChangeNumber(),
+ splitKillNotification.getDefaultTreatment()));
+ } catch (Exception ex) {
+ _log.warn(String.format("Exception on FeatureFlagWorker kill: %s", ex.getMessage()));
+ }
+ }
+
+ @Override
+ protected void executeRefresh(FeatureFlagChangeNotification featureFlagChangeNotification) {
+ boolean success = addOrUpdateFeatureFlag(featureFlagChangeNotification);
+
+ if (!success)
+ _synchronizer.refreshSplits(featureFlagChangeNotification.getChangeNumber());
+ }
+
+ private boolean addOrUpdateFeatureFlag(FeatureFlagChangeNotification featureFlagChangeNotification) {
+ if (featureFlagChangeNotification.getChangeNumber() <= _splitCacheProducer.getChangeNumber()) {
+ return true;
+ }
+ try {
+ if (featureFlagChangeNotification.getFeatureFlagDefinition() != null &&
+ featureFlagChangeNotification.getPreviousChangeNumber() == _splitCacheProducer.getChangeNumber()) {
+ Split featureFlag = featureFlagChangeNotification.getFeatureFlagDefinition();
+ FeatureFlagsToUpdate featureFlagsToUpdate = processFeatureFlagChanges(_splitParser, Collections.singletonList(featureFlag));
+ _splitCacheProducer.update(featureFlagsToUpdate.getToAdd(), featureFlagsToUpdate.getToRemove(), featureFlagChangeNotification.getChangeNumber());
+ Set segments = featureFlagsToUpdate.getSegments();
+ for (String segmentName: segments) {
+ _synchronizer.forceRefreshSegment(segmentName);
+ }
+ _telemetryRuntimeProducer.recordUpdatesFromSSE(UpdatesFromSSEEnum.SPLITS);
+ return true;
+ }
+ } catch (Exception e) {
+ _log.warn("Something went wrong processing a Feature Flag notification", e);
+ }
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/workers/FeatureFlagsWorker.java b/client/src/main/java/io/split/engine/sse/workers/FeatureFlagsWorker.java
new file mode 100644
index 000000000..354dbd7e1
--- /dev/null
+++ b/client/src/main/java/io/split/engine/sse/workers/FeatureFlagsWorker.java
@@ -0,0 +1,11 @@
+package io.split.engine.sse.workers;
+
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
+import io.split.engine.sse.dtos.SplitKillNotification;
+
+public interface FeatureFlagsWorker {
+ void addToQueue(FeatureFlagChangeNotification featureFlagChangeNotification);
+ void start();
+ void stop();
+ void kill(SplitKillNotification splitKillNotification);
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/engine/sse/workers/SplitsWorker.java b/client/src/main/java/io/split/engine/sse/workers/SplitsWorker.java
deleted file mode 100644
index 3664b7cd4..000000000
--- a/client/src/main/java/io/split/engine/sse/workers/SplitsWorker.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package io.split.engine.sse.workers;
-
-public interface SplitsWorker {
- void addToQueue(Long element);
- void start();
- void stop();
- void killSplit(long changeNumber, String splitName, String defaultTreatment);
-}
diff --git a/client/src/main/java/io/split/engine/sse/workers/SplitsWorkerImp.java b/client/src/main/java/io/split/engine/sse/workers/SplitsWorkerImp.java
deleted file mode 100644
index 16e155ed7..000000000
--- a/client/src/main/java/io/split/engine/sse/workers/SplitsWorkerImp.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package io.split.engine.sse.workers;
-
-import io.split.engine.common.Synchronizer;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-public class SplitsWorkerImp extends Worker implements SplitsWorker {
- private final Synchronizer _synchronizer;
-
- public SplitsWorkerImp(Synchronizer synchronizer) {
- super("Splits");
- _synchronizer = checkNotNull(synchronizer);
- }
-
- @Override
- public void killSplit(long changeNumber, String splitName, String defaultTreatment) {
- try {
- _synchronizer.localKillSplit(splitName, defaultTreatment, changeNumber);
- _log.debug(String.format("Kill split: %s, changeNumber: %s, defaultTreatment: %s", splitName, changeNumber, defaultTreatment));
- } catch (Exception ex) {
- _log.warn(String.format("Exception on SplitWorker killSplit: %s", ex.getMessage()));
- }
- }
-
- @Override
- protected void executeRefresh(Long changeNumber) {
- _synchronizer.refreshSplits(changeNumber);
- }
-}
diff --git a/client/src/main/java/io/split/storages/SplitCacheProducer.java b/client/src/main/java/io/split/storages/SplitCacheProducer.java
index a237b06f0..02a64ddc9 100644
--- a/client/src/main/java/io/split/storages/SplitCacheProducer.java
+++ b/client/src/main/java/io/split/storages/SplitCacheProducer.java
@@ -12,4 +12,5 @@ public interface SplitCacheProducer extends SplitCacheCommons{
void putMany(List splits);
void increaseTrafficType(String trafficType);
void decreaseTrafficType(String trafficType);
+ void update(List toAdd, List toRemove, long changeNumber);
}
diff --git a/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java b/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java
index 4a4c97a67..167741730 100644
--- a/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java
+++ b/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java
@@ -142,7 +142,20 @@ public void increaseTrafficType(String trafficType) {
public void decreaseTrafficType(String trafficType) {
_concurrentTrafficTypeNameSet.remove(trafficType);
}
-
+
+ @Override
+ public void update(List toAdd, List toRemove, long changeNumber) {
+ if(toAdd != null) {
+ putMany(toAdd);
+ }
+ if(toRemove != null) {
+ for(String featureFlag : toRemove) {
+ remove(featureFlag);
+ }
+ }
+ setChangeNumber(changeNumber);
+ }
+
public Set getSegments() {
return _concurrentMap.values().stream()
.flatMap(parsedSplit -> parsedSplit.getSegmentsNames().stream()).collect(Collectors.toSet());
diff --git a/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterProducer.java b/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterProducer.java
index a98391443..b9a034aff 100644
--- a/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterProducer.java
+++ b/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomSplitAdapterProducer.java
@@ -98,6 +98,19 @@ public void decreaseTrafficType(String trafficType) {
}
}
+ @Override
+ public void update(List toAdd, List toRemove, long changeNumber) {
+ if(toAdd != null) {
+ putMany(toAdd);
+ }
+ if(toRemove != null) {
+ for(String featureFlag : toRemove) {
+ remove(featureFlag);
+ }
+ }
+ setChangeNumber(changeNumber);
+ }
+
@Override
public Set getSegments() {
//NoOp
diff --git a/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomTelemetryAdapterProducer.java b/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomTelemetryAdapterProducer.java
index 802b8402a..1dd9412b7 100644
--- a/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomTelemetryAdapterProducer.java
+++ b/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomTelemetryAdapterProducer.java
@@ -10,6 +10,7 @@
import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
import io.split.telemetry.domain.enums.MethodEnum;
import io.split.telemetry.domain.enums.ResourceEnum;
+import io.split.telemetry.domain.enums.UpdatesFromSSEEnum;
import io.split.telemetry.storage.TelemetryStorageProducer;
import io.split.telemetry.utils.BucketCalculator;
import pluggable.CustomStorageWrapper;
@@ -99,4 +100,9 @@ public void recordStreamingEvents(StreamingEvent streamingEvent) {
public void recordSessionLength(long sessionLength) {
//No-op
}
+
+ @Override
+ public void recordUpdatesFromSSE(UpdatesFromSSEEnum updatesFromSSEEnum) {
+ //No-op
+ }
}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/domain/Stats.java b/client/src/main/java/io/split/telemetry/domain/Stats.java
index 8baec7261..31577caf5 100644
--- a/client/src/main/java/io/split/telemetry/domain/Stats.java
+++ b/client/src/main/java/io/split/telemetry/domain/Stats.java
@@ -23,6 +23,7 @@ public class Stats {
/* package private */ static final String FIELD_EVENTS_DROPPED = "eD";
/* package private */ static final String FIELD_STREAMING_EVENT = "sE";
/* package private */ static final String FIELD_TAGS = "t";
+ /* package private */ static final String FIELD_UPDATES_FROM_SSE = "ufs";
@SerializedName(FIELD_LAST_SYNCHRONIZATION)
private LastSynchronization _lastSynchronization;
@@ -60,6 +61,8 @@ public class Stats {
private List _streamingEvents;
@SerializedName(FIELD_TAGS)
private List _tags;
+ @SerializedName(FIELD_UPDATES_FROM_SSE)
+ private UpdatesFromSSE _updatesFromSSE;
public LastSynchronization get_lastSynchronization() {
return _lastSynchronization;
@@ -204,4 +207,12 @@ public List get_tags() {
public void set_tags(List _tags) {
this._tags = _tags;
}
+
+ public UpdatesFromSSE get_updatesFromSSE() {
+ return _updatesFromSSE;
+ }
+
+ public void set_updatesFromSSE(UpdatesFromSSE _updatesFromSSE) {
+ this._updatesFromSSE = _updatesFromSSE;
+ }
}
diff --git a/client/src/main/java/io/split/telemetry/domain/UpdatesFromSSE.java b/client/src/main/java/io/split/telemetry/domain/UpdatesFromSSE.java
new file mode 100644
index 000000000..1f2238f9c
--- /dev/null
+++ b/client/src/main/java/io/split/telemetry/domain/UpdatesFromSSE.java
@@ -0,0 +1,19 @@
+package io.split.telemetry.domain;
+
+import com.google.gson.annotations.SerializedName;
+
+public class UpdatesFromSSE {
+
+ /* package private */ static final String FIELD_FEATURE_FLAGS = "sp";
+
+ @SerializedName(FIELD_FEATURE_FLAGS)
+ private long splits;
+
+ public long getSplits() {
+ return splits;
+ }
+
+ public void setSplits(long splits) {
+ this.splits = splits;
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/domain/enums/UpdatesFromSSEEnum.java b/client/src/main/java/io/split/telemetry/domain/enums/UpdatesFromSSEEnum.java
new file mode 100644
index 000000000..eceb872e6
--- /dev/null
+++ b/client/src/main/java/io/split/telemetry/domain/enums/UpdatesFromSSEEnum.java
@@ -0,0 +1,5 @@
+package io.split.telemetry.domain.enums;
+
+public enum UpdatesFromSSEEnum {
+ SPLITS
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/storage/InMemoryTelemetryStorage.java b/client/src/main/java/io/split/telemetry/storage/InMemoryTelemetryStorage.java
index 493a34b75..0d950d8f6 100644
--- a/client/src/main/java/io/split/telemetry/storage/InMemoryTelemetryStorage.java
+++ b/client/src/main/java/io/split/telemetry/storage/InMemoryTelemetryStorage.java
@@ -1,8 +1,24 @@
package io.split.telemetry.storage;
import com.google.common.collect.Maps;
-import io.split.telemetry.domain.*;
-import io.split.telemetry.domain.enums.*;
+
+import io.split.telemetry.domain.HTTPErrors;
+import io.split.telemetry.domain.HTTPLatencies;
+import io.split.telemetry.domain.LastSynchronization;
+import io.split.telemetry.domain.MethodExceptions;
+import io.split.telemetry.domain.MethodLatencies;
+import io.split.telemetry.domain.StreamingEvent;
+import io.split.telemetry.domain.UpdatesFromSSE;
+import io.split.telemetry.domain.enums.EventsDataRecordsEnum;
+import io.split.telemetry.domain.enums.FactoryCountersEnum;
+import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
+import io.split.telemetry.domain.enums.ImpressionsDataTypeEnum;
+import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
+import io.split.telemetry.domain.enums.MethodEnum;
+import io.split.telemetry.domain.enums.PushCountersEnum;
+import io.split.telemetry.domain.enums.ResourceEnum;
+import io.split.telemetry.domain.enums.SdkRecordsEnum;
+import io.split.telemetry.domain.enums.UpdatesFromSSEEnum;
import io.split.telemetry.utils.AtomicLongArray;
import io.split.telemetry.utils.BucketCalculator;
@@ -13,7 +29,7 @@
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
-public class InMemoryTelemetryStorage implements TelemetryStorage{
+public class InMemoryTelemetryStorage implements TelemetryStorage{
public static final int MAX_LATENCY_BUCKET_COUNT = 23;
public static final int MAX_STREAMING_EVENTS = 20;
public static final int MAX_TAGS = 10;
@@ -32,6 +48,7 @@ public class InMemoryTelemetryStorage implements TelemetryStorage{
private final ConcurrentMap _eventsDataRecords = Maps.newConcurrentMap();
private final ConcurrentMap _lastSynchronizationRecords = Maps.newConcurrentMap();
private final ConcurrentMap _sdkRecords = Maps.newConcurrentMap();
+ private final ConcurrentMap _updatesFromSSERecords = Maps.newConcurrentMap();
//HTTPErrors
private final ConcurrentMap> _httpErrors = Maps.newConcurrentMap();
@@ -55,6 +72,7 @@ public InMemoryTelemetryStorage() {
initSdkRecords();
initLastSynchronizationRecords();
initEventDataRecords();
+ initUpdatesFromSEE();
}
@Override
@@ -209,6 +227,13 @@ public long getSessionLength() {
return _sdkRecords.get(SdkRecordsEnum.SESSION).get();
}
+ @Override
+ public UpdatesFromSSE popUpdatesFromSSE() {
+ UpdatesFromSSE updatesFromSSE = new UpdatesFromSSE();
+ updatesFromSSE.setSplits(_updatesFromSSERecords.get(UpdatesFromSSEEnum.SPLITS).getAndSet(0L));
+ return updatesFromSSE;
+ }
+
@Override
public void addTag(String tag) {
synchronized (_tagsLock) {
@@ -271,6 +296,11 @@ public void recordSessionLength(long sessionLength) {
_sdkRecords.replace(SdkRecordsEnum.SESSION, new AtomicLong(sessionLength));
}
+ @Override
+ public void recordUpdatesFromSSE(UpdatesFromSSEEnum updatesFromSSEEnum) {
+ _updatesFromSSERecords.get(UpdatesFromSSEEnum.SPLITS).incrementAndGet();
+ }
+
private void initMethodLatencies() {
_methodLatencies.put(MethodEnum.TREATMENT, new AtomicLongArray(MAX_LATENCY_BUCKET_COUNT));
_methodLatencies.put(MethodEnum.TREATMENTS, new AtomicLongArray(MAX_LATENCY_BUCKET_COUNT));
@@ -341,4 +371,8 @@ private void initEventDataRecords() {
_eventsDataRecords.put(EventsDataRecordsEnum.EVENTS_DROPPED, new AtomicLong());
_eventsDataRecords.put(EventsDataRecordsEnum.EVENTS_QUEUED, new AtomicLong());
}
-}
+
+ private void initUpdatesFromSEE() {
+ _updatesFromSSERecords.put(UpdatesFromSSEEnum.SPLITS, new AtomicLong());
+ }
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/storage/NoopTelemetryStorage.java b/client/src/main/java/io/split/telemetry/storage/NoopTelemetryStorage.java
index 3673d0c0a..d18134a8a 100644
--- a/client/src/main/java/io/split/telemetry/storage/NoopTelemetryStorage.java
+++ b/client/src/main/java/io/split/telemetry/storage/NoopTelemetryStorage.java
@@ -1,7 +1,19 @@
package io.split.telemetry.storage;
-import io.split.telemetry.domain.*;
-import io.split.telemetry.domain.enums.*;
+import io.split.telemetry.domain.HTTPErrors;
+import io.split.telemetry.domain.HTTPLatencies;
+import io.split.telemetry.domain.LastSynchronization;
+import io.split.telemetry.domain.MethodExceptions;
+import io.split.telemetry.domain.MethodLatencies;
+import io.split.telemetry.domain.StreamingEvent;
+import io.split.telemetry.domain.UpdatesFromSSE;
+import io.split.telemetry.domain.enums.EventsDataRecordsEnum;
+import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
+import io.split.telemetry.domain.enums.ImpressionsDataTypeEnum;
+import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
+import io.split.telemetry.domain.enums.MethodEnum;
+import io.split.telemetry.domain.enums.ResourceEnum;
+import io.split.telemetry.domain.enums.UpdatesFromSSEEnum;
import java.util.List;
@@ -77,6 +89,11 @@ public void recordSessionLength(long sessionLength) {
}
+ @Override
+ public void recordUpdatesFromSSE(UpdatesFromSSEEnum updatesFromSSEEnum) {
+
+ }
+
@Override
public long getBURTimeouts() {
return 0;
@@ -146,4 +163,9 @@ public List popTags() {
public long getSessionLength() {
return 0;
}
+
+ @Override
+ public UpdatesFromSSE popUpdatesFromSSE() {
+ return null;
+ }
}
diff --git a/client/src/main/java/io/split/telemetry/storage/TelemetryRuntimeConsumer.java b/client/src/main/java/io/split/telemetry/storage/TelemetryRuntimeConsumer.java
index 6a746e783..8be689989 100644
--- a/client/src/main/java/io/split/telemetry/storage/TelemetryRuntimeConsumer.java
+++ b/client/src/main/java/io/split/telemetry/storage/TelemetryRuntimeConsumer.java
@@ -4,6 +4,7 @@
import io.split.telemetry.domain.HTTPLatencies;
import io.split.telemetry.domain.LastSynchronization;
import io.split.telemetry.domain.StreamingEvent;
+import io.split.telemetry.domain.UpdatesFromSSE;
import io.split.telemetry.domain.enums.EventsDataRecordsEnum;
import io.split.telemetry.domain.enums.ImpressionsDataTypeEnum;
@@ -20,4 +21,5 @@ public interface TelemetryRuntimeConsumer {
List popStreamingEvents();
List popTags();
long getSessionLength();
+ UpdatesFromSSE popUpdatesFromSSE();
}
diff --git a/client/src/main/java/io/split/telemetry/storage/TelemetryRuntimeProducer.java b/client/src/main/java/io/split/telemetry/storage/TelemetryRuntimeProducer.java
index 2baf016f0..789562871 100644
--- a/client/src/main/java/io/split/telemetry/storage/TelemetryRuntimeProducer.java
+++ b/client/src/main/java/io/split/telemetry/storage/TelemetryRuntimeProducer.java
@@ -1,6 +1,7 @@
package io.split.telemetry.storage;
import io.split.telemetry.domain.StreamingEvent;
+import io.split.telemetry.domain.UpdatesFromSSE;
import io.split.telemetry.domain.enums.*;
public interface TelemetryRuntimeProducer {
@@ -14,4 +15,5 @@ public interface TelemetryRuntimeProducer {
void recordTokenRefreshes();
void recordStreamingEvents(StreamingEvent streamingEvent);
void recordSessionLength(long sessionLength);
-}
+ void recordUpdatesFromSSE(UpdatesFromSSEEnum updatesFromSSEEnum);
+}
\ No newline at end of file
diff --git a/client/src/main/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitter.java b/client/src/main/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitter.java
index 5666e1258..c58ed1380 100644
--- a/client/src/main/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitter.java
+++ b/client/src/main/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitter.java
@@ -92,6 +92,7 @@ Stats generateStats() throws Exception {
stats.set_eventsDropped(_teleTelemetryStorageConsumer.getEventStats(EventsDataRecordsEnum.EVENTS_DROPPED));
stats.set_streamingEvents(_teleTelemetryStorageConsumer.popStreamingEvents());
stats.set_tags(_teleTelemetryStorageConsumer.popTags());
+ stats.set_updatesFromSSE(_teleTelemetryStorageConsumer.popUpdatesFromSSE());
return stats;
}
diff --git a/client/src/test/java/io/split/client/utils/FeatureFlagProcessorTest.java b/client/src/test/java/io/split/client/utils/FeatureFlagProcessorTest.java
new file mode 100644
index 000000000..5c81d12bf
--- /dev/null
+++ b/client/src/test/java/io/split/client/utils/FeatureFlagProcessorTest.java
@@ -0,0 +1,34 @@
+package io.split.client.utils;
+
+import io.split.client.dtos.Split;
+import io.split.engine.experiments.SplitParser;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import static io.split.client.utils.FeatureFlagProcessor.processFeatureFlagChanges;
+
+public class FeatureFlagProcessorTest {
+
+ @Test
+ public void testProcessFeatureFlagChanges() {
+ SplitParser splitParser = new SplitParser();
+ List featureFlags = new ArrayList<>();
+
+ String definition1 = "{\"trafficTypeName\":\"user\",\"id\":\"d431cdd0-b0be-11ea-8a80-1660ada9ce39\",\"name\":\"mauro_java\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-92391491,\"seed\":-1769377604,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1684329854385,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"WHITELIST\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"matcherType\":\"WHITELIST\",\"negate\":false,\"whitelistMatcherData\":{\"whitelist\":[\"admin\",\"mauro\",\"nico\"]}}]},\"partitions\":[{\"treatment\":\"off\",\"size\":100}],\"label\":\"whitelisted\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\"},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"maur-2\"}}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100},{\"treatment\":\"V4\",\"size\":0},{\"treatment\":\"v5\",\"size\":0}],\"label\":\"in segment maur-2\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\"},\"matcherType\":\"ALL_KEYS\",\"negate\":false}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100},{\"treatment\":\"V4\",\"size\":0},{\"treatment\":\"v5\",\"size\":0}],\"label\":\"default rule\"}]}";
+ Split featureFlagTest1 = Json.fromJson(definition1, Split.class);
+
+ String definition2 = "{\"trafficTypeName\":\"user\",\"id\":\"d704f220-0567-11ee-80ee-fa3c6460cd13\",\"name\":\"NET_CORE_getTreatmentWithConfigAfterArchive\",\"trafficAllocation\":100,\"trafficAllocationSeed\":179018541,\"seed\":272707374,\"status\":\"ARCHIVED\",\"killed\":false,\"defaultTreatment\":\"V-FGyN\",\"changeNumber\":1686165617166,\"algo\":2,\"configurations\":{\"V-FGyN\":\"{\\\"color\\\":\\\"blue\\\"}\",\"V-YrWB\":\"{\\\"color\\\":\\\"red\\\"}\"},\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\",\"attribute\":\"test\"},\"matcherType\":\"LESS_THAN_OR_EQUAL_TO\",\"negate\":false,\"unaryNumericMatcherData\":{\"dataType\":\"NUMBER\",\"value\":20}}]},\"partitions\":[{\"treatment\":\"V-FGyN\",\"size\":0},{\"treatment\":\"V-YrWB\",\"size\":100}],\"label\":\"test \\u003c\\u003d 20\"}]}";
+ Split featureFlagTest2 = Json.fromJson(definition2, Split.class);
+
+ featureFlags.add(featureFlagTest1);
+ featureFlags.add(featureFlagTest2);
+ FeatureFlagsToUpdate featureFlagsToUpdate = processFeatureFlagChanges(splitParser, featureFlags);
+
+ Assert.assertEquals(1, featureFlagsToUpdate.toAdd.size());
+ Assert.assertEquals(1, featureFlagsToUpdate.toRemove.size());
+ Assert.assertEquals(1, featureFlagsToUpdate.segments.size());
+ }
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/common/LocalhostSynchronizerTest.java b/client/src/test/java/io/split/engine/common/LocalhostSynchronizerTest.java
index a7fbdeacf..3d303845e 100644
--- a/client/src/test/java/io/split/engine/common/LocalhostSynchronizerTest.java
+++ b/client/src/test/java/io/split/engine/common/LocalhostSynchronizerTest.java
@@ -11,7 +11,6 @@
import io.split.engine.segments.SegmentSynchronizationTaskImp;
import io.split.storages.SegmentCacheProducer;
import io.split.storages.SplitCache;
-import io.split.storages.SplitCacheConsumer;
import io.split.storages.SplitCacheProducer;
import io.split.storages.memory.InMemoryCacheImp;
import io.split.storages.memory.SegmentCacheInMemoryImpl;
@@ -26,14 +25,13 @@ public class LocalhostSynchronizerTest {
private static final TelemetryStorage TELEMETRY_STORAGE_NOOP = Mockito.mock(NoopTelemetryStorage.class);
@Test
- public void testSyncAll(){
+ public void testSyncAll() {
SplitCache splitCacheProducer = new InMemoryCacheImp();
- SplitCacheConsumer splitCacheConsumer = Mockito.mock(SplitCacheConsumer.class);
SplitChangeFetcher splitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/split_init.json");
SplitParser splitParser = new SplitParser();
- SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheConsumer, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
+ SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
SplitSynchronizationTask splitSynchronizationTask = new SplitSynchronizationTask(splitFetcher, splitCacheProducer, 1000L, null);
SegmentChangeFetcher segmentChangeFetcher = new LocalhostSegmentChangeFetcher("src/test/resources/");
@@ -51,12 +49,11 @@ public void testSyncAll(){
@Test
public void testPeriodicFetching() throws InterruptedException {
SplitCache splitCacheProducer = new InMemoryCacheImp();
- SplitCacheConsumer splitCacheConsumer = Mockito.mock(SplitCacheConsumer.class);
SplitChangeFetcher splitChangeFetcher = Mockito.mock(JsonLocalhostSplitChangeFetcher.class);
SplitParser splitParser = new SplitParser();
- SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheConsumer, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
+ SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
SplitSynchronizationTask splitSynchronizationTask = new SplitSynchronizationTask(splitFetcher, splitCacheProducer, 1000L, null);
FetchOptions fetchOptions = new FetchOptions.Builder().build();
@@ -77,13 +74,12 @@ public void testPeriodicFetching() throws InterruptedException {
}
@Test
- public void testRefreshSplits(){
+ public void testRefreshSplits() {
SplitCacheProducer splitCacheProducer = new InMemoryCacheImp();
- SplitCacheConsumer splitCacheConsumer = Mockito.mock(SplitCacheConsumer.class);
SplitChangeFetcher splitChangeFetcher = Mockito.mock(SplitChangeFetcher.class);
SplitParser splitParser = new SplitParser();
- SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheConsumer, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
+ SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
SplitSynchronizationTask splitSynchronizationTask = new SplitSynchronizationTask(splitFetcher, splitCacheProducer, 1000L, null);
SplitTasks splitTasks = SplitTasks.build(splitSynchronizationTask, null, null, null, null, null);
LocalhostSynchronizer localhostSynchronizer = new LocalhostSynchronizer(splitTasks, splitFetcher, false);
diff --git a/client/src/test/java/io/split/engine/common/PushManagerTest.java b/client/src/test/java/io/split/engine/common/PushManagerTest.java
index d95d13ce5..12664ad1d 100644
--- a/client/src/test/java/io/split/engine/common/PushManagerTest.java
+++ b/client/src/test/java/io/split/engine/common/PushManagerTest.java
@@ -7,7 +7,7 @@
import io.split.engine.sse.client.SSEClient;
import io.split.engine.sse.dtos.AuthenticationResponse;
import io.split.engine.sse.workers.SegmentsWorkerImp;
-import io.split.engine.sse.workers.SplitsWorker;
+import io.split.engine.sse.workers.FeatureFlagsWorker;
import io.split.telemetry.storage.InMemoryTelemetryStorage;
import io.split.telemetry.storage.TelemetryStorage;
import org.junit.Assert;
@@ -32,7 +32,7 @@ public void setUp() {
_telemetryStorage = new InMemoryTelemetryStorage();
_pushManager = new PushManagerImp(_authApiClient,
_eventSourceClient,
- Mockito.mock(SplitsWorker.class),
+ Mockito.mock(FeatureFlagsWorker.class),
Mockito.mock(SegmentsWorkerImp.class),
_pushStatusTracker,
_telemetryStorage,
diff --git a/client/src/test/java/io/split/engine/common/SynchronizerTest.java b/client/src/test/java/io/split/engine/common/SynchronizerTest.java
index a25ec4139..036115a93 100644
--- a/client/src/test/java/io/split/engine/common/SynchronizerTest.java
+++ b/client/src/test/java/io/split/engine/common/SynchronizerTest.java
@@ -5,7 +5,11 @@
import io.split.client.impressions.UniqueKeysTracker;
import io.split.engine.segments.SegmentChangeFetcher;
import io.split.engine.segments.SegmentSynchronizationTaskImp;
-import io.split.storages.*;
+import io.split.storages.SegmentCache;
+import io.split.storages.SegmentCacheProducer;
+import io.split.storages.SplitCache;
+import io.split.storages.SplitCacheConsumer;
+import io.split.storages.SplitCacheProducer;
import io.split.storages.memory.InMemoryCacheImp;
import io.split.engine.experiments.FetchResult;
import io.split.engine.experiments.SplitFetcherImp;
@@ -113,7 +117,7 @@ public void stopPeriodicFetching() {
public void streamingRetryOnSplit() {
when(_splitCacheProducer.getChangeNumber()).thenReturn(0l).thenReturn(0l).thenReturn(1l);
when(_splitFetcher.forceRefresh(Mockito.anyObject())).thenReturn(new FetchResult(true, new HashSet<>()));
- _synchronizer.refreshSplits(1l);
+ _synchronizer.refreshSplits(1L);
Mockito.verify(_splitCacheProducer, Mockito.times(3)).getChangeNumber();
}
@@ -138,14 +142,14 @@ public void streamingRetryOnSplitAndSegment() {
SegmentFetcher fetcher = Mockito.mock(SegmentFetcher.class);
when(_segmentCacheProducer.getChangeNumber(Mockito.anyString())).thenReturn(0l).thenReturn(0l).thenReturn(1l);
when(_segmentFetcher.getFetcher(Mockito.anyString())).thenReturn(fetcher);
- _synchronizer.refreshSplits(1l);
+ _synchronizer.refreshSplits(1L);
Mockito.verify(_splitCacheProducer, Mockito.times(3)).getChangeNumber();
Mockito.verify(_segmentFetcher, Mockito.times(2)).getFetcher(Mockito.anyString());
}
@Test
- public void testCDNBypassIsRequestedAfterNFailures() throws NoSuchFieldException, IllegalAccessException {
+ public void testCDNBypassIsRequestedAfterNFailures() {
SplitCache cache = new InMemoryCacheImp();
Synchronizer imp = new SynchronizerImp(_splitTasks,
@@ -317,4 +321,4 @@ public void testDataRecording(){
Mockito.verify(_uniqueKeysTracker, Mockito.times(1)).stop();
Mockito.verify(_telemetrySyncTask, Mockito.times(1)).stopScheduledTask();
}
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/experiments/SplitFetcherImpTest.java b/client/src/test/java/io/split/engine/experiments/SplitFetcherImpTest.java
index b1de841a0..d838cc0de 100644
--- a/client/src/test/java/io/split/engine/experiments/SplitFetcherImpTest.java
+++ b/client/src/test/java/io/split/engine/experiments/SplitFetcherImpTest.java
@@ -2,7 +2,6 @@
import io.split.client.JsonLocalhostSplitChangeFetcher;
import io.split.engine.common.FetchOptions;
-import io.split.storages.SplitCacheConsumer;
import io.split.storages.SplitCacheProducer;
import io.split.storages.memory.InMemoryCacheImp;
import io.split.telemetry.storage.NoopTelemetryStorage;
@@ -18,12 +17,11 @@ public class SplitFetcherImpTest {
@Test
public void testLocalHost(){
SplitCacheProducer splitCacheProducer = new InMemoryCacheImp();
- SplitCacheConsumer splitCacheConsumer = Mockito.mock(SplitCacheConsumer.class);
SplitChangeFetcher splitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/split_init.json");
SplitParser splitParser = new SplitParser();
FetchOptions fetchOptions = new FetchOptions.Builder().build();
- SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheConsumer, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
+ SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
FetchResult fetchResult = splitFetcher.forceRefresh(fetchOptions);
diff --git a/client/src/test/java/io/split/engine/experiments/SplitFetcherTest.java b/client/src/test/java/io/split/engine/experiments/SplitFetcherTest.java
index f27168587..a6be86240 100644
--- a/client/src/test/java/io/split/engine/experiments/SplitFetcherTest.java
+++ b/client/src/test/java/io/split/engine/experiments/SplitFetcherTest.java
@@ -36,6 +36,7 @@
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.mock;
@@ -63,13 +64,12 @@ public void works_when_we_start_with_any_state() throws InterruptedException {
private void works(long startingChangeNumber) throws InterruptedException {
AChangePerCallSplitChangeFetcher splitChangeFetcher = new AChangePerCallSplitChangeFetcher();
SplitCache cache = new InMemoryCacheImp(startingChangeNumber);
- SplitFetcherImp fetcher = new SplitFetcherImp(splitChangeFetcher, new SplitParser(), cache, cache, TELEMETRY_STORAGE);
+ SplitFetcherImp fetcher = new SplitFetcherImp(splitChangeFetcher, new SplitParser(), cache, TELEMETRY_STORAGE);
// execute the fetcher for a little bit.
executeWaitAndTerminate(fetcher, 1, 3, TimeUnit.SECONDS);
assertThat(splitChangeFetcher.lastAdded(), is(greaterThan(startingChangeNumber)));
-// assertThat(cache.getChangeNumber(), is(equalTo(splitChangeFetcher.lastAdded())));
// all previous splits have been removed since they are dead
for (long i = startingChangeNumber; i < cache.getChangeNumber(); i++) {
@@ -135,14 +135,14 @@ public void when_parser_fails_we_remove_the_experiment() throws InterruptedExcep
SegmentChangeFetcher segmentChangeFetcher = mock(SegmentChangeFetcher.class);
SegmentSynchronizationTask segmentSynchronizationTask = new SegmentSynchronizationTaskImp(segmentChangeFetcher, 1,10, segmentCache, TELEMETRY_STORAGE, cache, null);
segmentSynchronizationTask.start();
- SplitFetcherImp fetcher = new SplitFetcherImp(splitChangeFetcher, new SplitParser(), cache, cache, TELEMETRY_STORAGE);
+ SplitFetcherImp fetcher = new SplitFetcherImp(splitChangeFetcher, new SplitParser(), cache, TELEMETRY_STORAGE);
// execute the fetcher for a little bit.
executeWaitAndTerminate(fetcher, 1, 5, TimeUnit.SECONDS);
- assertThat(cache.getChangeNumber(), is(equalTo(1L)));
+ assertEquals(1L, cache.getChangeNumber());
// verify that the fetcher return null
- assertThat(cache.get("-1"), is(nullValue()));
+ Assert.assertNull(cache.get("-1"));
}
@Test
@@ -156,12 +156,12 @@ public void if_there_is_a_problem_talking_to_split_change_count_down_latch_is_no
SegmentChangeFetcher segmentChangeFetcher = mock(SegmentChangeFetcher.class);
SegmentSynchronizationTask segmentSynchronizationTask = new SegmentSynchronizationTaskImp(segmentChangeFetcher, 1,10, segmentCache, TELEMETRY_STORAGE, cache, null);
segmentSynchronizationTask.start();
- SplitFetcherImp fetcher = new SplitFetcherImp(splitChangeFetcher, new SplitParser(),cache, cache, TELEMETRY_STORAGE);
+ SplitFetcherImp fetcher = new SplitFetcherImp(splitChangeFetcher, new SplitParser(), cache, TELEMETRY_STORAGE);
// execute the fetcher for a little bit.
executeWaitAndTerminate(fetcher, 1, 5, TimeUnit.SECONDS);
- assertThat(cache.getChangeNumber(), is(equalTo(-1L)));
+ Assert.assertEquals(-1L, cache.getChangeNumber());
}
private void executeWaitAndTerminate(Runnable runnable, long frequency, long waitInBetween, TimeUnit unit) throws InterruptedException {
@@ -197,7 +197,7 @@ public void works_with_user_defined_segments() throws Exception {
when(segmentChangeFetcher.fetch(anyString(), anyLong(), any())).thenReturn(segmentChange);
SegmentSynchronizationTask segmentSynchronizationTask = new SegmentSynchronizationTaskImp(segmentChangeFetcher, 1,10, segmentCache, Mockito.mock(TelemetryStorage.class), cache, null);
segmentSynchronizationTask.start();
- SplitFetcherImp fetcher = new SplitFetcherImp(experimentChangeFetcher, new SplitParser(), cache, cache, TELEMETRY_STORAGE);
+ SplitFetcherImp fetcher = new SplitFetcherImp(experimentChangeFetcher, new SplitParser(), cache, TELEMETRY_STORAGE);
// execute the fetcher for a little bit.
executeWaitAndTerminate(fetcher, 1, 5, TimeUnit.SECONDS);
@@ -217,7 +217,7 @@ public void testBypassCdnClearedAfterFirstHit() {
SplitChangeFetcher mockFetcher = Mockito.mock(SplitChangeFetcher.class);
SplitParser mockParser = new SplitParser();
SplitCache mockCache = new InMemoryCacheImp();
- SplitFetcherImp fetcher = new SplitFetcherImp(mockFetcher, mockParser, mockCache, mockCache, Mockito.mock(TelemetryRuntimeProducer.class));
+ SplitFetcherImp fetcher = new SplitFetcherImp(mockFetcher, mockParser, mockCache, Mockito.mock(TelemetryRuntimeProducer.class));
SplitChange response1 = new SplitChange();
diff --git a/client/src/test/java/io/split/engine/experiments/SplitSynchronizationTaskTest.java b/client/src/test/java/io/split/engine/experiments/SplitSynchronizationTaskTest.java
index 1d3417795..d53ac89e7 100644
--- a/client/src/test/java/io/split/engine/experiments/SplitSynchronizationTaskTest.java
+++ b/client/src/test/java/io/split/engine/experiments/SplitSynchronizationTaskTest.java
@@ -2,7 +2,6 @@
import io.split.client.JsonLocalhostSplitChangeFetcher;
import io.split.engine.common.FetchOptions;
-import io.split.storages.SplitCacheConsumer;
import io.split.storages.SplitCacheProducer;
import io.split.storages.memory.InMemoryCacheImp;
import io.split.telemetry.storage.NoopTelemetryStorage;
@@ -17,12 +16,11 @@ public class SplitSynchronizationTaskTest {
@Test
public void testLocalhost() throws InterruptedException {
SplitCacheProducer splitCacheProducer = new InMemoryCacheImp();
- SplitCacheConsumer splitCacheConsumer = Mockito.mock(SplitCacheConsumer.class);
SplitChangeFetcher splitChangeFetcher = Mockito.mock(JsonLocalhostSplitChangeFetcher.class);
SplitParser splitParser = new SplitParser();
FetchOptions fetchOptions = new FetchOptions.Builder().build();
- SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheConsumer, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
+ SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
SplitSynchronizationTask splitSynchronizationTask = new SplitSynchronizationTask(splitFetcher, splitCacheProducer, 1000, null);
diff --git a/client/src/test/java/io/split/engine/segments/SegmentFetcherImpTest.java b/client/src/test/java/io/split/engine/segments/SegmentFetcherImpTest.java
index d9803d6cf..ce263bfe4 100644
--- a/client/src/test/java/io/split/engine/segments/SegmentFetcherImpTest.java
+++ b/client/src/test/java/io/split/engine/segments/SegmentFetcherImpTest.java
@@ -1,6 +1,5 @@
package io.split.engine.segments;
-import com.google.common.collect.Sets;
import io.split.storages.SegmentCache;
import io.split.storages.SegmentCacheProducer;
import io.split.storages.memory.SegmentCacheInMemoryImpl;
@@ -18,7 +17,6 @@
import java.util.ArrayList;
import java.util.List;
-import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
@@ -45,7 +43,6 @@ public void works_when_we_start_without_state() throws InterruptedException {
@Test
public void works_when_we_start_with_state() throws InterruptedException {
works(20L);
-
}
@Test
@@ -76,11 +73,8 @@ public void works_when_there_are_no_changes() throws InterruptedException {
Thread.currentThread().interrupt();
}
- Set expected = Sets.newHashSet("" + (startingChangeNumber + 1));
-
assertNotNull(segmentCache.getChangeNumber(SEGMENT_NAME));
assertEquals(10L, segmentCache.getChangeNumber(SEGMENT_NAME));
-
}
private void works(long startingChangeNumber) throws InterruptedException {
@@ -114,7 +108,6 @@ private void works(long startingChangeNumber) throws InterruptedException {
Thread.currentThread().interrupt();
}
Mockito.verify(segmentChangeFetcher, Mockito.times(2)).fetch(Mockito.anyString(), Mockito.anyLong(), Mockito.anyObject());
-
}
@@ -150,7 +143,7 @@ public void testBypassCdnClearedAfterFirstHit() {
response2.added = new ArrayList<>();
response2.removed = new ArrayList<>();
response2.since = 1;
- response1.till = 1;
+ response2.till = 1;
ArgumentCaptor optionsCaptor = ArgumentCaptor.forClass(FetchOptions.class);
ArgumentCaptor cnCaptor = ArgumentCaptor.forClass(Long.class);
diff --git a/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java b/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java
index d4a71ccbd..f9e1e354d 100644
--- a/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java
+++ b/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java
@@ -33,8 +33,7 @@
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
-import static org.hamcrest.Matchers.*;
-import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertEquals;
/**
* Tests for SegmentSynchronizationTaskImp
@@ -93,8 +92,8 @@ public void run() {
Thread.currentThread().interrupt();
}
- assertThat(fetcher1.get(), is(notNullValue()));
- assertThat(fetcher1.get(), is(sameInstance(fetcher2.get())));
+ Assert.assertNotNull(fetcher1.get());
+ assertEquals(fetcher1.get(), fetcher2.get());
}
@Test
@@ -107,10 +106,8 @@ public void testFetchAllAsynchronousAndGetFalse() throws NoSuchFieldException, I
_segmentFetchers.put("SF", segmentFetcher);
final SegmentSynchronizationTaskImp fetchers = new SegmentSynchronizationTaskImp(segmentChangeFetcher, 1L, 1,
segmentCacheProducer, TELEMETRY_STORAGE, Mockito.mock(SplitCacheConsumer.class), null);
- Mockito.doNothing().when(segmentFetcher).fetchUntil(Mockito.anyObject());
Mockito.when(segmentFetcher.runWhitCacheHeader()).thenReturn(false);
- Mockito.when(segmentFetcher.fetchAndUpdate(Mockito.anyObject())).thenReturn(false);
- Mockito.doNothing().when(segmentFetcher).fetchUntil(Mockito.anyObject());
+ Mockito.when(segmentFetcher.fetch(Mockito.anyObject())).thenReturn(false);
// Before executing, we'll update the map of segmentFecthers via reflection.
Field segmentFetchersForced = SegmentSynchronizationTaskImp.class.getDeclaredField("_segmentFetchers");
@@ -141,9 +138,8 @@ public void testFetchAllAsynchronousAndGetTrue() throws NoSuchFieldException, Il
modifiersField.setAccessible(true);
modifiersField.setInt(segmentFetchersForced, segmentFetchersForced.getModifiers() & ~Modifier.FINAL);
segmentFetchersForced.set(fetchers, _segmentFetchers);
- Mockito.doNothing().when(segmentFetcher).fetchUntil(Mockito.anyObject());
Mockito.when(segmentFetcher.runWhitCacheHeader()).thenReturn(true);
- Mockito.when(segmentFetcher.fetchAndUpdate(Mockito.anyObject())).thenReturn(true);
+ Mockito.when(segmentFetcher.fetch(Mockito.anyObject())).thenReturn(true);
boolean fetch = fetchers.fetchAllSynchronous();
Assert.assertEquals(true, fetch);
}
@@ -152,12 +148,11 @@ public void testFetchAllAsynchronousAndGetTrue() throws NoSuchFieldException, Il
public void testLocalhostSegmentChangeFetcher() throws InterruptedException {
SplitCache splitCacheProducer = new InMemoryCacheImp();
- SplitCache splitCacheConsumer = new InMemoryCacheImp();
SplitChangeFetcher splitChangeFetcher = new JsonLocalhostSplitChangeFetcher("src/test/resources/split_init.json");
SplitParser splitParser = new SplitParser();
FetchOptions fetchOptions = new FetchOptions.Builder().build();
- SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheConsumer, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
+ SplitFetcher splitFetcher = new SplitFetcherImp(splitChangeFetcher, splitParser, splitCacheProducer, TELEMETRY_STORAGE_NOOP);
SplitSynchronizationTask splitSynchronizationTask = new SplitSynchronizationTask(splitFetcher, splitCacheProducer, 1000, null);
diff --git a/client/src/test/java/io/split/engine/sse/EventSourceClientTest.java b/client/src/test/java/io/split/engine/sse/EventSourceClientTest.java
index d3bbc22c8..c5bc22b1b 100644
--- a/client/src/test/java/io/split/engine/sse/EventSourceClientTest.java
+++ b/client/src/test/java/io/split/engine/sse/EventSourceClientTest.java
@@ -3,7 +3,7 @@
import io.split.SSEMockServer;
import io.split.engine.sse.client.SSEClient;
import io.split.engine.sse.dtos.ErrorNotification;
-import io.split.engine.sse.dtos.SplitChangeNotification;
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
import io.split.telemetry.storage.InMemoryTelemetryStorage;
import io.split.telemetry.storage.TelemetryRuntimeProducer;
import org.apache.hc.client5.http.config.RequestConfig;
@@ -44,7 +44,7 @@ public void startShouldConnect() throws IOException {
EventSourceClient eventSourceClient = new EventSourceClientImp("http://localhost:" + sseServer.getPort(), _notificationParser, _notificationProcessor, _pushStatusTracker, buildHttpClient(), telemetryRuntimeProducer, null);
- boolean result = eventSourceClient.start("channel-test","token-test");
+ boolean result = eventSourceClient.start("channel-test", "token-test");
Assert.assertTrue(result);
@@ -59,7 +59,7 @@ public void startShouldReconnect() throws IOException {
sseServer.start();
EventSourceClient eventSourceClient = new EventSourceClientImp("http://fake:" + sseServer.getPort(), _notificationParser, _notificationProcessor, _pushStatusTracker, buildHttpClient(), telemetryRuntimeProducer, null);
- boolean result = eventSourceClient.start("channel-test","token-test");
+ boolean result = eventSourceClient.start("channel-test", "token-test");
Assert.assertFalse(result);
@@ -76,7 +76,7 @@ public void startAndReceiveNotification() throws IOException {
sseServer.start();
EventSourceClient eventSourceClient = new EventSourceClientImp("http://localhost:" + sseServer.getPort(), _notificationParser, _notificationProcessor, _pushStatusTracker, buildHttpClient(), telemetryRuntimeProducer, null);
- boolean result = eventSourceClient.start("channel-test","token-test");
+ boolean result = eventSourceClient.start("channel-test", "token-test");
Assert.assertTrue(result);
@@ -95,7 +95,7 @@ public void startAndReceiveNotification() throws IOException {
Awaitility.await()
.atMost(50L, TimeUnit.SECONDS)
- .untilAsserted(() -> Mockito.verify(_notificationProcessor, Mockito.times(1)).process(Mockito.any(SplitChangeNotification.class)));
+ .untilAsserted(() -> Mockito.verify(_notificationProcessor, Mockito.times(1)).process(Mockito.any(FeatureFlagChangeNotification.class)));
OutboundSseEvent sseEventError = new OutboundEvent
.Builder()
diff --git a/client/src/test/java/io/split/engine/sse/NotificationParserImpTest.java b/client/src/test/java/io/split/engine/sse/NotificationParserImpTest.java
new file mode 100644
index 000000000..cd57e56ac
--- /dev/null
+++ b/client/src/test/java/io/split/engine/sse/NotificationParserImpTest.java
@@ -0,0 +1,67 @@
+package io.split.engine.sse;
+
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
+import io.split.engine.sse.enums.CompressType;
+import io.split.engine.sse.exceptions.EventParsingException;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class NotificationParserImpTest {
+
+ @Test
+ public void validateZlibCompressType() throws EventParsingException {
+ String payload = "{\"id\":\"vQQ61wzBRO:0:0\",\"clientId\":\"pri:MTUxNzg3MDg1OQ==\",\"timestamp\":1684265694676,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MjkyNTIzNjczMw==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":1684265694505,\\\"pcn\\\":0,\\\"c\\\":2,\\\"d\\\":\\\"eJzMk99u2kwQxV8lOtdryQZj8N6hD5QPlThSTVNVEUKDPYZt1jZar1OlyO9emf8lVFWv2ss5zJyd82O8hTWUZSqZvW04opwhUVdsIKBSSKR+10vS1HWW7pIdz2NyBjRwHS8IXEopTLgbQqDYT+ZUm3LxlV4J4mg81LpMyKqygPRc94YeM6eQTtjphp4fegLVXvD6Qdjt9wPXF6gs2bqCxPC/2eRpDIEXpXXblpGuWCDljGptZ4bJ5lxYSJRZBoFkTcWKozpfsoH0goHfCXpB6PfcngDpVQnZEUjKIlOr2uwWqiC3zU5L1aF+3p7LFhUkPv8/mY2nk3gGgZxssmZzb8p6A9n25ktVtA9iGI3ODXunQ3HDp+AVWT6F+rZWlrWq7MN+YkSWWvuTDvkMSnNV7J6oTdl6qKTEvGnmjcCGjL2IYC/ovPYgUKnvvPtbmrmApiVryLM7p2jE++AfH6fTx09/HvuF32LWnNjStM0Xh3c8ukZcsZlEi3h8/zCObsBpJ0acqYLTmFdtqitK1V6NzrfpdPBbLmVx4uK26e27izpDu/r5yf/16AXun2Cr4u6w591xw7+LfDidLj6Mv8TXwP8xbofv/c7UmtHMmx8BAAD//0fclvU=\\\"}\"}";
+ NotificationParserImp notificationParserImp = new NotificationParserImp();
+
+ FeatureFlagChangeNotification incomingNotification = (FeatureFlagChangeNotification) notificationParserImp.parseMessage(payload);
+ Assert.assertEquals(incomingNotification.getFeatureFlagDefinition().name, "mauro_java");
+ Assert.assertEquals(incomingNotification.getFeatureFlagDefinition().changeNumber, 1684265694505L);
+ Assert.assertEquals(CompressType.ZLIB, incomingNotification.getCompressType());
+ Assert.assertEquals(0, incomingNotification.getPreviousChangeNumber());
+ }
+
+ @Test
+ public void validateGzipCompressType() throws EventParsingException {
+ String payload = "{\"id\":\"vQQ61wzBRO:0:0\",\"clientId\":\"pri:MTUxNzg3MDg1OQ==\",\"timestamp\":1684265694676,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MjkyNTIzNjczMw==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":1684265694505,\\\"pcn\\\":0,\\\"c\\\":1,\\\"d\\\":\\\"H4sIAAAAAAAA/8yT327aTBDFXyU612vJxoTgvUMfKB8qcaSapqoihAZ7DNusvWi9TpUiv3tl/pdQVb1qL+cwc3bOj/EGzlKeq3T6tuaYCoZEXbGFgMogkXXDIM0y31v4C/aCgMnrU9/3gl7Pp4yilMMIAuVusqDamvlXeiWIg/FAa5OSU6aEDHz/ip4wZ5Be1AmjoBsFAtVOCO56UXh31/O7ApUjV1eQGPw3HT+NIPCitG7bctIVC2ScU63d1DK5gksHCZPnEEhXVC45rosFW8ig1++GYej3g85tJEB6aSA7Aqkpc7Ws7XahCnLTbLVM7evnzalsUUHi8//j6WgyTqYQKMilK7b31tRryLa3WKiyfRCDeHhq2Dntiys+JS/J8THUt5VyrFXlHnYTQ3LU2h91yGdQVqhy+0RtTeuhUoNZ08wagTVZdxbBndF5vYVApb7z9m9pZgKaFqwhT+6coRHvg398nEweP/157Bd+S1hz6oxtm88O73B0jbhgM47nyej+YRRfgdNODDlXJWcJL9tUF5SqnRqfbtPr4LdcTHnk4rfp3buLOkG7+Pmp++vRM9w/wVblzX7Pm8OGfxf5YDKZfxh9SS6B/2Pc9t/7ja01o5k1PwIAAP//uTipVskEAAA=\\\"}\"}";
+ NotificationParserImp notificationParserImp = new NotificationParserImp();
+
+ FeatureFlagChangeNotification incomingNotification = (FeatureFlagChangeNotification) notificationParserImp.parseMessage(payload);
+ Assert.assertEquals(incomingNotification.getFeatureFlagDefinition().name, "mauro_java");
+ Assert.assertEquals(incomingNotification.getFeatureFlagDefinition().changeNumber, 1684333081259L);
+ Assert.assertEquals(CompressType.GZIP, incomingNotification.getCompressType());
+ Assert.assertEquals(0, incomingNotification.getPreviousChangeNumber());
+ }
+
+ @Test
+ public void validateNotCompressType() throws EventParsingException {
+ String payload = "{\"id\":\"vQQ61wzBRO:0:0\",\"clientId\":\"pri:MTUxNzg3MDg1OQ==\",\"timestamp\":1684265694676,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MjkyNTIzNjczMw==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":1684329854385,\\\"pcn\\\":0,\\\"c\\\":0,\\\"d\\\":\\\"eyJ0cmFmZmljVHlwZU5hbWUiOiJ1c2VyIiwiaWQiOiJkNDMxY2RkMC1iMGJlLTExZWEtOGE4MC0xNjYwYWRhOWNlMzkiLCJuYW1lIjoibWF1cm9famF2YSIsInRyYWZmaWNBbGxvY2F0aW9uIjoxMDAsInRyYWZmaWNBbGxvY2F0aW9uU2VlZCI6LTkyMzkxNDkxLCJzZWVkIjotMTc2OTM3NzYwNCwic3RhdHVzIjoiQUNUSVZFIiwia2lsbGVkIjpmYWxzZSwiZGVmYXVsdFRyZWF0bWVudCI6Im9mZiIsImNoYW5nZU51bWJlciI6MTY4NDMyOTg1NDM4NSwiYWxnbyI6MiwiY29uZmlndXJhdGlvbnMiOnt9LCJjb25kaXRpb25zIjpbeyJjb25kaXRpb25UeXBlIjoiV0hJVEVMSVNUIiwibWF0Y2hlckdyb3VwIjp7ImNvbWJpbmVyIjoiQU5EIiwibWF0Y2hlcnMiOlt7Im1hdGNoZXJUeXBlIjoiV0hJVEVMSVNUIiwibmVnYXRlIjpmYWxzZSwid2hpdGVsaXN0TWF0Y2hlckRhdGEiOnsid2hpdGVsaXN0IjpbImFkbWluIiwibWF1cm8iLCJuaWNvIl19fV19LCJwYXJ0aXRpb25zIjpbeyJ0cmVhdG1lbnQiOiJvZmYiLCJzaXplIjoxMDB9XSwibGFiZWwiOiJ3aGl0ZWxpc3RlZCJ9LHsiY29uZGl0aW9uVHlwZSI6IlJPTExPVVQiLCJtYXRjaGVyR3JvdXAiOnsiY29tYmluZXIiOiJBTkQiLCJtYXRjaGVycyI6W3sia2V5U2VsZWN0b3IiOnsidHJhZmZpY1R5cGUiOiJ1c2VyIn0sIm1hdGNoZXJUeXBlIjoiSU5fU0VHTUVOVCIsIm5lZ2F0ZSI6ZmFsc2UsInVzZXJEZWZpbmVkU2VnbWVudE1hdGNoZXJEYXRhIjp7InNlZ21lbnROYW1lIjoibWF1ci0yIn19XX0sInBhcnRpdGlvbnMiOlt7InRyZWF0bWVudCI6Im9uIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJvZmYiLCJzaXplIjoxMDB9LHsidHJlYXRtZW50IjoiVjQiLCJzaXplIjowfSx7InRyZWF0bWVudCI6InY1Iiwic2l6ZSI6MH1dLCJsYWJlbCI6ImluIHNlZ21lbnQgbWF1ci0yIn0seyJjb25kaXRpb25UeXBlIjoiUk9MTE9VVCIsIm1hdGNoZXJHcm91cCI6eyJjb21iaW5lciI6IkFORCIsIm1hdGNoZXJzIjpbeyJrZXlTZWxlY3RvciI6eyJ0cmFmZmljVHlwZSI6InVzZXIifSwibWF0Y2hlclR5cGUiOiJBTExfS0VZUyIsIm5lZ2F0ZSI6ZmFsc2V9XX0sInBhcnRpdGlvbnMiOlt7InRyZWF0bWVudCI6Im9uIiwic2l6ZSI6MH0seyJ0cmVhdG1lbnQiOiJvZmYiLCJzaXplIjoxMDB9LHsidHJlYXRtZW50IjoiVjQiLCJzaXplIjowfSx7InRyZWF0bWVudCI6InY1Iiwic2l6ZSI6MH1dLCJsYWJlbCI6ImRlZmF1bHQgcnVsZSJ9XX0=\\\"}\"}";
+ NotificationParserImp notificationParserImp = new NotificationParserImp();
+
+ FeatureFlagChangeNotification incomingNotification = (FeatureFlagChangeNotification) notificationParserImp.parseMessage(payload);
+ Assert.assertEquals(incomingNotification.getFeatureFlagDefinition().name, "mauro_java");
+ Assert.assertEquals(incomingNotification.getFeatureFlagDefinition().changeNumber, 1684329854385L);
+ Assert.assertEquals(CompressType.NOT_COMPRESSED, incomingNotification.getCompressType());
+ Assert.assertEquals(0, incomingNotification.getPreviousChangeNumber());
+ }
+
+ @Test
+ public void validateCompressTypeIncorrect() throws EventParsingException {
+ String payload = "{\"id\":\"vQQ61wzBRO:0:0\",\"clientId\":\"pri:MTUxNzg3MDg1OQ==\",\"timestamp\":1684265694676,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MjkyNTIzNjczMw==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":1684265694505,\\\"pcn\\\":0,\\\"c\\\":3,\\\"d\\\":\\\"eJzMk99u2kwQxV8lOtdryQZj8N6hD5QPlThSTVNVEUKDPYZt1jZar1OlyO9emf8lVFWv2ss5zJyd82O8hTWUZSqZvW04opwhUVdsIKBSSKR+10vS1HWW7pIdz2NyBjRwHS8IXEopTLgbQqDYT+ZUm3LxlV4J4mg81LpMyKqygPRc94YeM6eQTtjphp4fegLVXvD6Qdjt9wPXF6gs2bqCxPC/2eRpDIEXpXXblpGuWCDljGptZ4bJ5lxYSJRZBoFkTcWKozpfsoH0goHfCXpB6PfcngDpVQnZEUjKIlOr2uwWqiC3zU5L1aF+3p7LFhUkPv8/mY2nk3gGgZxssmZzb8p6A9n25ktVtA9iGI3ODXunQ3HDp+AVWT6F+rZWlrWq7MN+YkSWWvuTDvkMSnNV7J6oTdl6qKTEvGnmjcCGjL2IYC/ovPYgUKnvvPtbmrmApiVryLM7p2jE++AfH6fTx09/HvuF32LWnNjStM0Xh3c8ukZcsZlEi3h8/zCObsBpJ0acqYLTmFdtqitK1V6NzrfpdPBbLmVx4uK26e27izpDu/r5yf/16AXun2Cr4u6w591xw7+LfDidLj6Mv8TXwP8xbofv/c7UmtHMmx8BAAD//0fclvU=\\\"}\"}";
+ NotificationParserImp notificationParserImp = new NotificationParserImp();
+
+ FeatureFlagChangeNotification incomingNotification = (FeatureFlagChangeNotification) notificationParserImp.parseMessage(payload);
+ Assert.assertNull(incomingNotification.getCompressType());
+ Assert.assertEquals(0, incomingNotification.getPreviousChangeNumber());
+ }
+
+ @Test
+ public void validateCompressTypeNull() throws EventParsingException {
+ String payload = "{\"id\":\"vQQ61wzBRO:0:0\",\"clientId\":\"pri:MTUxNzg3MDg1OQ==\",\"timestamp\":1684265694676,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MjkyNTIzNjczMw==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":1684265694505,\\\"pcn\\\":0,\\\"d\\\":\\\"eJzMk99u2kwQxV8lOtdryQZj8N6hD5QPlThSTVNVEUKDPYZt1jZar1OlyO9emf8lVFWv2ss5zJyd82O8hTWUZSqZvW04opwhUVdsIKBSSKR+10vS1HWW7pIdz2NyBjRwHS8IXEopTLgbQqDYT+ZUm3LxlV4J4mg81LpMyKqygPRc94YeM6eQTtjphp4fegLVXvD6Qdjt9wPXF6gs2bqCxPC/2eRpDIEXpXXblpGuWCDljGptZ4bJ5lxYSJRZBoFkTcWKozpfsoH0goHfCXpB6PfcngDpVQnZEUjKIlOr2uwWqiC3zU5L1aF+3p7LFhUkPv8/mY2nk3gGgZxssmZzb8p6A9n25ktVtA9iGI3ODXunQ3HDp+AVWT6F+rZWlrWq7MN+YkSWWvuTDvkMSnNV7J6oTdl6qKTEvGnmjcCGjL2IYC/ovPYgUKnvvPtbmrmApiVryLM7p2jE++AfH6fTx09/HvuF32LWnNjStM0Xh3c8ukZcsZlEi3h8/zCObsBpJ0acqYLTmFdtqitK1V6NzrfpdPBbLmVx4uK26e27izpDu/r5yf/16AXun2Cr4u6w591xw7+LfDidLj6Mv8TXwP8xbofv/c7UmtHMmx8BAAD//0fclvU=\\\"}\"}";
+ NotificationParserImp notificationParserImp = new NotificationParserImp();
+
+ FeatureFlagChangeNotification incomingNotification = (FeatureFlagChangeNotification) notificationParserImp.parseMessage(payload);
+ Assert.assertNull(incomingNotification.getCompressType());
+ Assert.assertEquals(0, incomingNotification.getPreviousChangeNumber());
+ }
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/sse/NotificationParserTest.java b/client/src/test/java/io/split/engine/sse/NotificationParserTest.java
index 1bbf355ae..26f9e1997 100644
--- a/client/src/test/java/io/split/engine/sse/NotificationParserTest.java
+++ b/client/src/test/java/io/split/engine/sse/NotificationParserTest.java
@@ -1,11 +1,18 @@
package io.split.engine.sse;
-import io.split.engine.sse.dtos.*;
+import io.split.engine.sse.dtos.ControlNotification;
+import io.split.engine.sse.dtos.ControlType;
+import io.split.engine.sse.dtos.ErrorNotification;
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
+import io.split.engine.sse.dtos.IncomingNotification;
+import io.split.engine.sse.dtos.OccupancyNotification;
+import io.split.engine.sse.dtos.SegmentChangeNotification;
+import io.split.engine.sse.dtos.SplitKillNotification;
import io.split.engine.sse.exceptions.EventParsingException;
import org.junit.Before;
import org.junit.Test;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
public class NotificationParserTest {
private NotificationParser notificationParser;
@@ -22,7 +29,7 @@ public void parseSplitUpdateShouldReturnParsedEvent() throws EventParsingExcepti
IncomingNotification result = notificationParser.parseMessage(payload);
assertEquals(IncomingNotification.Type.SPLIT_UPDATE, result.getType());
assertEquals("xxxx_xxxx_splits", result.getChannel());
- assertEquals(1592590435115L, ((SplitChangeNotification) result).getChangeNumber());
+ assertEquals(1592590435115L, ((FeatureFlagChangeNotification) result).getChangeNumber());
}
@Test
@@ -149,4 +156,4 @@ public void parseControlStreamingDisabledShouldReturnParsedEvent() throws EventP
assertEquals("control_pri", result.getChannel());
assertEquals(ControlType.STREAMING_DISABLED, ((ControlNotification)result).getControlType());
}
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/sse/NotificationProcessorTest.java b/client/src/test/java/io/split/engine/sse/NotificationProcessorTest.java
index 4bbbaab72..a56f05dd1 100644
--- a/client/src/test/java/io/split/engine/sse/NotificationProcessorTest.java
+++ b/client/src/test/java/io/split/engine/sse/NotificationProcessorTest.java
@@ -1,38 +1,47 @@
package io.split.engine.sse;
-import io.split.engine.sse.dtos.*;
+import io.split.engine.sse.dtos.ControlNotification;
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
+import io.split.engine.sse.dtos.GenericNotificationData;
+import io.split.engine.sse.dtos.OccupancyNotification;
+import io.split.engine.sse.dtos.SegmentChangeNotification;
+import io.split.engine.sse.dtos.SegmentQueueDto;
+import io.split.engine.sse.dtos.SplitKillNotification;
import io.split.engine.sse.workers.SegmentsWorkerImp;
-import io.split.engine.sse.workers.SplitsWorker;
+import io.split.engine.sse.workers.FeatureFlagsWorker;
import io.split.engine.sse.workers.Worker;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class NotificationProcessorTest {
- private SplitsWorker _splitsWorker;
+ private FeatureFlagsWorker _featureFlagsWorker;
private Worker _segmentWorker;
private NotificationProcessor _notificationProcessor;
private PushStatusTracker _pushStatusTracker;
@Before
public void setUp() {
- _splitsWorker = Mockito.mock(SplitsWorker.class);
+ _featureFlagsWorker = Mockito.mock(FeatureFlagsWorker.class);
_segmentWorker = Mockito.mock(SegmentsWorkerImp.class);
_pushStatusTracker = Mockito.mock(PushStatusTracker.class);
- _notificationProcessor = new NotificationProcessorImp(_splitsWorker, _segmentWorker, _pushStatusTracker);
+ _notificationProcessor = new NotificationProcessorImp(_featureFlagsWorker, _segmentWorker, _pushStatusTracker);
}
@Test
public void processSplitUpdateAddToQueueInWorker() {
long changeNumber = 1585867723838L;
String channel = "splits";
- GenericNotificationData genericNotificationData = new GenericNotificationData(changeNumber, null, null, null, null, null, null, channel);
- SplitChangeNotification splitChangeNotification = new SplitChangeNotification(genericNotificationData);
+ GenericNotificationData genericNotificationData = GenericNotificationData.builder()
+ .changeNumber(changeNumber)
+ .channel(channel)
+ .build();
+ FeatureFlagChangeNotification splitChangeNotification = new FeatureFlagChangeNotification(genericNotificationData);
_notificationProcessor.process(splitChangeNotification);
- Mockito.verify(_splitsWorker, Mockito.times(1)).addToQueue(splitChangeNotification.getChangeNumber());
+ Mockito.verify(_featureFlagsWorker, Mockito.times(1)).addToQueue(Mockito.anyObject());
}
@Test
@@ -41,13 +50,18 @@ public void processSplitKillAndAddToQueueInWorker() {
String defaultTreatment = "off";
String splitName = "test-split";
String channel = "splits";
- GenericNotificationData genericNotificationData = new GenericNotificationData(changeNumber, defaultTreatment, splitName, null, null, null, null, channel);
+ GenericNotificationData genericNotificationData = GenericNotificationData.builder()
+ .changeNumber(changeNumber)
+ .defaultTreatment(defaultTreatment)
+ .featureFlagName(splitName)
+ .channel(channel)
+ .build();
SplitKillNotification splitKillNotification = new SplitKillNotification(genericNotificationData);
_notificationProcessor.process(splitKillNotification);
- Mockito.verify(_splitsWorker, Mockito.times(1)).killSplit(splitKillNotification.getChangeNumber(), splitKillNotification.getSplitName(), splitKillNotification.getDefaultTreatment());
- Mockito.verify(_splitsWorker, Mockito.times(1)).addToQueue(splitKillNotification.getChangeNumber());
+ Mockito.verify(_featureFlagsWorker, Mockito.times(1)).kill(splitKillNotification);
+ Mockito.verify(_featureFlagsWorker, Mockito.times(1)).addToQueue(Mockito.anyObject());
}
@Test
@@ -55,7 +69,11 @@ public void processSegmentUpdateAddToQueueInWorker() {
long changeNumber = 1585867723838L;
String segmentName = "segment-test";
String channel = "segments";
- GenericNotificationData genericNotificationData = new GenericNotificationData(changeNumber, null, null, null, null, segmentName, null, channel);
+ GenericNotificationData genericNotificationData = GenericNotificationData.builder()
+ .changeNumber(changeNumber)
+ .segmentName(segmentName)
+ .channel(channel)
+ .build();
SegmentChangeNotification segmentChangeNotification = new SegmentChangeNotification(genericNotificationData);
_notificationProcessor.process(segmentChangeNotification);
@@ -75,11 +93,13 @@ public void processControlNotification() {
@Test
public void processOccupancyNotification() {
- GenericNotificationData genericNotificationData = new GenericNotificationData(null, null, null, null, null, null, null, "control_pri");
+ GenericNotificationData genericNotificationData = GenericNotificationData.builder()
+ .channel("control_pri")
+ .build();
OccupancyNotification occupancyNotification = new OccupancyNotification(genericNotificationData);
_notificationProcessor.process(occupancyNotification);
Mockito.verify(_pushStatusTracker, Mockito.times(1)).handleIncomingOccupancyEvent(Mockito.any(OccupancyNotification.class));
}
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/sse/PushStatusTrackerTest.java b/client/src/test/java/io/split/engine/sse/PushStatusTrackerTest.java
index 8e245f5af..2660f9e1c 100644
--- a/client/src/test/java/io/split/engine/sse/PushStatusTrackerTest.java
+++ b/client/src/test/java/io/split/engine/sse/PushStatusTrackerTest.java
@@ -10,12 +10,6 @@
import org.mockito.Mockito;
import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static org.hamcrest.Matchers.equalTo;
-import static org.hamcrest.Matchers.is;
-import static org.junit.Assert.assertThat;
public class PushStatusTrackerTest {
private static final String CONTROL_PRI = "control_pri";
@@ -28,8 +22,8 @@ public void HandleControlEventStreamingPausedShouldNotifyEvent() {
PushStatusTracker pushStatusTracker = new PushStatusTrackerImp(messages, telemetryStorage);
pushStatusTracker.handleIncomingControlEvent(controlNotification);
- assertThat(messages.size(), is(equalTo(1)));
- assertThat(messages.peek(), is(equalTo(PushManager.Status.STREAMING_DOWN)));
+ Assert.assertEquals(1, messages.size());
+ Assert.assertEquals(PushManager.Status.STREAMING_DOWN, messages.peek());
}
@Test
@@ -40,9 +34,9 @@ public void HandleControlEventStreamingResumedShouldNotifyEvent() throws Interru
pushStatusTracker.handleIncomingControlEvent(buildControlNotification(ControlType.STREAMING_PAUSED));
pushStatusTracker.handleIncomingControlEvent(buildControlNotification(ControlType.STREAMING_RESUMED));
- assertThat(messages.size(), is(equalTo(2)));
- assertThat(messages.take(), is(equalTo(PushManager.Status.STREAMING_DOWN)));
- assertThat(messages.take(), is(equalTo(PushManager.Status.STREAMING_READY)));
+ Assert.assertEquals(2, messages.size());
+ Assert.assertEquals(PushManager.Status.STREAMING_DOWN, messages.take());
+ Assert.assertEquals(PushManager.Status.STREAMING_READY, messages.take());
}
@Test
@@ -57,8 +51,8 @@ public void HandleControlEventStreamingResumedShouldNotNotifyEvent() {
pushStatusTracker.handleIncomingControlEvent(controlNotification);
pushStatusTracker.handleIncomingControlEvent(controlNotification);
- assertThat(messages.size(), is(equalTo(1)));
- assertThat(messages.peek(), is(equalTo(PushManager.Status.STREAMING_DOWN)));
+ Assert.assertEquals(1, messages.size());
+ Assert.assertEquals(PushManager.Status.STREAMING_DOWN, messages.peek());
Assert.assertEquals(1, telemetryStorage.popStreamingEvents().size());
}
@@ -72,8 +66,8 @@ public void HandleControlEventStreamingDisabledShouldNotifyShutdownEvent() {
pushStatusTracker.handleIncomingControlEvent(controlNotification);
pushStatusTracker.handleIncomingControlEvent(controlNotification);
- assertThat(messages.size(), is(equalTo(1)));
- assertThat(messages.peek(), is(equalTo(PushManager.Status.STREAMING_OFF)));
+ Assert.assertEquals(1, messages.size());
+ Assert.assertEquals(PushManager.Status.STREAMING_OFF, messages.peek());
}
@Test
@@ -84,7 +78,7 @@ public void HandleOccupancyEventWithPublishersFirstTimeShouldNotNotifyEvent() {
PushStatusTracker pushStatusTracker = new PushStatusTrackerImp(messages, telemetryStorage);
pushStatusTracker.handleIncomingOccupancyEvent(occupancyNotification);
- assertThat(messages.size(), is(equalTo(0)));
+ Assert.assertEquals(0, messages.size());
Assert.assertEquals(1, telemetryStorage.popStreamingEvents().size());
}
@@ -96,12 +90,12 @@ public void HandleOccupancyEventWithPublishersAndWithStreamingDisabledShouldNoti
pushStatusTracker.handleIncomingOccupancyEvent(buildOccupancyNotification(0, null));
pushStatusTracker.handleIncomingOccupancyEvent(buildOccupancyNotification(2, null));
- assertThat(messages.size(), is(equalTo(2)));
+ Assert.assertEquals(2, messages.size());
PushManager.Status m1 = messages.take();
- assertThat(m1, is(equalTo(PushManager.Status.STREAMING_DOWN)));
+ Assert.assertEquals(PushManager.Status.STREAMING_DOWN, m1);
PushManager.Status m2 = messages.take();
- assertThat(m2, is(equalTo(PushManager.Status.STREAMING_READY)));
+ Assert.assertEquals(PushManager.Status.STREAMING_READY, m2);
}
@Test
@@ -112,12 +106,12 @@ public void HandleOccupancyEventWithDifferentChannelsPublishersShouldNotifyEvent
pushStatusTracker.handleIncomingOccupancyEvent(buildOccupancyNotification(0, "control_pri"));
pushStatusTracker.handleIncomingOccupancyEvent(buildOccupancyNotification(2, "control_sec"));
- assertThat(messages.size(), is(equalTo(2)));
+ Assert.assertEquals(2, messages.size());
PushManager.Status m1 = messages.take();
- assertThat(m1, is(equalTo(PushManager.Status.STREAMING_DOWN)));
+ Assert.assertEquals(PushManager.Status.STREAMING_DOWN, m1);
PushManager.Status m2 = messages.take();
- assertThat(m2, is(equalTo(PushManager.Status.STREAMING_READY)));
+ Assert.assertEquals(PushManager.Status.STREAMING_READY, m2);
}
@Test
@@ -151,13 +145,12 @@ public void HandleTwoRetryableErrorInARow() throws InterruptedException {
pushStatusTracker.handleSseStatus(SSEClient.StatusMessage.RETRYABLE_ERROR);
pushStatusTracker.handleSseStatus(SSEClient.StatusMessage.RETRYABLE_ERROR);
-
- assertThat(messages.size(), is(equalTo(2)));
+ Assert.assertEquals(2, messages.size());
PushManager.Status m1 = messages.take();
- assertThat(m1, is(equalTo(PushManager.Status.STREAMING_BACKOFF)));
+ Assert.assertEquals(PushManager.Status.STREAMING_BACKOFF, m1);
PushManager.Status m2 = messages.take();
- assertThat(m2, is(equalTo(PushManager.Status.STREAMING_BACKOFF)));
+ Assert.assertEquals(PushManager.Status.STREAMING_BACKOFF, m2);
}
private ControlNotification buildControlNotification(ControlType controlType) {
@@ -169,14 +162,11 @@ private OccupancyNotification buildOccupancyNotification(int publishers, String
}
private GenericNotificationData buildGenericData(ControlType controlType, IncomingNotification.Type type, Integer publishers, String channel) {
- return new GenericNotificationData(
- null,
- null,
- null,
- controlType,
- publishers != null ? new OccupancyMetrics(publishers) : null,
- null,
- type,
- channel == null ? "channel-test" : channel);
+ return GenericNotificationData.builder()
+ .controlType(controlType)
+ .metrics(publishers != null ? new OccupancyMetrics(publishers) : null)
+ .type(type)
+ .channel(channel == null ? "channel-test" : channel)
+ .build();
}
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/sse/utils/DecompressionUtilTest.java b/client/src/test/java/io/split/engine/sse/utils/DecompressionUtilTest.java
new file mode 100644
index 000000000..bc62b431f
--- /dev/null
+++ b/client/src/test/java/io/split/engine/sse/utils/DecompressionUtilTest.java
@@ -0,0 +1,36 @@
+package io.split.engine.sse.utils;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.Base64;
+import java.util.zip.DataFormatException;
+
+import static io.split.engine.sse.utils.DecompressionUtil.gZipDecompress;
+import static io.split.engine.sse.utils.DecompressionUtil.zLibDecompress;
+
+public class DecompressionUtilTest {
+
+
+ @Test
+ public void testZLibDecompress() throws UnsupportedEncodingException, DataFormatException {
+ String toDecode = "eJzMk99u2kwQxV8lOtdryQZj8N6hD5QPlThSTVNVEUKDPYZt1jZar1OlyO9emf8lVFWv2ss5zJyd82O8hTWUZSqZvW04opwhUVdsIKBSSKR+10vS1HWW7pIdz2NyBjRwHS8IXEopTLgbQqDYT+ZUm3LxlV4J4mg81LpMyKqygPRc94YeM6eQTtjphp4fegLVXvD6Qdjt9wPXF6gs2bqCxPC/2eRpDIEXpXXblpGuWCDljGptZ4bJ5lxYSJRZBoFkTcWKozpfsoH0goHfCXpB6PfcngDpVQnZEUjKIlOr2uwWqiC3zU5L1aF+3p7LFhUkPv8/mY2nk3gGgZxssmZzb8p6A9n25ktVtA9iGI3ODXunQ3HDp+AVWT6F+rZWlrWq7MN+YkSWWvuTDvkMSnNV7J6oTdl6qKTEvGnmjcCGjL2IYC/ovPYgUKnvvPtbmrmApiVryLM7p2jE++AfH6fTx09/HvuF32LWnNjStM0Xh3c8ukZcsZlEi3h8/zCObsBpJ0acqYLTmFdtqitK1V6NzrfpdPBbLmVx4uK26e27izpDu/r5yf/16AXun2Cr4u6w591xw7+LfDidLj6Mv8TXwP8xbofv/c7UmtHMmx8BAAD//0fclvU=";
+
+ byte[] decodedBytes = Base64.getDecoder().decode(toDecode);
+ byte[] decompressFeatureFlag = zLibDecompress(decodedBytes);
+ String featureFlag = new String(decompressFeatureFlag, 0, decompressFeatureFlag.length, "UTF-8");
+ Assert.assertEquals("{\"trafficTypeName\":\"user\",\"id\":\"d431cdd0-b0be-11ea-8a80-1660ada9ce39\",\"name\":\"mauro_java\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-92391491,\"seed\":-1769377604,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1684265694505,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"WHITELIST\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"matcherType\":\"WHITELIST\",\"negate\":false,\"whitelistMatcherData\":{\"whitelist\":[\"admin\",\"mauro\",\"nico\"]}}]},\"partitions\":[{\"treatment\":\"v5\",\"size\":100}],\"label\":\"whitelisted\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\"},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"maur-2\"}}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100},{\"treatment\":\"V4\",\"size\":0},{\"treatment\":\"v5\",\"size\":0}],\"label\":\"in segment maur-2\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\"},\"matcherType\":\"ALL_KEYS\",\"negate\":false}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100},{\"treatment\":\"V4\",\"size\":0},{\"treatment\":\"v5\",\"size\":0}],\"label\":\"default rule\"}]}", featureFlag);
+ }
+
+ @Test
+ public void testGZipDecompress() throws IOException {
+ String toDecode = "H4sIAAAAAAAA/8yT327aTBDFXyU612vJxoTgvUMfKB8qcaSapqoihAZ7DNusvWi9TpUiv3tl/pdQVb1qL+cwc3bOj/EGzlKeq3T6tuaYCoZEXbGFgMogkXXDIM0y31v4C/aCgMnrU9/3gl7Pp4yilMMIAuVusqDamvlXeiWIg/FAa5OSU6aEDHz/ip4wZ5Be1AmjoBsFAtVOCO56UXh31/O7ApUjV1eQGPw3HT+NIPCitG7bctIVC2ScU63d1DK5gksHCZPnEEhXVC45rosFW8ig1++GYej3g85tJEB6aSA7Aqkpc7Ws7XahCnLTbLVM7evnzalsUUHi8//j6WgyTqYQKMilK7b31tRryLa3WKiyfRCDeHhq2Dntiys+JS/J8THUt5VyrFXlHnYTQ3LU2h91yGdQVqhy+0RtTeuhUoNZ08wagTVZdxbBndF5vYVApb7z9m9pZgKaFqwhT+6coRHvg398nEweP/157Bd+S1hz6oxtm88O73B0jbhgM47nyej+YRRfgdNODDlXJWcJL9tUF5SqnRqfbtPr4LdcTHnk4rfp3buLOkG7+Pmp++vRM9w/wVblzX7Pm8OGfxf5YDKZfxh9SS6B/2Pc9t/7ja01o5k1PwIAAP//uTipVskEAAA=";
+
+ byte[] decodedBytes = Base64.getDecoder().decode(toDecode);
+ byte[] decompressFeatureFlag = gZipDecompress(decodedBytes);
+ String featureFlag = new String(decompressFeatureFlag, 0, decompressFeatureFlag.length, "UTF-8");
+ Assert.assertEquals("{\"trafficTypeName\":\"user\",\"id\":\"d431cdd0-b0be-11ea-8a80-1660ada9ce39\",\"name\":\"mauro_java\",\"trafficAllocation\":100,\"trafficAllocationSeed\":-92391491,\"seed\":-1769377604,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"off\",\"changeNumber\":1684333081259,\"algo\":2,\"configurations\":{},\"conditions\":[{\"conditionType\":\"WHITELIST\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"matcherType\":\"WHITELIST\",\"negate\":false,\"whitelistMatcherData\":{\"whitelist\":[\"admin\",\"mauro\",\"nico\"]}}]},\"partitions\":[{\"treatment\":\"v5\",\"size\":100}],\"label\":\"whitelisted\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\"},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"maur-2\"}}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100},{\"treatment\":\"V4\",\"size\":0},{\"treatment\":\"v5\",\"size\":0}],\"label\":\"in segment maur-2\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"user\"},\"matcherType\":\"ALL_KEYS\",\"negate\":false}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":100},{\"treatment\":\"V4\",\"size\":0},{\"treatment\":\"v5\",\"size\":0}],\"label\":\"default rule\"}]}", featureFlag);
+ }
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/sse/workers/FeatureFlagWorkerImpTest.java b/client/src/test/java/io/split/engine/sse/workers/FeatureFlagWorkerImpTest.java
new file mode 100644
index 000000000..09dd66b38
--- /dev/null
+++ b/client/src/test/java/io/split/engine/sse/workers/FeatureFlagWorkerImpTest.java
@@ -0,0 +1,74 @@
+package io.split.engine.sse.workers;
+
+import io.split.client.utils.Json;
+import io.split.engine.common.Synchronizer;
+import io.split.engine.common.SynchronizerImp;
+import io.split.engine.experiments.SplitParser;
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
+import io.split.engine.sse.dtos.GenericNotificationData;
+import io.split.engine.sse.dtos.RawMessageNotification;
+import io.split.storages.SplitCacheProducer;
+import io.split.storages.memory.InMemoryCacheImp;
+import io.split.telemetry.domain.UpdatesFromSSE;
+import io.split.telemetry.storage.InMemoryTelemetryStorage;
+import io.split.telemetry.storage.TelemetryStorage;
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class FeatureFlagWorkerImpTest {
+
+ @Test
+ public void testRefreshSplitsWithCorrectFF() {
+ SplitParser splitParser = new SplitParser();
+ Synchronizer synchronizer = Mockito.mock(SynchronizerImp.class);
+ SplitCacheProducer splitCacheProducer = Mockito.mock(SplitCacheProducer.class);
+ TelemetryStorage telemetryRuntimeProducer = new InMemoryTelemetryStorage();
+ FeatureFlagWorkerImp featureFlagsWorker = new FeatureFlagWorkerImp(synchronizer, splitParser, splitCacheProducer, telemetryRuntimeProducer);
+ String notification = "{\"id\":\"vQQ61wzBRO:0:0\",\"clientId\":\"pri:MTUxNzg3MDg1OQ==\",\"timestamp\":1684265694676,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MjkyNTIzNjczMw==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":1684265694505,\\\"pcn\\\":0,\\\"c\\\":2,\\\"d\\\":\\\"eJzMk99u2kwQxV8lOtdryQZj8N6hD5QPlThSTVNVEUKDPYZt1jZar1OlyO9emf8lVFWv2ss5zJyd82O8hTWUZSqZvW04opwhUVdsIKBSSKR+10vS1HWW7pIdz2NyBjRwHS8IXEopTLgbQqDYT+ZUm3LxlV4J4mg81LpMyKqygPRc94YeM6eQTtjphp4fegLVXvD6Qdjt9wPXF6gs2bqCxPC/2eRpDIEXpXXblpGuWCDljGptZ4bJ5lxYSJRZBoFkTcWKozpfsoH0goHfCXpB6PfcngDpVQnZEUjKIlOr2uwWqiC3zU5L1aF+3p7LFhUkPv8/mY2nk3gGgZxssmZzb8p6A9n25ktVtA9iGI3ODXunQ3HDp+AVWT6F+rZWlrWq7MN+YkSWWvuTDvkMSnNV7J6oTdl6qKTEvGnmjcCGjL2IYC/ovPYgUKnvvPtbmrmApiVryLM7p2jE++AfH6fTx09/HvuF32LWnNjStM0Xh3c8ukZcsZlEi3h8/zCObsBpJ0acqYLTmFdtqitK1V6NzrfpdPBbLmVx4uK26e27izpDu/r5yf/16AXun2Cr4u6w591xw7+LfDidLj6Mv8TXwP8xbofv/c7UmtHMmx8BAAD//0fclvU=\\\"}\"}";
+ RawMessageNotification rawMessageNotification = Json.fromJson(notification, RawMessageNotification.class);
+ GenericNotificationData genericNotificationData = Json.fromJson(rawMessageNotification.getData(), GenericNotificationData.class);
+ FeatureFlagChangeNotification featureFlagChangeNotification = new FeatureFlagChangeNotification(genericNotificationData);
+ featureFlagsWorker.executeRefresh(featureFlagChangeNotification);
+ UpdatesFromSSE updatesFromSSE = telemetryRuntimeProducer.popUpdatesFromSSE();
+ Assert.assertEquals(1, updatesFromSSE.getSplits());
+ Mockito.verify(synchronizer, Mockito.times(0)).refreshSplits(1684265694505L);
+ Mockito.verify(synchronizer, Mockito.times(1)).forceRefreshSegment(Mockito.anyString());
+ }
+
+ @Test
+ public void testRefreshSplitsWithEmptyData() {
+ SplitParser splitParser = new SplitParser();
+ Synchronizer synchronizer = Mockito.mock(SynchronizerImp.class);
+ SplitCacheProducer splitCacheProducer = Mockito.mock(SplitCacheProducer.class);
+ TelemetryStorage telemetryRuntimeProducer = new InMemoryTelemetryStorage();
+ FeatureFlagWorkerImp featureFlagsWorker = new FeatureFlagWorkerImp(synchronizer, splitParser, splitCacheProducer, telemetryRuntimeProducer);
+ String notification = "{\"id\":\"vQQ61wzBRO:0:0\",\"clientId\":\"pri:MTUxNzg3MDg1OQ==\",\"timestamp\":1684265694676,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MjkyNTIzNjczMw==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":1684265694505}\"}";
+ RawMessageNotification rawMessageNotification = Json.fromJson(notification, RawMessageNotification.class);
+ GenericNotificationData genericNotificationData = Json.fromJson(rawMessageNotification.getData(), GenericNotificationData.class);
+ FeatureFlagChangeNotification featureFlagChangeNotification = new FeatureFlagChangeNotification(genericNotificationData);
+ featureFlagsWorker.executeRefresh(featureFlagChangeNotification);
+ UpdatesFromSSE updatesFromSSE = telemetryRuntimeProducer.popUpdatesFromSSE();
+ Assert.assertEquals(0, updatesFromSSE.getSplits());
+ Mockito.verify(synchronizer, Mockito.times(1)).refreshSplits(1684265694505L);
+ Mockito.verify(synchronizer, Mockito.times(0)).forceRefreshSegment(Mockito.anyString());
+ }
+
+ @Test
+ public void testRefreshSplitsArchiveFF() {
+ SplitParser splitParser = new SplitParser();
+ Synchronizer synchronizer = Mockito.mock(SynchronizerImp.class);
+ SplitCacheProducer splitCacheProducer = new InMemoryCacheImp(1686165614090L);
+ TelemetryStorage telemetryRuntimeProducer = new InMemoryTelemetryStorage();
+ FeatureFlagWorkerImp featureFlagsWorker = new FeatureFlagWorkerImp(synchronizer, splitParser, splitCacheProducer, telemetryRuntimeProducer);
+ String notification = "{\"id\":\"vQQ61wzBRO:0:0\",\"clientId\":\"pri:MTUxNzg3MDg1OQ==\",\"timestamp\":1684265694676,\"encoding\":\"json\",\"channel\":\"NzM2MDI5Mzc0_MjkyNTIzNjczMw==_splits\",\"data\":\"{\\\"type\\\":\\\"SPLIT_UPDATE\\\",\\\"changeNumber\\\":1686165617166,\\\"pcn\\\":1686165614090,\\\"c\\\":2,\\\"d\\\":\\\"eJxsUdFu4jAQ/JVqnx3JDjTh/JZCrj2JBh0EqtOBIuNswKqTIMeuxKH8+ykhiKrqiyXvzM7O7lzAGlEUSqbnEyaiRODgGjRAQOXAIQ/puPB96tHHIPQYQ/QmFNErxEgG44DKnI2AQHXtTOI0my6WcXZAmxoUtsTKvil7nNZVoQ5RYdFERh7VBwK5TY60rqWwqq6AM0q/qa8Qc+As/EHZ5HHMCDR9wQ/9kIajcEygscK6BjhEy+nLr008AwLvSuuOVgjdIIEcC+H03RZw2Hg/n88JEJBHUR0wceUeDXAWTAIWPAYsZEFAQOhDDdwnIPslnOk9NcAvNwEOly3IWtdmC3wLe+1wCy0Q2Hh/zNvTV9xg3sFtr5irQe3v5f7twgAOy8V8vlinQKAUVh7RPJvanbrBsi73qurMQpTM7oSrzjueV6hR2tp05E8J39MV1hq1d7YrWWxsZ2cQGYjzeLXK0pcoyRbLLP69juZZuuiyxoPo2oa7ukqYc+JKNEq+XgVmwopucC6sGMSS9etTvAQCH0I7BO7Ttt21BE7C2E8XsN+l06h/CJy25CveH/eGM0rbHQEt9qiHnR62jtKR7N/8wafQ7tr/AQAA//8S4fPB\\\"}\"}";
+ RawMessageNotification rawMessageNotification = Json.fromJson(notification, RawMessageNotification.class);
+ GenericNotificationData genericNotificationData = Json.fromJson(rawMessageNotification.getData(), GenericNotificationData.class);
+ FeatureFlagChangeNotification featureFlagChangeNotification = new FeatureFlagChangeNotification(genericNotificationData);
+ featureFlagsWorker.executeRefresh(featureFlagChangeNotification);
+ UpdatesFromSSE updatesFromSSE = telemetryRuntimeProducer.popUpdatesFromSSE();
+ Assert.assertEquals(1, updatesFromSSE.getSplits());
+ Mockito.verify(synchronizer, Mockito.times(0)).refreshSplits(1686165617166L);
+ Mockito.verify(synchronizer, Mockito.times(0)).forceRefreshSegment(Mockito.anyString());
+ }
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/engine/sse/workers/SplitsWorkerTest.java b/client/src/test/java/io/split/engine/sse/workers/SplitsWorkerTest.java
index 8db49054b..d8a6811a5 100644
--- a/client/src/test/java/io/split/engine/sse/workers/SplitsWorkerTest.java
+++ b/client/src/test/java/io/split/engine/sse/workers/SplitsWorkerTest.java
@@ -1,6 +1,13 @@
package io.split.engine.sse.workers;
import io.split.engine.common.Synchronizer;
+import io.split.engine.experiments.SplitParser;
+import io.split.engine.sse.dtos.FeatureFlagChangeNotification;
+import io.split.engine.sse.dtos.GenericNotificationData;
+import io.split.engine.sse.dtos.SplitKillNotification;
+import io.split.storages.SplitCacheProducer;
+import io.split.telemetry.storage.InMemoryTelemetryStorage;
+import io.split.telemetry.storage.TelemetryRuntimeProducer;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mockito;
@@ -15,70 +22,103 @@ public class SplitsWorkerTest {
@Test
public void addToQueueWithoutElementsWShouldNotTriggerFetch() throws InterruptedException {
Synchronizer splitFetcherMock = Mockito.mock(Synchronizer.class);
+ SplitParser splitParser = new SplitParser();
+ SplitCacheProducer splitCacheProducer = Mockito.mock(SplitCacheProducer.class);
+ TelemetryRuntimeProducer telemetryRuntimeProducer = Mockito.mock(InMemoryTelemetryStorage.class);
- SplitsWorker splitsWorker = new SplitsWorkerImp(splitFetcherMock);
- splitsWorker.start();
+ FeatureFlagsWorker featureFlagsWorker = new FeatureFlagWorkerImp(splitFetcherMock, splitParser, splitCacheProducer, telemetryRuntimeProducer);
+ featureFlagsWorker.start();
Thread.sleep(500);
- Mockito.verify(splitFetcherMock, Mockito.never()).refreshSplits(Mockito.anyLong());
- splitsWorker.stop();
+ Mockito.verify(splitFetcherMock, Mockito.never()).refreshSplits(Mockito.anyObject());
+ featureFlagsWorker.stop();
}
@Test
public void addToQueueWithElementsWShouldTriggerFetch() throws InterruptedException {
Synchronizer syncMock = Mockito.mock(Synchronizer.class);
+ SplitParser splitParser = new SplitParser();
+ SplitCacheProducer splitCacheProducer = Mockito.mock(SplitCacheProducer.class);
+ TelemetryRuntimeProducer telemetryRuntimeProducer = Mockito.mock(InMemoryTelemetryStorage.class);
- SplitsWorker splitsWorker = new SplitsWorkerImp(syncMock);
- splitsWorker.start();
+ FeatureFlagsWorker featureFlagsWorker = new FeatureFlagWorkerImp(syncMock, splitParser, splitCacheProducer, telemetryRuntimeProducer);
+ featureFlagsWorker.start();
ArgumentCaptor cnCaptor = ArgumentCaptor.forClass(Long.class);
- splitsWorker.addToQueue(1585956698457L);
- splitsWorker.addToQueue(1585956698467L);
- splitsWorker.addToQueue(1585956698477L);
- splitsWorker.addToQueue(1585956698476L);
+
+ featureFlagsWorker.addToQueue(new FeatureFlagChangeNotification(GenericNotificationData.builder()
+ .changeNumber(1585956698457L)
+ .build()));
+ featureFlagsWorker.addToQueue(new FeatureFlagChangeNotification(GenericNotificationData.builder()
+ .changeNumber(1585956698467L)
+ .build()));
+ featureFlagsWorker.addToQueue(new FeatureFlagChangeNotification(GenericNotificationData.builder()
+ .changeNumber(1585956698477L)
+ .build()));
+ featureFlagsWorker.addToQueue(new FeatureFlagChangeNotification(GenericNotificationData.builder()
+ .changeNumber(1585956698476L)
+ .build()));
Thread.sleep(1000);
Mockito.verify(syncMock, Mockito.times(4)).refreshSplits(cnCaptor.capture());
List captured = cnCaptor.getAllValues();
assertThat(captured, contains(1585956698457L, 1585956698467L, 1585956698477L, 1585956698476L));
- splitsWorker.stop();
+ featureFlagsWorker.stop();
}
@Test
public void killShouldTriggerFetch() {
long changeNumber = 1585956698457L;
- String splitName = "split-test";
+ String featureFlagName = "feature-flag-test";
String defaultTreatment = "off";
Synchronizer syncMock = Mockito.mock(Synchronizer.class);
- SplitsWorker splitsWorker = new SplitsWorkerImp(syncMock);
- splitsWorker.start();
-
- splitsWorker.killSplit(changeNumber, splitName, defaultTreatment);
- Mockito.verify(syncMock, Mockito.times(1)).localKillSplit(splitName, defaultTreatment, changeNumber);
- splitsWorker.stop();
+ SplitParser splitParser = new SplitParser();
+ SplitCacheProducer splitCacheProducer = Mockito.mock(SplitCacheProducer.class);
+ TelemetryRuntimeProducer telemetryRuntimeProducer = Mockito.mock(InMemoryTelemetryStorage.class);
+ FeatureFlagsWorker featureFlagsWorker = new FeatureFlagWorkerImp(syncMock, splitParser, splitCacheProducer, telemetryRuntimeProducer) {
+ };
+ featureFlagsWorker.start();
+ SplitKillNotification splitKillNotification = new SplitKillNotification(GenericNotificationData.builder()
+ .changeNumber(changeNumber)
+ .defaultTreatment(defaultTreatment)
+ .featureFlagName(featureFlagName)
+ .build());
+
+ featureFlagsWorker.kill(splitKillNotification);
+ Mockito.verify(syncMock, Mockito.times(1)).localKillSplit(splitKillNotification);
+ featureFlagsWorker.stop();
}
@Test
public void messagesNotProcessedWhenWorkerStopped() throws InterruptedException {
Synchronizer syncMock = Mockito.mock(Synchronizer.class);
- SplitsWorker splitsWorker = new SplitsWorkerImp(syncMock);
- splitsWorker.start();
- splitsWorker.addToQueue(1585956698457L);
+ SplitParser splitParser = new SplitParser();
+ SplitCacheProducer splitCacheProducer = Mockito.mock(SplitCacheProducer.class);
+ TelemetryRuntimeProducer telemetryRuntimeProducer = Mockito.mock(InMemoryTelemetryStorage.class);
+ FeatureFlagsWorker featureFlagsWorker = new FeatureFlagWorkerImp(syncMock, splitParser, splitCacheProducer, telemetryRuntimeProducer);
+ featureFlagsWorker.start();
+ featureFlagsWorker.addToQueue(new FeatureFlagChangeNotification(GenericNotificationData.builder()
+ .changeNumber(1585956698457L)
+ .build()));
Thread.sleep(500);
- splitsWorker.stop();
+ featureFlagsWorker.stop();
Thread.sleep(500);
- splitsWorker.addToQueue(1585956698467L);
- Mockito.verify(syncMock, Mockito.times(1)).refreshSplits(1585956698457L); // Previous one!
+ featureFlagsWorker.addToQueue(new FeatureFlagChangeNotification(GenericNotificationData.builder()
+ .changeNumber(1585956698467L)
+ .build()));
+ Mockito.verify(syncMock, Mockito.times(1)).refreshSplits(Mockito.anyObject()); // Previous one!
Mockito.reset(syncMock);
- splitsWorker.start();
- splitsWorker.addToQueue(1585956698477L);
+ featureFlagsWorker.start();
+ featureFlagsWorker.addToQueue(new FeatureFlagChangeNotification(GenericNotificationData.builder()
+ .changeNumber(1585956698477L)
+ .build()));
Thread.sleep(500);
- Mockito.verify(syncMock, Mockito.times(1)).refreshSplits(1585956698477L);
- splitsWorker.stop();
+ Mockito.verify(syncMock, Mockito.times(1)).refreshSplits(Mockito.anyObject());
+ featureFlagsWorker.stop();
}
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/telemetry/storage/InMemoryTelemetryStorageTest.java b/client/src/test/java/io/split/telemetry/storage/InMemoryTelemetryStorageTest.java
index 9f4986ad1..5bfc3b5d3 100644
--- a/client/src/test/java/io/split/telemetry/storage/InMemoryTelemetryStorageTest.java
+++ b/client/src/test/java/io/split/telemetry/storage/InMemoryTelemetryStorageTest.java
@@ -1,7 +1,21 @@
package io.split.telemetry.storage;
-import io.split.telemetry.domain.*;
-import io.split.telemetry.domain.enums.*;
+
+import io.split.telemetry.domain.HTTPErrors;
+import io.split.telemetry.domain.HTTPLatencies;
+import io.split.telemetry.domain.LastSynchronization;
+import io.split.telemetry.domain.MethodExceptions;
+import io.split.telemetry.domain.MethodLatencies;
+import io.split.telemetry.domain.StreamingEvent;
+import io.split.telemetry.domain.UpdatesFromSSE;
+
+import io.split.telemetry.domain.enums.EventsDataRecordsEnum;
+import io.split.telemetry.domain.enums.HTTPLatenciesEnum;
+import io.split.telemetry.domain.enums.ImpressionsDataTypeEnum;
+import io.split.telemetry.domain.enums.LastSynchronizationRecordsEnum;
+import io.split.telemetry.domain.enums.MethodEnum;
+import io.split.telemetry.domain.enums.ResourceEnum;
+import io.split.telemetry.domain.enums.UpdatesFromSSEEnum;
import org.junit.Assert;
import org.junit.Test;
@@ -10,7 +24,7 @@
public class InMemoryTelemetryStorageTest{
@Test
- public void testInMemoryTelemetryStorage() throws Exception {
+ public void testInMemoryTelemetryStorage() {
InMemoryTelemetryStorage telemetryStorage = new InMemoryTelemetryStorage();
//MethodLatencies
@@ -215,5 +229,12 @@ public void testInMemoryTelemetryStorage() throws Exception {
tags = telemetryStorage.popTags();
Assert.assertEquals(0, tags.size());
+ //UpdatesFromSSE
+ telemetryStorage.recordUpdatesFromSSE(UpdatesFromSSEEnum.SPLITS);
+ telemetryStorage.recordUpdatesFromSSE(UpdatesFromSSEEnum.SPLITS);
+ telemetryStorage.recordUpdatesFromSSE(UpdatesFromSSEEnum.SPLITS);
+
+ UpdatesFromSSE updatesFromSSE = telemetryStorage.popUpdatesFromSSE();
+ Assert.assertEquals(3, updatesFromSSE.getSplits());
}
-}
+}
\ No newline at end of file
diff --git a/client/src/test/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitterTest.java b/client/src/test/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitterTest.java
index 42913f5cd..e0dffb152 100644
--- a/client/src/test/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitterTest.java
+++ b/client/src/test/java/io/split/telemetry/synchronizer/TelemetryInMemorySubmitterTest.java
@@ -2,7 +2,6 @@
import io.split.TestHelper;
import io.split.client.dtos.UniqueKeys;
-import io.split.client.impressions.UniqueKeysTrackerImp;
import io.split.storages.SegmentCacheConsumer;
import io.split.storages.SplitCacheConsumer;
import io.split.client.ApiKeyCounter;
@@ -73,7 +72,7 @@ public void testSynchronizeUniqueKeys() throws Exception {
}
@Test
- public void testConfig() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, IOException, URISyntaxException, NoSuchFieldException, ClassNotFoundException {
+ public void testConfig() throws InvocationTargetException, NoSuchMethodException, IllegalAccessException, IOException, URISyntaxException, NoSuchFieldException {
ApiKeyCounter.getApiKeyCounterInstance().clearApiKeys();
ApiKeyCounter.getApiKeyCounterInstance().add(FIRST_KEY);
ApiKeyCounter.getApiKeyCounterInstance().add(FIRST_KEY);
@@ -154,9 +153,10 @@ public void testStats() throws Exception {
Assert.assertEquals(290, streamingEvents.get(0).get_data());
Assert.assertEquals(1, streamingEvents.get(0).get_type());
Assert.assertEquals(91218, streamingEvents.get(0).getTimestamp());
+ Assert.assertEquals(1, stats.get_updatesFromSSE().getSplits());
}
- private TelemetryInMemorySubmitter getTelemetrySynchronizer(CloseableHttpClient httpClient) throws URISyntaxException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, IOException {
+ private TelemetryInMemorySubmitter getTelemetrySynchronizer(CloseableHttpClient httpClient) throws URISyntaxException {
TelemetryStorageConsumer consumer = Mockito.mock(InMemoryTelemetryStorage.class);
TelemetryRuntimeProducer telemetryRuntimeProducer = Mockito.mock(TelemetryRuntimeProducer.class);
SplitCacheConsumer splitCacheConsumer = Mockito.mock(SplitCacheConsumer.class);
@@ -229,6 +229,7 @@ private void populateStats(TelemetryStorage telemetryStorage) {
StreamingEvent streamingEvent = new StreamingEvent(1, 290, 91218);
telemetryStorage.recordStreamingEvents(streamingEvent);
+ telemetryStorage.recordUpdatesFromSSE(UpdatesFromSSEEnum.SPLITS);
}
private void populateConfig(TelemetryStorage telemetryStorage) {
@@ -238,5 +239,4 @@ private void populateConfig(TelemetryStorage telemetryStorage) {
telemetryStorage.recordNonReadyUsage();
telemetryStorage.recordNonReadyUsage();
}
-
}
\ No newline at end of file
diff --git a/pluggable-storage/pom.xml b/pluggable-storage/pom.xml
index 973356e62..240e626f5 100644
--- a/pluggable-storage/pom.xml
+++ b/pluggable-storage/pom.xml
@@ -6,7 +6,7 @@
java-client-parent
io.split.client
- 4.7.2
+ 4.8.0
2.0.0
diff --git a/pom.xml b/pom.xml
index bcdb9e209..502fdce4b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
io.split.client
java-client-parent
- 4.7.2
+ 4.8.0
diff --git a/redis-wrapper/pom.xml b/redis-wrapper/pom.xml
index d91cee285..2e6c6da58 100644
--- a/redis-wrapper/pom.xml
+++ b/redis-wrapper/pom.xml
@@ -6,7 +6,7 @@
java-client-parent
io.split.client
- 4.7.2
+ 4.8.0
redis-wrapper
3.0.0
diff --git a/testing/pom.xml b/testing/pom.xml
index 8d146fd11..b75f26bd4 100644
--- a/testing/pom.xml
+++ b/testing/pom.xml
@@ -5,7 +5,7 @@
io.split.client
java-client-parent
- 4.7.2
+ 4.8.0
java-client-testing
jar