From cfd6febe1aead4d46f025291d75a8fe284bfb231 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 24 Apr 2025 16:15:38 -0700 Subject: [PATCH 01/12] updated storage, localhost and tests --- .../split/client/HttpSplitChangeFetcher.java | 29 +- .../JsonLocalhostSplitChangeFetcher.java | 16 +- .../io/split/client/SplitClientConfig.java | 2 +- .../java/io/split/client/dtos/ChangeDto.java | 9 + .../io/split/client/dtos/SplitChange.java | 1 + .../engine/experiments/SplitFetcherImp.java | 8 + .../RuleBasedSegmentCacheProducer.java | 1 + .../storages/memory/InMemoryCacheImp.java | 2 + .../RuleBasedSegmentCacheInMemoryImp.java | 2 + ...CustomRuleBasedSegmentAdapterProducer.java | 5 + .../client/HttpSplitChangeFetcherTest.java | 1 - .../JsonLocalhostSplitChangeFetcherTest.java | 19 + .../client/JsonLocalhostSplitFactoryTest.java | 17 + .../client/SplitClientIntegrationTest.java | 92 ++- client/src/test/resources/split_old_spec.json | 566 ++++++++++++++++++ 15 files changed, 747 insertions(+), 23 deletions(-) create mode 100644 client/src/test/resources/split_old_spec.json diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java index 1b084e24..7c20f206 100644 --- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java @@ -78,6 +78,8 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { if (SPEC_VERSION.equals(SPEC_1_1) && (System.currentTimeMillis() - _lastProxyCheckTimestamp >= PROXY_CHECK_INTERVAL_MILLISECONDS_SS)) { _log.info("Switching to new Feature flag spec ({}) and fetching.", SPEC_1_3); SPEC_VERSION = SPEC_1_3; + since = -1; + sinceRBS = -1; } URI uri = buildURL(options, since, sinceRBS); response = _client.get(uri, options, null); @@ -109,30 +111,17 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { SplitChange splitChange = new SplitChange(); if (SPEC_VERSION.equals(Spec.SPEC_1_1)) { splitChange.featureFlags = convertBodyToOldSpec(response.body()); - splitChange.ruleBasedSegments = createEmptyDTO(); + splitChange.ruleBasedSegments = ChangeDto.createEmptyDto(); } else { splitChange = Json.fromJson(response.body(), SplitChange.class); + if (SPEC_VERSION.equals(Spec.SPEC_1_3) && _lastProxyCheckTimestamp != 0) { + splitChange.clearCache = true; + _lastProxyCheckTimestamp = 0L; + } } return splitChange; } - public Long getLastProxyCheckTimestamp() { - return _lastProxyCheckTimestamp; - } - - public void setLastProxyCheckTimestamp(long lastProxyCheckTimestamp) { - synchronized (_lock) { - _lastProxyCheckTimestamp = lastProxyCheckTimestamp; - } - } - - private ChangeDto createEmptyDTO() { - ChangeDto dto = new ChangeDto<>(); - dto.d = new ArrayList<>(); - dto.t = -1; - dto.s = -1; - return dto; - } private ChangeDto convertBodyToOldSpec(String body) { return Json.fromJson(body, SplitChangesOldPayloadDto.class).toChangeDTO(); } @@ -140,7 +129,9 @@ private ChangeDto convertBodyToOldSpec(String body) { private URI buildURL(FetchOptions options, long since, long sinceRBS) throws URISyntaxException { URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + SPEC_VERSION); uriBuilder.addParameter(SINCE, "" + since); - uriBuilder.addParameter(RB_SINCE, "" + sinceRBS); + if (SPEC_VERSION.equals(SPEC_1_3)) { + uriBuilder.addParameter(RB_SINCE, "" + sinceRBS); + } if (!options.flagSetsFilter().isEmpty()) { uriBuilder.addParameter(SETS, "" + options.flagSetsFilter()); } diff --git a/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java b/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java index 554877a0..456b3b6e 100644 --- a/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java @@ -1,8 +1,10 @@ package io.split.client; +import com.google.gson.JsonObject; import com.google.gson.stream.JsonReader; import io.split.client.dtos.ChangeDto; import io.split.client.dtos.SplitChange; +import io.split.client.dtos.SplitChangesOldPayloadDto; import io.split.client.utils.InputStreamProvider; import io.split.client.utils.Json; import io.split.client.utils.LocalhostSanitizer; @@ -20,6 +22,8 @@ import java.util.ArrayList; import java.util.Arrays; +import static io.split.client.utils.Json.fromJson; + public class JsonLocalhostSplitChangeFetcher implements SplitChangeFetcher { private static final Logger _log = LoggerFactory.getLogger(JsonLocalhostSplitChangeFetcher.class); @@ -37,13 +41,23 @@ public JsonLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider) public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { try { JsonReader jsonReader = new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8))); - SplitChange splitChange = Json.fromJson(jsonReader, SplitChange.class); + if (checkOldSpec(new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8))))) { + SplitChange splitChange = new SplitChange(); + splitChange.featureFlags = Json.fromJson(jsonReader, SplitChangesOldPayloadDto.class).toChangeDTO(); + splitChange.ruleBasedSegments = ChangeDto.createEmptyDto(); + return splitChange; + } + SplitChange splitChange = fromJson(jsonReader, SplitChange.class); return processSplitChange(splitChange, since, sinceRBS); } catch (Exception e) { throw new IllegalStateException("Problem fetching splitChanges: " + e.getMessage(), e); } } + private boolean checkOldSpec(JsonReader jsonReader) { + return Json.fromJson(jsonReader, JsonObject.class).has("splits"); + } + private SplitChange processSplitChange(SplitChange splitChange, long changeNumber, long changeNumberRBS) throws NoSuchAlgorithmException { SplitChange splitChangeToProcess = LocalhostSanitizer.sanitization(splitChange); // if the till is less than storage CN and different from the default till ignore the change diff --git a/client/src/main/java/io/split/client/SplitClientConfig.java b/client/src/main/java/io/split/client/SplitClientConfig.java index fa73ef8c..c6a9605c 100644 --- a/client/src/main/java/io/split/client/SplitClientConfig.java +++ b/client/src/main/java/io/split/client/SplitClientConfig.java @@ -413,7 +413,7 @@ public CustomHeaderDecorator customHeaderDecorator() { } public boolean isRootURIOverriden() { - return _endpoint == SDK_ENDPOINT; + return _endpoint != SDK_ENDPOINT; } public CustomHttpModule alternativeHTTPModule() { return _alternativeHTTPModule; } diff --git a/client/src/main/java/io/split/client/dtos/ChangeDto.java b/client/src/main/java/io/split/client/dtos/ChangeDto.java index 596c05e0..d714e69a 100644 --- a/client/src/main/java/io/split/client/dtos/ChangeDto.java +++ b/client/src/main/java/io/split/client/dtos/ChangeDto.java @@ -1,9 +1,18 @@ package io.split.client.dtos; +import java.util.ArrayList; import java.util.List; public class ChangeDto { public long s; public long t; public List d; + + public static ChangeDto createEmptyDto() { + ChangeDto dto = new ChangeDto<>(); + dto.d = new ArrayList<>(); + dto.t = -1; + dto.s = -1; + return dto; + } } \ No newline at end of file diff --git a/client/src/main/java/io/split/client/dtos/SplitChange.java b/client/src/main/java/io/split/client/dtos/SplitChange.java index f15bf758..f3676bf7 100644 --- a/client/src/main/java/io/split/client/dtos/SplitChange.java +++ b/client/src/main/java/io/split/client/dtos/SplitChange.java @@ -7,4 +7,5 @@ public class SplitChange { public ChangeDto featureFlags; @SerializedName("rbs") public ChangeDto ruleBasedSegments; + public boolean clearCache; } 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 4b522f6a..5d7bc0fa 100644 --- a/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java +++ b/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java @@ -1,5 +1,6 @@ package io.split.engine.experiments; +import io.split.Spec; import io.split.client.dtos.ChangeDto; import io.split.client.dtos.RuleBasedSegment; import io.split.client.dtos.Split; @@ -20,6 +21,7 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; +import static io.split.Spec.SPEC_VERSION; import static io.split.client.utils.FeatureFlagProcessor.processFeatureFlagChanges; import static io.split.client.utils.RuleBasedSegmentProcessor.processRuleBasedSegmentChanges; @@ -124,6 +126,11 @@ private Set runWithoutExceptionHandling(FetchOptions options) throws Int throw new IllegalStateException("SplitChange was null"); } + if (change.clearCache) { + _splitCacheProducer.clear(); + _ruleBasedSegmentCacheProducer.clear(); + } + if (checkExitConditions(change.featureFlags, _splitCacheProducer.getChangeNumber()) || checkExitConditions(change.ruleBasedSegments, _ruleBasedSegmentCacheProducer.getChangeNumber())) { return segments; @@ -149,6 +156,7 @@ private Set runWithoutExceptionHandling(FetchOptions options) throws Int // some other thread may have updated the shared state. exit return segments; } + FeatureFlagsToUpdate featureFlagsToUpdate = processFeatureFlagChanges(_parser, change.featureFlags.d, _flagSetsFilter); segments = featureFlagsToUpdate.getSegments(); _splitCacheProducer.update(featureFlagsToUpdate.getToAdd(), featureFlagsToUpdate.getToRemove(), change.featureFlags.t); diff --git a/client/src/main/java/io/split/storages/RuleBasedSegmentCacheProducer.java b/client/src/main/java/io/split/storages/RuleBasedSegmentCacheProducer.java index e3c48047..02dd79e1 100644 --- a/client/src/main/java/io/split/storages/RuleBasedSegmentCacheProducer.java +++ b/client/src/main/java/io/split/storages/RuleBasedSegmentCacheProducer.java @@ -8,4 +8,5 @@ public interface RuleBasedSegmentCacheProducer extends RuleBasedSegmentCacheCom boolean remove(String name); void setChangeNumber(long changeNumber); void update(List toAdd, List toRemove, long changeNumber); + void clear(); } 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 8b89d6a6..57c63b99 100644 --- a/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java +++ b/client/src/main/java/io/split/storages/memory/InMemoryCacheImp.java @@ -140,7 +140,9 @@ public void kill(String splitName, String defaultTreatment, long changeNumber) { @Override public void clear() { _concurrentMap.clear(); + _changeNumber.set(-1); _concurrentTrafficTypeNameSet.clear(); + _flagSets.clear(); } @Override diff --git a/client/src/main/java/io/split/storages/memory/RuleBasedSegmentCacheInMemoryImp.java b/client/src/main/java/io/split/storages/memory/RuleBasedSegmentCacheInMemoryImp.java index 5bf1d468..7d1d205a 100644 --- a/client/src/main/java/io/split/storages/memory/RuleBasedSegmentCacheInMemoryImp.java +++ b/client/src/main/java/io/split/storages/memory/RuleBasedSegmentCacheInMemoryImp.java @@ -71,7 +71,9 @@ public List ruleBasedSegmentNames() { return ruleBasedSegmentNamesList; } + @Override public void clear() { + _changeNumber.set(-1); _concurrentMap.clear(); } diff --git a/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomRuleBasedSegmentAdapterProducer.java b/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomRuleBasedSegmentAdapterProducer.java index 87bee32b..2835cabb 100644 --- a/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomRuleBasedSegmentAdapterProducer.java +++ b/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomRuleBasedSegmentAdapterProducer.java @@ -44,6 +44,11 @@ public void setChangeNumber(long changeNumber) { //NoOp } + @Override + public void clear() { + //NoOp + } + @Override public void update(List toAdd, List toRemove, long changeNumber) { //NoOp diff --git a/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java b/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java index 37c52cd8..339df9f1 100644 --- a/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java +++ b/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java @@ -262,7 +262,6 @@ public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTarget Assert.assertEquals(0, change.ruleBasedSegments.d.size()); Assert.assertEquals(-1, change.ruleBasedSegments.s); Assert.assertEquals(-1, change.ruleBasedSegments.t); - Assert.assertTrue(fetcher.getLastProxyCheckTimestamp() > 0); // Set proxy interval to low number to force check for spec 1.3 Field proxyInterval = fetcher.getClass().getDeclaredField("PROXY_CHECK_INTERVAL_MILLISECONDS_SS"); diff --git a/client/src/test/java/io/split/client/JsonLocalhostSplitChangeFetcherTest.java b/client/src/test/java/io/split/client/JsonLocalhostSplitChangeFetcherTest.java index 512f6d8e..583dddab 100644 --- a/client/src/test/java/io/split/client/JsonLocalhostSplitChangeFetcherTest.java +++ b/client/src/test/java/io/split/client/JsonLocalhostSplitChangeFetcherTest.java @@ -201,4 +201,23 @@ public void processTestForException() { SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, -1, fetchOptions); } + + @Test + public void testParseOldSpec() throws FileNotFoundException { + InputStream inputStream = new FileInputStream("src/test/resources/split_old_spec.json"); + InputStreamProvider inputStreamProvider = new StaticContentInputStreamProvider(inputStream); + JsonLocalhostSplitChangeFetcher localhostSplitChangeFetcher = new JsonLocalhostSplitChangeFetcher(inputStreamProvider); + FetchOptions fetchOptions = Mockito.mock(FetchOptions.class); + + SplitChange splitChange = localhostSplitChangeFetcher.fetch(-1L, -1, fetchOptions); + + List split = splitChange.featureFlags.d; + Assert.assertEquals(7, split.size()); + Assert.assertEquals(1660326991072L, splitChange.featureFlags.t); + Assert.assertEquals(-1L, splitChange.featureFlags.s); + + Assert.assertEquals(new ArrayList<>(), splitChange.ruleBasedSegments.d); + Assert.assertEquals(-1L, splitChange.ruleBasedSegments.t); + Assert.assertEquals(-1L, splitChange.ruleBasedSegments.s); + } } \ No newline at end of file diff --git a/client/src/test/java/io/split/client/JsonLocalhostSplitFactoryTest.java b/client/src/test/java/io/split/client/JsonLocalhostSplitFactoryTest.java index b0ebf960..1152615a 100644 --- a/client/src/test/java/io/split/client/JsonLocalhostSplitFactoryTest.java +++ b/client/src/test/java/io/split/client/JsonLocalhostSplitFactoryTest.java @@ -32,4 +32,21 @@ public void works() throws IOException, URISyntaxException, InterruptedException Assert.assertEquals("on_whitelist", client.getTreatment("admin", "push_test")); client.destroy(); } + + @Test + public void testOldSpec() throws IOException, URISyntaxException, InterruptedException, TimeoutException { + SplitClientConfig config = SplitClientConfig.builder() + .splitFile("src/test/resources/split_old_spec.json") + .segmentDirectory("src/test/resources") + .setBlockUntilReadyTimeout(10000) + .build(); + SplitFactory splitFactory = SplitFactoryBuilder.build("localhost", config); + SplitClient client = splitFactory.client(); + client.blockUntilReady(); + + Assert.assertEquals("on", client.getTreatment("bilal", "split_1")); + Assert.assertEquals("off", client.getTreatment("bilal", "split_2")); + Assert.assertEquals("v5", client.getTreatment("admin", "split_2")); + client.destroy(); + } } \ No newline at end of file diff --git a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java index 51d55135..def17703 100644 --- a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java +++ b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java @@ -1,11 +1,13 @@ package io.split.client; import io.split.SSEMockServer; +import io.split.Spec; import io.split.SplitMockServer; import io.split.client.api.SplitView; import io.split.client.dtos.EvaluationOptions; import io.split.client.impressions.ImpressionsManager; import io.split.client.utils.CustomDispatcher; +import io.split.engine.experiments.SplitFetcherImp; import io.split.storages.enums.OperationMode; import io.split.storages.enums.StorageMode; import io.split.storages.pluggable.CustomStorageWrapperImp; @@ -24,6 +26,7 @@ import javax.ws.rs.sse.OutboundSseEvent; import java.io.IOException; +import java.lang.reflect.Field; import java.net.URISyntaxException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; @@ -246,6 +249,7 @@ public void managerSplitsWithStreamingEnabled() throws Exception { splitServer.stop(); sseServer.stop(); + factory.destroy(); } @Test @@ -641,6 +645,7 @@ public void testConnectionClosedByRemoteHostIsProperlyHandled() throws Exception Thread.sleep(1000); result = client.getTreatment("admin", "push_test"); Assert.assertNotEquals("on_whitelist", result); + client.destroy(); } @Test @@ -677,6 +682,7 @@ public void testConnectionClosedIsProperlyHandled() throws Exception { Thread.sleep(1000); result = client.getTreatment("admin", "push_test"); Assert.assertNotEquals("on_whitelist", result); + client.destroy(); } @Test @@ -745,7 +751,7 @@ public void testPluggableMode() throws IOException, URISyntaxException { Assert.assertNotNull(customStorageWrapper.getConfig()); String key = customStorageWrapper.getConfig().keySet().stream().collect(Collectors.toList()).get(0); Assert.assertTrue(customStorageWrapper.getConfig().get(key).contains(StorageMode.PLUGGABLE.name())); - + client.destroy(); } catch (TimeoutException | InterruptedException e) { } } @@ -1101,6 +1107,90 @@ public MockResponse dispatch(RecordedRequest request) { Assert.assertTrue(check2); } + @Test + public void oldSpecTest() throws Exception { + String splits = new String(Files.readAllBytes(Paths.get("src/test/resources/split_old_spec.json")), StandardCharsets.UTF_8); + String splits13 = new String(Files.readAllBytes(Paths.get("src/test/resources/split_init.json")), StandardCharsets.UTF_8); + String segment_1 = new String(Files.readAllBytes(Paths.get("src/test/resources/segment_1.json")), StandardCharsets.UTF_8); + List allRequests = new ArrayList<>(); + + class OldSpecDispatch extends Dispatcher { + public int initCode = 400; + @Override + public MockResponse dispatch (RecordedRequest request){ + allRequests.add(request); + switch (request.getPath()) { + case "/api/splitChanges?s=1.3&since=-1&rbSince=-1": + return new MockResponse().setResponseCode(initCode).setBody(splits13); + case "/api/splitChanges?s=1.1&since=-1": + return new MockResponse().setResponseCode(200).setBody(splits); + case "/api/splitChanges?s=1.1&since=1660326991072": + return new MockResponse().setResponseCode(200).setBody("{\"splits\": [], \"since\":1660326991072, \"till\":1660326991072}"); + case "/api/splitChanges?s=1.3&since=1660326991072&rbSince=-1": + return new MockResponse().setResponseCode(200).setBody("{\"ff\":{\"d\": [], \"s\":1660326991072, \"t\":1660326991072},\"rbs\":{\"t\":-1,\"s\":-1,\"d\":[]}}"); + case "/api/segmentChanges/segment_1?since=-1": + return new MockResponse().setResponseCode(200).setBody(segment_1); + case "/api/segmentChanges/segment_1?since=1585948850110": + return new MockResponse().setResponseCode(200).setBody("{\"name\": \"segment_1\",\"added\": [],\"removed\": [],\"since\": 1585948850110,\"till\": 1585948850110}"); + case "/api/testImpressions/bulk": + return new MockResponse().setResponseCode(200); + case "/api/testImpressions/count": + return new MockResponse().setResponseCode(200); + case "/v1/keys/ss": + return new MockResponse().setResponseCode(200); + case "/v1/metrics/usage": + return new MockResponse().setResponseCode(200); + case "/v1/metrics/config": + return new MockResponse().setResponseCode(200); + } + return new MockResponse().setResponseCode(404); + } + }; + + OldSpecDispatch dispatcher = new OldSpecDispatch(); + MockWebServer server = new MockWebServer(); + server.setDispatcher(dispatcher); + + server.start(); + String serverURL = String.format("http://%s:%s", server.getHostName(), server.getPort()); + SplitClientConfig config = SplitClientConfig.builder() + .setBlockUntilReadyTimeout(10000) + .endpoint(serverURL, serverURL) + .telemetryURL(serverURL + "/v1") + .authServiceURL(String.format("%s/api/auth/enabled", serverURL)) + .streamingEnabled(false) + .featuresRefreshRate(5) + .build(); + + SplitFactory factory = SplitFactoryBuilder.build("fake-api-token", config); + SplitClient client = factory.client(); + client.blockUntilReady(); + Assert.assertEquals(Spec.SPEC_1_1, Spec.SPEC_VERSION); + Assert.assertEquals("on", client.getTreatment("bilal", "split_1")); + Assert.assertEquals("off", client.getTreatment("bilal", "split_2")); + Assert.assertEquals("v5", client.getTreatment("admin", "split_2")); + + Field fetcher = factory.getClass().getDeclaredField("_splitFetcher"); + fetcher.setAccessible(true); + SplitFetcherImp splitFetcher = (SplitFetcherImp) fetcher.get(factory); + Field changeFetcher = splitFetcher.getClass().getDeclaredField("_splitChangeFetcher"); + changeFetcher.setAccessible(true); + HttpSplitChangeFetcher splitChangeFetcher = (HttpSplitChangeFetcher) changeFetcher.get(splitFetcher); + Field proxyInterval = splitChangeFetcher.getClass().getDeclaredField("PROXY_CHECK_INTERVAL_MILLISECONDS_SS"); + proxyInterval.setAccessible(true); + proxyInterval.set(splitChangeFetcher, 1); + dispatcher.initCode = 200; + Thread.sleep(6000); + + Assert.assertEquals(Spec.SPEC_1_3, Spec.SPEC_VERSION); + Assert.assertEquals("on", client.getTreatment("bilal", "split_1")); + Assert.assertEquals("off", client.getTreatment("bilal", "split_2")); + Assert.assertEquals("v5", client.getTreatment("admin", "split_2")); + + client.destroy(); + server.shutdown(); + } + private SSEMockServer buildSSEMockServer(SSEMockServer.SseEventQueue eventQueue) { return new SSEMockServer(eventQueue, (token, version, channel) -> { if (!"1.1".equals(version)) { diff --git a/client/src/test/resources/split_old_spec.json b/client/src/test/resources/split_old_spec.json new file mode 100644 index 00000000..66a05ca8 --- /dev/null +++ b/client/src/test/resources/split_old_spec.json @@ -0,0 +1,566 @@ +{"splits": [ + { + "trafficTypeName": "user", + "name": "split_1", + "trafficAllocation": 100, + "trafficAllocationSeed": -1364119282, + "seed": -605938843, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1660326991072, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 50 + }, + { + "treatment": "off", + "size": 50 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "split_2", + "trafficAllocation": 100, + "trafficAllocationSeed": -92391491, + "seed": -1769377604, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1651003069855, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin", + "user_1", + "user_2" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "v5", + "size": 100 + } + ], + "label": "whitelisted" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "IN_SEGMENT", + "negate": false, + "userDefinedSegmentMatcherData": { + "segmentName": "segment_1" + }, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 0 + } + ], + "label": "in segment segment_1" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "V4", + "size": 0 + }, + { + "treatment": "v5", + "size": 0 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "split_3", + "trafficAllocation": 100, + "trafficAllocationSeed": -670005248, + "seed": -1297078412, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1650919058695, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 + } + ], + "label": "whitelisted" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + }, + { + "treatment": "V5", + "size": 0 + }, + { + "treatment": "v8", + "size": 0 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "split_4", + "trafficAllocation": 50, + "trafficAllocationSeed": -1520910077, + "seed": -1785086567, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1647274074042, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "split_5", + "trafficAllocation": 100, + "trafficAllocationSeed": -3629915, + "seed": 816031817, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1622494310037, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "seba", + "tincho" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ], + "label": "whitelisted" + }, + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "user_3" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "off", + "size": 100 + } + ], + "label": "whitelisted" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "split_6", + "trafficAllocation": 100, + "trafficAllocationSeed": -970151859, + "seed": -1258287669, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1605020019151, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "WHITELIST", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": null, + "matcherType": "WHITELIST", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": { + "whitelist": [ + "admin" + ] + }, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + } + ], + "label": "whitelisted" + }, + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 0 + }, + { + "treatment": "off", + "size": 100 + } + ], + "label": "default rule" + } + ] + }, + { + "trafficTypeName": "user", + "name": "split_7", + "trafficAllocation": 100, + "trafficAllocationSeed": 291807630, + "seed": -134149800, + "status": "ACTIVE", + "killed": false, + "defaultTreatment": "off", + "changeNumber": 1603461301902, + "algo": 2, + "configurations": {}, + "conditions": [ + { + "conditionType": "ROLLOUT", + "matcherGroup": { + "combiner": "AND", + "matchers": [ + { + "keySelector": { + "trafficType": "user", + "attribute": null + }, + "matcherType": "ALL_KEYS", + "negate": false, + "userDefinedSegmentMatcherData": null, + "whitelistMatcherData": null, + "unaryNumericMatcherData": null, + "betweenMatcherData": null, + "booleanMatcherData": null, + "dependencyMatcherData": null, + "stringMatcherData": null + } + ] + }, + "partitions": [ + { + "treatment": "on", + "size": 100 + }, + { + "treatment": "off", + "size": 0 + } + ], + "label": "default rule" + } + ] + } + ], + "since": -1, + "till": 1660326991072 +} \ No newline at end of file From 5ab09fb697413297e555675d9167fe0c50ee62a9 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 24 Apr 2025 18:39:40 -0700 Subject: [PATCH 02/12] polish --- .../java/io/split/client/SplitClientIntegrationTest.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java index def17703..dc5782c7 100644 --- a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java +++ b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java @@ -1180,9 +1180,10 @@ public MockResponse dispatch (RecordedRequest request){ proxyInterval.setAccessible(true); proxyInterval.set(splitChangeFetcher, 1); dispatcher.initCode = 200; - Thread.sleep(6000); - Assert.assertEquals(Spec.SPEC_1_3, Spec.SPEC_VERSION); + Awaitility.await() + .atMost(10L, TimeUnit.SECONDS) + .until(() -> (Spec.SPEC_1_3.equals(Spec.SPEC_VERSION))); Assert.assertEquals("on", client.getTreatment("bilal", "split_1")); Assert.assertEquals("off", client.getTreatment("bilal", "split_2")); Assert.assertEquals("v5", client.getTreatment("admin", "split_2")); From 5db33400b9b9c9708cab5dfe6fa4baf93c1e3406 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Thu, 24 Apr 2025 19:02:50 -0700 Subject: [PATCH 03/12] polish --- .../test/java/io/split/client/SplitClientIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java index dc5782c7..ad4224a8 100644 --- a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java +++ b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java @@ -601,7 +601,7 @@ public void keepAlive() throws Exception { // must reconnect and after the second syncAll the result must be different Awaitility.await() - .atMost(1L, TimeUnit.MINUTES) + .atMost(3L, TimeUnit.MINUTES) .untilAsserted(() -> Assert.assertEquals("split_killed", client.getTreatment("admin", "push_test"))); client.destroy(); From 904ec280efe0b747ba143f1f5d7ff7966dbfd67a Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 25 Apr 2025 11:55:36 -0700 Subject: [PATCH 04/12] polish --- client/src/main/java/io/split/Spec.java | 1 - .../split/client/HttpSplitChangeFetcher.java | 21 +++++++++---------- .../engine/experiments/SplitFetcherImp.java | 2 +- .../io/split/engine/sse/AuthApiClientImp.java | 4 ++-- .../client/HttpSplitChangeFetcherTest.java | 9 ++++---- .../client/SplitClientIntegrationTest.java | 13 ++++++------ .../split/client/utils/CustomDispatcher.java | 4 +++- .../SegmentSynchronizationTaskImpTest.java | 2 +- 8 files changed, 29 insertions(+), 27 deletions(-) diff --git a/client/src/main/java/io/split/Spec.java b/client/src/main/java/io/split/Spec.java index 79f9a4bc..05d73aba 100644 --- a/client/src/main/java/io/split/Spec.java +++ b/client/src/main/java/io/split/Spec.java @@ -9,6 +9,5 @@ private Spec() { // TODO: Change the schema to 1.3 when updating splitclient public static final String SPEC_1_3 = "1.3"; public static final String SPEC_1_1 = "1.1"; - public static String SPEC_VERSION = SPEC_1_3; } diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java index 7c20f206..3cdc2ac3 100644 --- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java @@ -5,7 +5,6 @@ import io.split.Spec; import io.split.client.dtos.SplitChange; import io.split.client.dtos.SplitHttpResponse; -import io.split.client.dtos.RuleBasedSegment; import io.split.client.dtos.SplitChangesOldPayloadDto; import io.split.client.dtos.ChangeDto; import io.split.client.dtos.Split; @@ -25,10 +24,8 @@ import java.net.URI; import java.net.URISyntaxException; -import java.util.ArrayList; import static com.google.common.base.Preconditions.checkNotNull; -import static io.split.Spec.SPEC_VERSION; import static io.split.Spec.SPEC_1_3; import static io.split.Spec.SPEC_1_1; @@ -44,6 +41,7 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher { private static final String TILL = "till"; private static final String SETS = "sets"; private static final String SPEC = "s"; + private String specVersion = SPEC_1_3; private int PROXY_CHECK_INTERVAL_MILLISECONDS_SS = 24 * 60 * 60 * 1000; private Long _lastProxyCheckTimestamp = 0L; private final SplitHttpClient _client; @@ -75,13 +73,14 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { long start = System.currentTimeMillis(); SplitHttpResponse response; try { - if (SPEC_VERSION.equals(SPEC_1_1) && (System.currentTimeMillis() - _lastProxyCheckTimestamp >= PROXY_CHECK_INTERVAL_MILLISECONDS_SS)) { + if (specVersion.equals(SPEC_1_1) && (System.currentTimeMillis() - _lastProxyCheckTimestamp >= PROXY_CHECK_INTERVAL_MILLISECONDS_SS)) { _log.info("Switching to new Feature flag spec ({}) and fetching.", SPEC_1_3); - SPEC_VERSION = SPEC_1_3; + specVersion = SPEC_1_3; since = -1; sinceRBS = -1; } URI uri = buildURL(options, since, sinceRBS); + _log.error(uri.toString()); response = _client.get(uri, options, null); if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) { if (response.statusCode() == HttpStatus.SC_REQUEST_URI_TOO_LONG) { @@ -89,8 +88,8 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { throw new UriTooLongException(String.format("Status code: %s. Message: %s", response.statusCode(), response.statusMessage())); } - if (response.statusCode() == HttpStatus.SC_BAD_REQUEST && SPEC_VERSION.equals(Spec.SPEC_1_3) && _rootURIOverriden) { - SPEC_VERSION = Spec.SPEC_1_1; + if (response.statusCode() == HttpStatus.SC_BAD_REQUEST && specVersion.equals(Spec.SPEC_1_3) && _rootURIOverriden) { + specVersion = Spec.SPEC_1_1; _log.warn("Detected proxy without support for Feature flags spec {} version, will switch to spec version {}", SPEC_1_3, SPEC_1_1); _lastProxyCheckTimestamp = System.currentTimeMillis(); @@ -109,12 +108,12 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { } SplitChange splitChange = new SplitChange(); - if (SPEC_VERSION.equals(Spec.SPEC_1_1)) { + if (specVersion.equals(Spec.SPEC_1_1)) { splitChange.featureFlags = convertBodyToOldSpec(response.body()); splitChange.ruleBasedSegments = ChangeDto.createEmptyDto(); } else { splitChange = Json.fromJson(response.body(), SplitChange.class); - if (SPEC_VERSION.equals(Spec.SPEC_1_3) && _lastProxyCheckTimestamp != 0) { + if (specVersion.equals(Spec.SPEC_1_3) && _lastProxyCheckTimestamp != 0) { splitChange.clearCache = true; _lastProxyCheckTimestamp = 0L; } @@ -127,9 +126,9 @@ private ChangeDto convertBodyToOldSpec(String body) { } private URI buildURL(FetchOptions options, long since, long sinceRBS) throws URISyntaxException { - URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + SPEC_VERSION); + URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + specVersion); uriBuilder.addParameter(SINCE, "" + since); - if (SPEC_VERSION.equals(SPEC_1_3)) { + if (specVersion.equals(SPEC_1_3)) { uriBuilder.addParameter(RB_SINCE, "" + sinceRBS); } if (!options.flagSetsFilter().isEmpty()) { 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 5d7bc0fa..72f95552 100644 --- a/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java +++ b/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java @@ -21,7 +21,7 @@ import java.util.Set; import static com.google.common.base.Preconditions.checkNotNull; -import static io.split.Spec.SPEC_VERSION; + import static io.split.client.utils.FeatureFlagProcessor.processFeatureFlagChanges; import static io.split.client.utils.RuleBasedSegmentProcessor.processRuleBasedSegmentChanges; diff --git a/client/src/main/java/io/split/engine/sse/AuthApiClientImp.java b/client/src/main/java/io/split/engine/sse/AuthApiClientImp.java index 28464ebd..5c45e1b7 100644 --- a/client/src/main/java/io/split/engine/sse/AuthApiClientImp.java +++ b/client/src/main/java/io/split/engine/sse/AuthApiClientImp.java @@ -1,6 +1,7 @@ package io.split.engine.sse; import com.google.gson.JsonObject; +import io.split.Spec; import io.split.client.dtos.SplitHttpResponse; import io.split.client.utils.Json; import io.split.engine.common.FetchOptions; @@ -18,7 +19,6 @@ import java.net.URI; import static com.google.common.base.Preconditions.checkNotNull; -import static io.split.Spec.SPEC_VERSION; public class AuthApiClientImp implements AuthApiClient { private static final Logger _log = LoggerFactory.getLogger(AuthApiClientImp.class); @@ -38,7 +38,7 @@ public AuthApiClientImp(String url, SplitHttpClient httpClient, TelemetryRuntime public AuthenticationResponse Authenticate() { try { long initTime = System.currentTimeMillis(); - URI uri = new URIBuilder(_target).addParameter(SPEC, "" + SPEC_VERSION).build(); + URI uri = new URIBuilder(_target).addParameter(SPEC, "" + Spec.SPEC_1_3).build(); SplitHttpResponse response = _httpClient.get(uri, new FetchOptions.Builder().cacheControlHeaders(false).build(), null); Integer statusCode = response.statusCode(); diff --git a/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java b/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java index 339df9f1..4c45589e 100644 --- a/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java +++ b/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java @@ -198,7 +198,7 @@ public void testURLTooLong() throws IOException, URISyntaxException, IllegalAcce @Test public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, IOException, NoSuchFieldException, InterruptedException { - Spec.SPEC_VERSION = Spec.SPEC_1_3; +// Spec.SPEC_VERSION = Spec.SPEC_1_3; URI rootTarget = URI.create("https://api.split.io"); CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class); HttpEntity entityMock = Mockito.mock(HttpEntity.class); @@ -247,9 +247,12 @@ public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTarget HttpSplitChangeFetcher fetcher = HttpSplitChangeFetcher.create(splitHtpClient, rootTarget, Mockito.mock(TelemetryRuntimeProducer.class), true); + Field specVersion = fetcher.getClass().getDeclaredField("specVersion"); + specVersion.setAccessible(true); + specVersion.set(fetcher, Spec.SPEC_1_1); + SplitChange change = fetcher.fetch(-1, -1, new FetchOptions.Builder().cacheControlHeaders(true).build()); - Assert.assertEquals(Spec.SPEC_1_1, Spec.SPEC_VERSION); List captured = requestCaptor.getAllValues(); Assert.assertEquals(captured.size(), 2); Assert.assertTrue(captured.get(0).getUri().toString().contains("s=1.3")); @@ -270,7 +273,6 @@ public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTarget Thread.sleep(1000); change = fetcher.fetch(-1, -1, new FetchOptions.Builder().cacheControlHeaders(true).build()); - Assert.assertEquals(Spec.SPEC_1_1, Spec.SPEC_VERSION); Assert.assertTrue(captured.get(2).getUri().toString().contains("s=1.3")); Assert.assertTrue(captured.get(3).getUri().toString().contains("s=1.1")); Assert.assertEquals(122, change.featureFlags.s); @@ -282,7 +284,6 @@ public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTarget // test if proxy is upgraded and spec 1.3 now works. Thread.sleep(1000); change = fetcher.fetch(-1, -1, new FetchOptions.Builder().cacheControlHeaders(true).build()); - Assert.assertEquals(Spec.SPEC_1_3, Spec.SPEC_VERSION); Assert.assertTrue(captured.get(4).getUri().toString().contains("s=1.3")); Assert.assertEquals(122, change.featureFlags.s); Assert.assertEquals(123, change.featureFlags.t); diff --git a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java index ad4224a8..0e358b4a 100644 --- a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java +++ b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java @@ -577,9 +577,11 @@ public void keepAlive() throws Exception { Queue responses = new LinkedList<>(); responses.add(response); - SplitMockServer splitServer = new SplitMockServer(CustomDispatcher.builder() + CustomDispatcher dispatcher = CustomDispatcher.builder() .path(CustomDispatcher.SINCE_1585948850109, responses) - .build()); + .build(); + dispatcher.bodySince1585948850109 = "{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850109}, \"rbs\":{\"d\":[],\"s\":1585948850109,\"t\":1585948850109}}"; + SplitMockServer splitServer = new SplitMockServer(dispatcher); //plitMockServer splitServer = new SplitMockServer(CustomDispatcher.builder().build()); SSEMockServer.SseEventQueue eventQueue = new SSEMockServer.SseEventQueue(); @@ -592,16 +594,16 @@ public void keepAlive() throws Exception { SplitFactory factory = SplitFactoryBuilder.build("fake-api-token-1", config); SplitClient client = factory.client(); client.blockUntilReady(); + dispatcher.bodySince1585948850109 = "{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850110}, \"rbs\":{\"s\":1585948850109,\"t\":1585948850110,\"d\":[]}}"; String result = client.getTreatment("admin", "push_test"); Assert.assertEquals("on_whitelist", result); // wait to check keep alive notification. Thread.sleep(50000); - // must reconnect and after the second syncAll the result must be different Awaitility.await() - .atMost(3L, TimeUnit.MINUTES) + .atMost(1L, TimeUnit.MINUTES) .untilAsserted(() -> Assert.assertEquals("split_killed", client.getTreatment("admin", "push_test"))); client.destroy(); @@ -1165,7 +1167,6 @@ public MockResponse dispatch (RecordedRequest request){ SplitFactory factory = SplitFactoryBuilder.build("fake-api-token", config); SplitClient client = factory.client(); client.blockUntilReady(); - Assert.assertEquals(Spec.SPEC_1_1, Spec.SPEC_VERSION); Assert.assertEquals("on", client.getTreatment("bilal", "split_1")); Assert.assertEquals("off", client.getTreatment("bilal", "split_2")); Assert.assertEquals("v5", client.getTreatment("admin", "split_2")); @@ -1183,7 +1184,7 @@ public MockResponse dispatch (RecordedRequest request){ Awaitility.await() .atMost(10L, TimeUnit.SECONDS) - .until(() -> (Spec.SPEC_1_3.equals(Spec.SPEC_VERSION))); + .until(() -> (Spec.SPEC_1_3.equals(Spec.SPEC_1_3))); Assert.assertEquals("on", client.getTreatment("bilal", "split_1")); Assert.assertEquals("off", client.getTreatment("bilal", "split_2")); Assert.assertEquals("v5", client.getTreatment("admin", "split_2")); diff --git a/client/src/test/java/io/split/client/utils/CustomDispatcher.java b/client/src/test/java/io/split/client/utils/CustomDispatcher.java index a9938204..9ee9ac39 100644 --- a/client/src/test/java/io/split/client/utils/CustomDispatcher.java +++ b/client/src/test/java/io/split/client/utils/CustomDispatcher.java @@ -37,6 +37,8 @@ public static CustomDispatcher.Builder builder() { return new CustomDispatcher.Builder(); } + public String bodySince1585948850109 = "{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850110}, \"rbs\":{\"s\":1585948850109,\"t\":1585948850110,\"d\":[]}}"; + @NotNull @Override public MockResponse dispatch(@NotNull RecordedRequest request) { @@ -50,7 +52,7 @@ public MockResponse dispatch(@NotNull RecordedRequest request) { case CustomDispatcher.AUTH_DISABLED: return getResponse(CustomDispatcher.AUTH_DISABLED,new MockResponse().setBody(inputStreamToString("streaming-auth-push-disabled.json"))); case CustomDispatcher.SINCE_1585948850109: - return getResponse(CustomDispatcher.SINCE_1585948850109, new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850110}, \"rbs\":{\"s\":1585948850109,\"t\":1585948850110,\"d\":[]}}")); + return getResponse(CustomDispatcher.SINCE_1585948850109, new MockResponse().setBody(bodySince1585948850109)); case SINCE_1585948850109_FLAG_SET: return getResponse(SINCE_1585948850109_FLAG_SET, new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850110}, \"rbs\":{\"s\":1585948850109,\"t\":1585948850110,\"d\":[]}}")); case CustomDispatcher.SINCE_1585948850110: 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 5270c65a..0544ddae 100644 --- a/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java +++ b/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java @@ -171,7 +171,7 @@ public void testLocalhostSegmentChangeFetcher() throws InterruptedException, Fil ruleBasedSegmentParser, ruleBasedSegmentCacheProducer); SplitSynchronizationTask splitSynchronizationTask = new SplitSynchronizationTask(splitFetcher, splitCacheProducer, 1000, null); - Spec.SPEC_VERSION = Spec.SPEC_1_1; // check old spec +// Spec.SPEC_VERSION = Spec.SPEC_1_1; // check old spec splitSynchronizationTask.start(); From c8f8533af688a78a87d2aa9043a7456d6e0c5832 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 25 Apr 2025 12:12:39 -0700 Subject: [PATCH 05/12] polish --- client/src/main/java/io/split/client/HttpSplitChangeFetcher.java | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java index 3cdc2ac3..d6bd0c98 100644 --- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java @@ -80,7 +80,6 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { sinceRBS = -1; } URI uri = buildURL(options, since, sinceRBS); - _log.error(uri.toString()); response = _client.get(uri, options, null); if (response.statusCode() < HttpStatus.SC_OK || response.statusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) { if (response.statusCode() == HttpStatus.SC_REQUEST_URI_TOO_LONG) { From 00402ebb048964842f44d632defc8b51406f5fcf Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Fri, 25 Apr 2025 20:32:02 -0700 Subject: [PATCH 06/12] resolve conflicts --- .../io/split/client/HttpSplitChangeFetcher.java | 16 ++++++++-------- .../client/JsonLocalhostSplitChangeFetcher.java | 12 ++++++++++++ .../java/io/split/client/SplitClientConfig.java | 4 ++-- .../engine/experiments/SplitFetcherImp.java | 2 -- ...serCustomRuleBasedSegmentAdapterProducer.java | 1 - .../split/client/HttpSplitChangeFetcherTest.java | 5 ----- .../io/split/client/utils/CustomDispatcher.java | 4 +--- .../SegmentSynchronizationTaskImpTest.java | 1 - 8 files changed, 23 insertions(+), 22 deletions(-) diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java index 1b084e24..4754d608 100644 --- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java @@ -28,7 +28,6 @@ import java.util.ArrayList; import static com.google.common.base.Preconditions.checkNotNull; -import static io.split.Spec.SPEC_VERSION; import static io.split.Spec.SPEC_1_3; import static io.split.Spec.SPEC_1_1; @@ -44,6 +43,7 @@ public final class HttpSplitChangeFetcher implements SplitChangeFetcher { private static final String TILL = "till"; private static final String SETS = "sets"; private static final String SPEC = "s"; + private String specVersion = SPEC_1_3; private int PROXY_CHECK_INTERVAL_MILLISECONDS_SS = 24 * 60 * 60 * 1000; private Long _lastProxyCheckTimestamp = 0L; private final SplitHttpClient _client; @@ -75,9 +75,9 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { long start = System.currentTimeMillis(); SplitHttpResponse response; try { - if (SPEC_VERSION.equals(SPEC_1_1) && (System.currentTimeMillis() - _lastProxyCheckTimestamp >= PROXY_CHECK_INTERVAL_MILLISECONDS_SS)) { + if (specVersion.equals(SPEC_1_1) && (System.currentTimeMillis() - _lastProxyCheckTimestamp >= PROXY_CHECK_INTERVAL_MILLISECONDS_SS)) { _log.info("Switching to new Feature flag spec ({}) and fetching.", SPEC_1_3); - SPEC_VERSION = SPEC_1_3; + specVersion = SPEC_1_3; } URI uri = buildURL(options, since, sinceRBS); response = _client.get(uri, options, null); @@ -87,8 +87,8 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { throw new UriTooLongException(String.format("Status code: %s. Message: %s", response.statusCode(), response.statusMessage())); } - if (response.statusCode() == HttpStatus.SC_BAD_REQUEST && SPEC_VERSION.equals(Spec.SPEC_1_3) && _rootURIOverriden) { - SPEC_VERSION = Spec.SPEC_1_1; + if (response.statusCode() == HttpStatus.SC_BAD_REQUEST && specVersion.equals(Spec.SPEC_1_3) && _rootURIOverriden) { + specVersion = Spec.SPEC_1_1; _log.warn("Detected proxy without support for Feature flags spec {} version, will switch to spec version {}", SPEC_1_3, SPEC_1_1); _lastProxyCheckTimestamp = System.currentTimeMillis(); @@ -107,7 +107,7 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { } SplitChange splitChange = new SplitChange(); - if (SPEC_VERSION.equals(Spec.SPEC_1_1)) { + if (specVersion.equals(Spec.SPEC_1_1)) { splitChange.featureFlags = convertBodyToOldSpec(response.body()); splitChange.ruleBasedSegments = createEmptyDTO(); } else { @@ -134,11 +134,11 @@ private ChangeDto createEmptyDTO() { return dto; } private ChangeDto convertBodyToOldSpec(String body) { - return Json.fromJson(body, SplitChangesOldPayloadDto.class).toChangeDTO(); + return Json.fromJson(body, SplitChangesOldPayloadDto.class).toSplitChange().featureFlags; } private URI buildURL(FetchOptions options, long since, long sinceRBS) throws URISyntaxException { - URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + SPEC_VERSION); + URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + specVersion); uriBuilder.addParameter(SINCE, "" + since); uriBuilder.addParameter(RB_SINCE, "" + sinceRBS); if (!options.flagSetsFilter().isEmpty()) { diff --git a/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java b/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java index 554877a0..6be74846 100644 --- a/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java @@ -1,8 +1,10 @@ package io.split.client; +import com.google.gson.JsonObject; import com.google.gson.stream.JsonReader; import io.split.client.dtos.ChangeDto; import io.split.client.dtos.SplitChange; +import io.split.client.dtos.SplitChangesOldPayloadDto; import io.split.client.utils.InputStreamProvider; import io.split.client.utils.Json; import io.split.client.utils.LocalhostSanitizer; @@ -37,6 +39,12 @@ public JsonLocalhostSplitChangeFetcher(InputStreamProvider inputStreamProvider) public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { try { JsonReader jsonReader = new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8))); + if (checkOldSpec(new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8))))) { + SplitChange splitChange = new SplitChange(); + splitChange.featureFlags = Json.fromJson(jsonReader, SplitChangesOldPayloadDto.class).toSplitChange().featureFlags; + splitChange.ruleBasedSegments = ChangeDto.createEmptyDto(); + return splitChange; + } SplitChange splitChange = Json.fromJson(jsonReader, SplitChange.class); return processSplitChange(splitChange, since, sinceRBS); } catch (Exception e) { @@ -44,6 +52,10 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { } } + private boolean checkOldSpec(JsonReader jsonReader) { + return Json.fromJson(jsonReader, JsonObject.class).has("splits"); + } + private SplitChange processSplitChange(SplitChange splitChange, long changeNumber, long changeNumberRBS) throws NoSuchAlgorithmException { SplitChange splitChangeToProcess = LocalhostSanitizer.sanitization(splitChange); // if the till is less than storage CN and different from the default till ignore the change diff --git a/client/src/main/java/io/split/client/SplitClientConfig.java b/client/src/main/java/io/split/client/SplitClientConfig.java index fa73ef8c..fd312c3b 100644 --- a/client/src/main/java/io/split/client/SplitClientConfig.java +++ b/client/src/main/java/io/split/client/SplitClientConfig.java @@ -412,8 +412,8 @@ public CustomHeaderDecorator customHeaderDecorator() { return _customHeaderDecorator; } - public boolean isRootURIOverriden() { - return _endpoint == SDK_ENDPOINT; + public boolean isSdkEndpointOverridden() { + return !_endpoint.equals(SDK_ENDPOINT); } public CustomHttpModule alternativeHTTPModule() { return _alternativeHTTPModule; } 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 4b522f6a..2ecb9920 100644 --- a/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java +++ b/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java @@ -1,8 +1,6 @@ package io.split.engine.experiments; import io.split.client.dtos.ChangeDto; -import io.split.client.dtos.RuleBasedSegment; -import io.split.client.dtos.Split; import io.split.client.dtos.SplitChange; import io.split.client.exceptions.UriTooLongException; import io.split.client.interceptors.FlagSetsFilter; diff --git a/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomRuleBasedSegmentAdapterProducer.java b/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomRuleBasedSegmentAdapterProducer.java index 2835cabb..a143b95a 100644 --- a/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomRuleBasedSegmentAdapterProducer.java +++ b/client/src/main/java/io/split/storages/pluggable/adapters/UserCustomRuleBasedSegmentAdapterProducer.java @@ -54,7 +54,6 @@ public void update(List toAdd, List toRemove, lo //NoOp } - @Override public Set getSegments() { //NoOp return new HashSet<>(); diff --git a/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java b/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java index 37c52cd8..9a95727d 100644 --- a/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java +++ b/client/src/test/java/io/split/client/HttpSplitChangeFetcherTest.java @@ -198,7 +198,6 @@ public void testURLTooLong() throws IOException, URISyntaxException, IllegalAcce @Test public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTargetException, NoSuchMethodException, IllegalAccessException, IOException, NoSuchFieldException, InterruptedException { - Spec.SPEC_VERSION = Spec.SPEC_1_3; URI rootTarget = URI.create("https://api.split.io"); CloseableHttpClient httpClientMock = Mockito.mock(CloseableHttpClient.class); HttpEntity entityMock = Mockito.mock(HttpEntity.class); @@ -249,7 +248,6 @@ public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTarget SplitChange change = fetcher.fetch(-1, -1, new FetchOptions.Builder().cacheControlHeaders(true).build()); - Assert.assertEquals(Spec.SPEC_1_1, Spec.SPEC_VERSION); List captured = requestCaptor.getAllValues(); Assert.assertEquals(captured.size(), 2); Assert.assertTrue(captured.get(0).getUri().toString().contains("s=1.3")); @@ -262,7 +260,6 @@ public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTarget Assert.assertEquals(0, change.ruleBasedSegments.d.size()); Assert.assertEquals(-1, change.ruleBasedSegments.s); Assert.assertEquals(-1, change.ruleBasedSegments.t); - Assert.assertTrue(fetcher.getLastProxyCheckTimestamp() > 0); // Set proxy interval to low number to force check for spec 1.3 Field proxyInterval = fetcher.getClass().getDeclaredField("PROXY_CHECK_INTERVAL_MILLISECONDS_SS"); @@ -271,7 +268,6 @@ public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTarget Thread.sleep(1000); change = fetcher.fetch(-1, -1, new FetchOptions.Builder().cacheControlHeaders(true).build()); - Assert.assertEquals(Spec.SPEC_1_1, Spec.SPEC_VERSION); Assert.assertTrue(captured.get(2).getUri().toString().contains("s=1.3")); Assert.assertTrue(captured.get(3).getUri().toString().contains("s=1.1")); Assert.assertEquals(122, change.featureFlags.s); @@ -283,7 +279,6 @@ public void testSwitchingToOldSpec() throws URISyntaxException, InvocationTarget // test if proxy is upgraded and spec 1.3 now works. Thread.sleep(1000); change = fetcher.fetch(-1, -1, new FetchOptions.Builder().cacheControlHeaders(true).build()); - Assert.assertEquals(Spec.SPEC_1_3, Spec.SPEC_VERSION); Assert.assertTrue(captured.get(4).getUri().toString().contains("s=1.3")); Assert.assertEquals(122, change.featureFlags.s); Assert.assertEquals(123, change.featureFlags.t); diff --git a/client/src/test/java/io/split/client/utils/CustomDispatcher.java b/client/src/test/java/io/split/client/utils/CustomDispatcher.java index 9ee9ac39..a9938204 100644 --- a/client/src/test/java/io/split/client/utils/CustomDispatcher.java +++ b/client/src/test/java/io/split/client/utils/CustomDispatcher.java @@ -37,8 +37,6 @@ public static CustomDispatcher.Builder builder() { return new CustomDispatcher.Builder(); } - public String bodySince1585948850109 = "{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850110}, \"rbs\":{\"s\":1585948850109,\"t\":1585948850110,\"d\":[]}}"; - @NotNull @Override public MockResponse dispatch(@NotNull RecordedRequest request) { @@ -52,7 +50,7 @@ public MockResponse dispatch(@NotNull RecordedRequest request) { case CustomDispatcher.AUTH_DISABLED: return getResponse(CustomDispatcher.AUTH_DISABLED,new MockResponse().setBody(inputStreamToString("streaming-auth-push-disabled.json"))); case CustomDispatcher.SINCE_1585948850109: - return getResponse(CustomDispatcher.SINCE_1585948850109, new MockResponse().setBody(bodySince1585948850109)); + return getResponse(CustomDispatcher.SINCE_1585948850109, new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850110}, \"rbs\":{\"s\":1585948850109,\"t\":1585948850110,\"d\":[]}}")); case SINCE_1585948850109_FLAG_SET: return getResponse(SINCE_1585948850109_FLAG_SET, new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850110}, \"rbs\":{\"s\":1585948850109,\"t\":1585948850110,\"d\":[]}}")); case CustomDispatcher.SINCE_1585948850110: 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 5270c65a..ae33691e 100644 --- a/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java +++ b/client/src/test/java/io/split/engine/segments/SegmentSynchronizationTaskImpTest.java @@ -171,7 +171,6 @@ public void testLocalhostSegmentChangeFetcher() throws InterruptedException, Fil ruleBasedSegmentParser, ruleBasedSegmentCacheProducer); SplitSynchronizationTask splitSynchronizationTask = new SplitSynchronizationTask(splitFetcher, splitCacheProducer, 1000, null); - Spec.SPEC_VERSION = Spec.SPEC_1_1; // check old spec splitSynchronizationTask.start(); From fe83f0d714878f3b4ba243d4d25c4701577cfa83 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Sat, 26 Apr 2025 17:13:52 -0700 Subject: [PATCH 07/12] polish --- .../split/client/HttpSplitChangeFetcher.java | 46 ++++++------------- .../JsonLocalhostSplitChangeFetcher.java | 13 ++---- .../client/dtos/RuleBasedSegmentChange.java | 9 ---- .../engine/experiments/SplitFetcherImp.java | 11 +++-- .../client/SplitClientIntegrationTest.java | 14 +++--- 5 files changed, 30 insertions(+), 63 deletions(-) delete mode 100644 client/src/main/java/io/split/client/dtos/RuleBasedSegmentChange.java diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java index 4754d608..77d3b26a 100644 --- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java @@ -5,10 +5,7 @@ import io.split.Spec; import io.split.client.dtos.SplitChange; import io.split.client.dtos.SplitHttpResponse; -import io.split.client.dtos.RuleBasedSegment; import io.split.client.dtos.SplitChangesOldPayloadDto; -import io.split.client.dtos.ChangeDto; -import io.split.client.dtos.Split; import io.split.client.exceptions.UriTooLongException; import io.split.client.utils.Json; import io.split.client.utils.Utils; @@ -25,7 +22,6 @@ import java.net.URI; import java.net.URISyntaxException; -import java.util.ArrayList; import static com.google.common.base.Preconditions.checkNotNull; import static io.split.Spec.SPEC_1_3; @@ -100,41 +96,27 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { String.format("Could not retrieve splitChanges since %s; http return code %s", since, response.statusCode()) ); } + + SplitChange splitChange = new SplitChange(); + if (specVersion.equals(Spec.SPEC_1_1)) { + splitChange = convertBodyToOldSpec(response.body()); + } else { + if (_lastProxyCheckTimestamp != 0) { + splitChange.clearCache = true; + _lastProxyCheckTimestamp = 0L; + } + splitChange = Json.fromJson(response.body(), SplitChange.class); + } + return splitChange; } catch (Exception e) { throw new IllegalStateException(String.format("Problem fetching splitChanges since %s: %s", since, e), e); } finally { _telemetryRuntimeProducer.recordSyncLatency(HTTPLatenciesEnum.SPLITS, System.currentTimeMillis() - start); } - - SplitChange splitChange = new SplitChange(); - if (specVersion.equals(Spec.SPEC_1_1)) { - splitChange.featureFlags = convertBodyToOldSpec(response.body()); - splitChange.ruleBasedSegments = createEmptyDTO(); - } else { - splitChange = Json.fromJson(response.body(), SplitChange.class); - } - return splitChange; } - public Long getLastProxyCheckTimestamp() { - return _lastProxyCheckTimestamp; - } - - public void setLastProxyCheckTimestamp(long lastProxyCheckTimestamp) { - synchronized (_lock) { - _lastProxyCheckTimestamp = lastProxyCheckTimestamp; - } - } - - private ChangeDto createEmptyDTO() { - ChangeDto dto = new ChangeDto<>(); - dto.d = new ArrayList<>(); - dto.t = -1; - dto.s = -1; - return dto; - } - private ChangeDto convertBodyToOldSpec(String body) { - return Json.fromJson(body, SplitChangesOldPayloadDto.class).toSplitChange().featureFlags; + private SplitChange convertBodyToOldSpec(String body) { + return Json.fromJson(body, SplitChangesOldPayloadDto.class).toSplitChange(); } private URI buildURL(FetchOptions options, long since, long sinceRBS) throws URISyntaxException { diff --git a/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java b/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java index 6be74846..dfcc632e 100644 --- a/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/JsonLocalhostSplitChangeFetcher.java @@ -2,7 +2,6 @@ import com.google.gson.JsonObject; import com.google.gson.stream.JsonReader; -import io.split.client.dtos.ChangeDto; import io.split.client.dtos.SplitChange; import io.split.client.dtos.SplitChangesOldPayloadDto; import io.split.client.utils.InputStreamProvider; @@ -19,9 +18,10 @@ import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; import java.util.Arrays; +import static io.split.client.utils.Utils.checkExitConditions; + public class JsonLocalhostSplitChangeFetcher implements SplitChangeFetcher { private static final Logger _log = LoggerFactory.getLogger(JsonLocalhostSplitChangeFetcher.class); @@ -40,10 +40,7 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { try { JsonReader jsonReader = new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8))); if (checkOldSpec(new JsonReader(new BufferedReader(new InputStreamReader(_inputStreamProvider.get(), StandardCharsets.UTF_8))))) { - SplitChange splitChange = new SplitChange(); - splitChange.featureFlags = Json.fromJson(jsonReader, SplitChangesOldPayloadDto.class).toSplitChange().featureFlags; - splitChange.ruleBasedSegments = ChangeDto.createEmptyDto(); - return splitChange; + return Json.fromJson(jsonReader, SplitChangesOldPayloadDto.class).toSplitChange(); } SplitChange splitChange = Json.fromJson(jsonReader, SplitChange.class); return processSplitChange(splitChange, since, sinceRBS); @@ -82,10 +79,6 @@ private SplitChange processSplitChange(SplitChange splitChange, long changeNumbe return splitChangeToProcess; } - private boolean checkExitConditions(ChangeDto change, long cn) { - return change.t < cn && change.t != -1; - } - private byte[] getStringDigest(String Json) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.reset(); diff --git a/client/src/main/java/io/split/client/dtos/RuleBasedSegmentChange.java b/client/src/main/java/io/split/client/dtos/RuleBasedSegmentChange.java deleted file mode 100644 index 9f475003..00000000 --- a/client/src/main/java/io/split/client/dtos/RuleBasedSegmentChange.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.split.client.dtos; - -import java.util.List; - -public class RuleBasedSegmentChange { - public List ruleBasedSegments; - public long since; - public long till; -} 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 2ecb9920..53f07c87 100644 --- a/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java +++ b/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java @@ -1,6 +1,5 @@ package io.split.engine.experiments; -import io.split.client.dtos.ChangeDto; import io.split.client.dtos.SplitChange; import io.split.client.exceptions.UriTooLongException; import io.split.client.interceptors.FlagSetsFilter; @@ -20,6 +19,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static io.split.client.utils.FeatureFlagProcessor.processFeatureFlagChanges; import static io.split.client.utils.RuleBasedSegmentProcessor.processRuleBasedSegmentChanges; +import static io.split.client.utils.Utils.checkExitConditions; /** * An ExperimentFetcher that refreshes experiment definitions periodically. @@ -122,6 +122,11 @@ private Set runWithoutExceptionHandling(FetchOptions options) throws Int throw new IllegalStateException("SplitChange was null"); } + if (change.clearCache) { + _splitCacheProducer.clear(); + _ruleBasedSegmentCacheProducer.clear(); + } + if (checkExitConditions(change.featureFlags, _splitCacheProducer.getChangeNumber()) || checkExitConditions(change.ruleBasedSegments, _ruleBasedSegmentCacheProducer.getChangeNumber())) { return segments; @@ -161,8 +166,4 @@ private Set runWithoutExceptionHandling(FetchOptions options) throws Int return segments; } - - private boolean checkExitConditions(ChangeDto change, long cn) { - return change.s != cn || change.t < cn; - } } \ No newline at end of file diff --git a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java index 51d55135..82f65602 100644 --- a/client/src/test/java/io/split/client/SplitClientIntegrationTest.java +++ b/client/src/test/java/io/split/client/SplitClientIntegrationTest.java @@ -210,7 +210,7 @@ public void getTreatmentWithStreamingDisabled() throws Exception { @Test public void managerSplitsWithStreamingEnabled() throws Exception { - MockResponse response = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850109}, \"rbs\":{\"d\":[],\"s\":-1,\"t\":-1}}"); + MockResponse response = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850109}, \"rbs\":{\"d\":[],\"s\":1585948850109,\"t\":1585948850109}}"); Queue responses = new LinkedList<>(); responses.add(response); SplitMockServer splitServer = new SplitMockServer(CustomDispatcher.builder() @@ -250,9 +250,9 @@ public void managerSplitsWithStreamingEnabled() throws Exception { @Test public void splitClientOccupancyNotifications() throws Exception { - MockResponse response = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850109}, \"rbs\":{\"d\":[],\"s\":-1,\"t\":-1}}"); - MockResponse response2 = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850110, \"t\":1585948850110}, \"rbs\":{\"d\":[],\"s\":-1,\"t\":-1}}"); - MockResponse response3 = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850111, \"t\":1585948850111}, \"rbs\":{\"d\":[],\"s\":-1,\"t\":-1}}"); + MockResponse response = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850109}, \"rbs\":{\"d\":[],\"s\":1585948850109,\"t\":1585948850109}}"); + MockResponse response2 = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850110, \"t\":1585948850110}, \"rbs\":{\"d\":[],\"s\":1585948850110,\"t\":1585948850110}}"); + MockResponse response3 = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850111, \"t\":1585948850111}, \"rbs\":{\"d\":[],\"s\":1585948850111,\"t\":1585948850111}}"); Queue responses = new LinkedList<>(); responses.add(response); Queue responses2 = new LinkedList<>(); @@ -420,7 +420,7 @@ public void splitClientControlNotifications() throws Exception { @Test public void splitClientMultiFactory() throws Exception { - MockResponse response = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850109}, \"rbs\":{\"d\":[],\"s\":-1,\"t\":-1}}"); + MockResponse response = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1585948850109, \"t\":1585948850109}, \"rbs\":{\"d\":[],\"s\":1585948850109,\"t\":1585948850109}}"); Queue responses = new LinkedList<>(); responses.add(response); responses.add(response); @@ -752,8 +752,8 @@ public void testPluggableMode() throws IOException, URISyntaxException { @Test public void getTreatmentFlagSetWithPolling() throws Exception { - MockResponse response = new MockResponse().setBody("{\"splits\":[{\"trafficTypeName\":\"client\",\"name\":\"workm\",\"trafficAllocation\":100,\"trafficAllocationSeed\":147392224,\"seed\":524417105,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"on\",\"changeNumber\":1602796638344,\"algo\":2,\"configurations\":{},\"sets\":[\"set1\",\"set2\"],\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"client\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"new_segment\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":0},{\"treatment\":\"free\",\"size\":100},{\"treatment\":\"conta\",\"size\":0}],\"label\":\"in segment new_segment\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"client\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0},{\"treatment\":\"free\",\"size\":0},{\"treatment\":\"conta\",\"size\":0}],\"label\":\"default rule\"}]},{\"trafficTypeName\":\"client\",\"name\":\"workm_set_3\",\"trafficAllocation\":100,\"trafficAllocationSeed\":147392224,\"seed\":524417105,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"on\",\"changeNumber\":1602796638344,\"algo\":2,\"configurations\":{},\"sets\":[\"set3\"],\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"client\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"new_segment\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":0},{\"treatment\":\"free\",\"size\":100},{\"treatment\":\"conta\",\"size\":0}],\"label\":\"in segment new_segment\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"client\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0},{\"treatment\":\"free\",\"size\":0},{\"treatment\":\"conta\",\"size\":0}],\"label\":\"default rule\"}]}],\"since\":-1,\"till\":1602796638344}"); - MockResponse responseFlag = new MockResponse().setBody("{\"splits\": [], \"since\":1602796638344, \"till\":1602796638344}"); + MockResponse response = new MockResponse().setBody("{\"ff\":{\"d\":[{\"trafficTypeName\":\"client\",\"name\":\"workm\",\"trafficAllocation\":100,\"trafficAllocationSeed\":147392224,\"seed\":524417105,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"on\",\"changeNumber\":1602796638344,\"algo\":2,\"configurations\":{},\"sets\":[\"set1\",\"set2\"],\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"client\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"new_segment\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":0},{\"treatment\":\"free\",\"size\":100},{\"treatment\":\"conta\",\"size\":0}],\"label\":\"in segment new_segment\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"client\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0},{\"treatment\":\"free\",\"size\":0},{\"treatment\":\"conta\",\"size\":0}],\"label\":\"default rule\"}]},{\"trafficTypeName\":\"client\",\"name\":\"workm_set_3\",\"trafficAllocation\":100,\"trafficAllocationSeed\":147392224,\"seed\":524417105,\"status\":\"ACTIVE\",\"killed\":false,\"defaultTreatment\":\"on\",\"changeNumber\":1602796638344,\"algo\":2,\"configurations\":{},\"sets\":[\"set3\"],\"conditions\":[{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"client\",\"attribute\":null},\"matcherType\":\"IN_SEGMENT\",\"negate\":false,\"userDefinedSegmentMatcherData\":{\"segmentName\":\"new_segment\"},\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":0},{\"treatment\":\"off\",\"size\":0},{\"treatment\":\"free\",\"size\":100},{\"treatment\":\"conta\",\"size\":0}],\"label\":\"in segment new_segment\"},{\"conditionType\":\"ROLLOUT\",\"matcherGroup\":{\"combiner\":\"AND\",\"matchers\":[{\"keySelector\":{\"trafficType\":\"client\",\"attribute\":null},\"matcherType\":\"ALL_KEYS\",\"negate\":false,\"userDefinedSegmentMatcherData\":null,\"whitelistMatcherData\":null,\"unaryNumericMatcherData\":null,\"betweenMatcherData\":null,\"booleanMatcherData\":null,\"dependencyMatcherData\":null,\"stringMatcherData\":null}]},\"partitions\":[{\"treatment\":\"on\",\"size\":100},{\"treatment\":\"off\",\"size\":0},{\"treatment\":\"free\",\"size\":0},{\"treatment\":\"conta\",\"size\":0}],\"label\":\"default rule\"}]}],\"s\":-1,\"t\":1602796638344},\"rbs\":{\"d\":[],\"t\":-1,\"s\":-1}}"); + MockResponse responseFlag = new MockResponse().setBody("{\"ff\":{\"d\": [], \"s\":1602796638344, \"t\":1602796638344},\"rbs\":{\"d\":[],\"t\":-1,\"s\":-1}}"); MockResponse segmentResponse = new MockResponse().setBody("{\"name\":\"new_segment\",\"added\":[\"user-1\"],\"removed\":[\"user-2\",\"user-3\"],\"since\":-1,\"till\":-1}"); Queue responses = new LinkedList<>(); responses.add(response); From d96a62dd553e026c07456f9c749873d2ba911dcc Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany <41021307+chillaq@users.noreply.github.com> Date: Mon, 28 Apr 2025 08:33:52 -0700 Subject: [PATCH 08/12] Update client/src/main/java/io/split/client/HttpSplitChangeFetcher.java Co-authored-by: Mauro Sanz <51236193+sanzmauro@users.noreply.github.com> --- client/src/main/java/io/split/client/HttpSplitChangeFetcher.java | 1 - 1 file changed, 1 deletion(-) diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java index 77d3b26a..4e4bca58 100644 --- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java @@ -97,7 +97,6 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { ); } - SplitChange splitChange = new SplitChange(); if (specVersion.equals(Spec.SPEC_1_1)) { splitChange = convertBodyToOldSpec(response.body()); } else { From efa44fa76434cebff75c5e949478043d449ca81e Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany <41021307+chillaq@users.noreply.github.com> Date: Mon, 28 Apr 2025 08:34:00 -0700 Subject: [PATCH 09/12] Update client/src/main/java/io/split/client/HttpSplitChangeFetcher.java Co-authored-by: Mauro Sanz <51236193+sanzmauro@users.noreply.github.com> --- .../src/main/java/io/split/client/HttpSplitChangeFetcher.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java index 4e4bca58..49a105a8 100644 --- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java @@ -98,8 +98,8 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { } if (specVersion.equals(Spec.SPEC_1_1)) { - splitChange = convertBodyToOldSpec(response.body()); - } else { + return Json.fromJson(response.body(), SplitChangesOldPayloadDto.class).toSplitChange(); + } if (_lastProxyCheckTimestamp != 0) { splitChange.clearCache = true; _lastProxyCheckTimestamp = 0L; From 0af690d7ff5c7a81fc73e9b17bc243795db83dfb Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Mon, 28 Apr 2025 08:45:07 -0700 Subject: [PATCH 10/12] polish --- .../java/io/split/client/HttpSplitChangeFetcher.java | 10 ++++------ .../src/main/java/io/split/client/dtos/ChangeDto.java | 8 -------- .../io/split/engine/experiments/SplitFetcherImp.java | 2 +- 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java index 49a105a8..87497538 100644 --- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java @@ -100,12 +100,10 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { if (specVersion.equals(Spec.SPEC_1_1)) { return Json.fromJson(response.body(), SplitChangesOldPayloadDto.class).toSplitChange(); } - if (_lastProxyCheckTimestamp != 0) { - splitChange.clearCache = true; - _lastProxyCheckTimestamp = 0L; - } - splitChange = Json.fromJson(response.body(), SplitChange.class); - } + + SplitChange splitChange = Json.fromJson(response.body(), SplitChange.class); + splitChange.clearCache = _lastProxyCheckTimestamp != 0; + _lastProxyCheckTimestamp = 0L; return splitChange; } catch (Exception e) { throw new IllegalStateException(String.format("Problem fetching splitChanges since %s: %s", since, e), e); diff --git a/client/src/main/java/io/split/client/dtos/ChangeDto.java b/client/src/main/java/io/split/client/dtos/ChangeDto.java index d714e69a..14cdbf88 100644 --- a/client/src/main/java/io/split/client/dtos/ChangeDto.java +++ b/client/src/main/java/io/split/client/dtos/ChangeDto.java @@ -7,12 +7,4 @@ public class ChangeDto { public long s; public long t; public List d; - - public static ChangeDto createEmptyDto() { - ChangeDto dto = new ChangeDto<>(); - dto.d = new ArrayList<>(); - dto.t = -1; - dto.s = -1; - return dto; - } } \ 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 53f07c87..a2d8681d 100644 --- a/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java +++ b/client/src/main/java/io/split/engine/experiments/SplitFetcherImp.java @@ -147,7 +147,7 @@ private Set runWithoutExceptionHandling(FetchOptions options) throws Int synchronized (_lock) { // check state one more time. - if (checkExitConditions(change.featureFlags, _splitCacheProducer.getChangeNumber()) && + if (checkExitConditions(change.featureFlags, _splitCacheProducer.getChangeNumber()) || checkExitConditions(change.ruleBasedSegments, _ruleBasedSegmentCacheProducer.getChangeNumber())) { // some other thread may have updated the shared state. exit return segments; From c17372b73b21cf4410338794949dbbde3680714e Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany Date: Mon, 28 Apr 2025 08:49:03 -0700 Subject: [PATCH 11/12] polish --- .../src/main/java/io/split/client/HttpSplitChangeFetcher.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java index 80df89df..a8fac191 100644 --- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java @@ -118,7 +118,9 @@ private SplitChange convertBodyToOldSpec(String body) { private URI buildURL(FetchOptions options, long since, long sinceRBS) throws URISyntaxException { URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + specVersion); uriBuilder.addParameter(SINCE, "" + since); - uriBuilder.addParameter(RB_SINCE, "" + sinceRBS); + if (specVersion.equals(SPEC_1_3)) { + uriBuilder.addParameter(RB_SINCE, "" + sinceRBS); + } if (!options.flagSetsFilter().isEmpty()) { uriBuilder.addParameter(SETS, "" + options.flagSetsFilter()); } From 39325f8f4d2bb63d225e819ce2d2d38396ebf076 Mon Sep 17 00:00:00 2001 From: Bilal Al-Shahwany <41021307+chillaq@users.noreply.github.com> Date: Mon, 28 Apr 2025 09:37:52 -0700 Subject: [PATCH 12/12] Update client/src/main/java/io/split/client/HttpSplitChangeFetcher.java Co-authored-by: Mauro Sanz <51236193+sanzmauro@users.noreply.github.com> --- .../src/main/java/io/split/client/HttpSplitChangeFetcher.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java index a8fac191..3659af9f 100644 --- a/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java +++ b/client/src/main/java/io/split/client/HttpSplitChangeFetcher.java @@ -111,9 +111,6 @@ public SplitChange fetch(long since, long sinceRBS, FetchOptions options) { } } - private SplitChange convertBodyToOldSpec(String body) { - return Json.fromJson(body, SplitChangesOldPayloadDto.class).toSplitChange(); - } private URI buildURL(FetchOptions options, long since, long sinceRBS) throws URISyntaxException { URIBuilder uriBuilder = new URIBuilder(_target).addParameter(SPEC, "" + specVersion);