Skip to content

Add RBS change number as query param #747

New issue

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

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

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Mar 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ba303d8
Persistent storage
gthea Feb 25, 2025
1ade063
WIP
gthea Feb 25, 2025
2b2b424
SqLite persistent storage tests
gthea Feb 25, 2025
e292005
Tests
gthea Feb 25, 2025
bae1f83
Fixes
gthea Feb 25, 2025
c1c96dd
Add exception handling
gthea Feb 25, 2025
ad23322
More tests
gthea Feb 25, 2025
2fc3f2e
Add nullability annotations
gthea Feb 25, 2025
96bca34
Merge branch 'SDKS-9357_baseline' into SDKS-9439_1
gthea Feb 27, 2025
a8bff33
Prep for ParserCommons
gthea Feb 27, 2025
537ed77
Tests for lazy storage provider
gthea Feb 27, 2025
7519aca
Fix impl
gthea Feb 27, 2025
724c681
Storage change & matcher implementation
gthea Feb 27, 2025
2d221fb
Temp parser for RBS storage
gthea Feb 27, 2025
24b8c6b
Fix
gthea Feb 27, 2025
f042de3
Fix test
gthea Feb 27, 2025
cfa0c6c
In RBS matcher test
gthea Feb 27, 2025
0cbde4c
RBS parser
gthea Feb 27, 2025
4ddb4aa
Simplify initialization
gthea Feb 27, 2025
509204e
Fix tests
gthea Feb 27, 2025
83d4550
Merge branch 'SDKS-9357_baseline' into SDKS-9439_3
gthea Feb 28, 2025
2672fbf
WIP
gthea Feb 28, 2025
fa22170
Manual test updates
gthea Mar 5, 2025
0aabcaa
Change mock .json
gthea Mar 5, 2025
d714903
UT migrated
gthea Mar 5, 2025
9f29fb3
WIP tests migration
gthea Mar 5, 2025
7ffc626
Tests migration continued
gthea Mar 5, 2025
c0c380d
Fix
gthea Mar 5, 2025
5cf6449
Merge branch 'SDKS-9357_baseline' into SDKS-9437
gthea Mar 5, 2025
056141a
WIP
gthea Mar 5, 2025
3a6b873
Move storage init
gthea Mar 6, 2025
0d75096
Producer tests
gthea Mar 6, 2025
e4beef9
Merge branch 'SDKS-9357_baseline' into SDKS-9437_2
gthea Mar 6, 2025
738cdc7
Additional test check
gthea Mar 6, 2025
04ccc45
WIP rbSince
gthea Mar 6, 2025
4de3a79
WIP
gthea Mar 6, 2025
98f4ffc
Fixes
gthea Mar 7, 2025
ac8b250
Update tests
gthea Mar 7, 2025
259ea3f
Merge branch 'SDKS-9357_baseline' into SDKS-9441
gthea Mar 7, 2025
7cb981f
Alternate json field names for SplitChange
gthea Mar 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/androidTest/java/helper/IntegrationHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,18 @@ public static void logSeparator(String tag) {
Logger.i(tag, Utils.repeat("-", 200));
}

@Deprecated
public static String emptySplitChanges(long since, long till) {
return emptySplitChanges(till);
}

@Deprecated
public static String emptySplitChanges(long till) {
return String.format("{\"ff\":{\"splits\":[], \"since\": %d, \"till\": %d},\"rbs\":{\"d\":[],\"s\":%d,\"t\":%d}}", till, till, till, till);
return emptyTargetingRulesChanges(till, till);
}

public static String emptyTargetingRulesChanges(long flagsTill, long rbsTill) {
return String.format("{\"ff\":{\"splits\":[], \"since\": %d, \"till\": %d},\"rbs\":{\"d\":[],\"s\":%d,\"t\":%d}}", flagsTill, flagsTill, rbsTill, rbsTill);
}

public static SplitFactory buildFactory(String apiToken, Key key, SplitClientConfig config,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void queryStringContainsFlagsSpec() throws InterruptedException {
testingConfig.setFlagsSpec("1.1");
initSplitFactory(new TestableSplitConfigBuilder(), mHttpClient, testingConfig);

assertEquals("s=1.1&since=-1", mQueryString.get());
assertEquals("s=1.1&since=-1&rbSince=-1", mQueryString.get());
}

@Test
Expand All @@ -71,7 +71,7 @@ public void nullFlagsSpecValueOmitsQueryParam() throws InterruptedException {
testingConfig.setFlagsSpec(null);
initSplitFactory(new TestableSplitConfigBuilder(), mHttpClient, testingConfig);

assertEquals("since=-1", mQueryString.get());
assertEquals("since=-1&rbSince=-1", mQueryString.get());
}

@Test
Expand All @@ -92,7 +92,7 @@ public void newFlagsSpecIsUsedInRequest() throws InterruptedException {
testingConfig.setFlagsSpec("1.2");
initSplitFactory(new TestableSplitConfigBuilder(), mHttpClient, testingConfig);

assertEquals("s=1.2&since=-1", mQueryString.get());
assertEquals("s=1.2&since=-1&rbSince=-1", mQueryString.get());
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ public void testAll() throws Exception {
.build();
splitRoomDatabase.clearAllTables();
splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.CHANGE_NUMBER_INFO, 2));
splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity("rbsChangeNumber", 4));
splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.SPLITS_FILTER_QUERY_STRING, expectedQs));
splitRoomDatabase.generalInfoDao().update(new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, System.currentTimeMillis()));
SplitClient client;
Expand Down Expand Up @@ -143,7 +144,7 @@ public void testAll() throws Exception {

latch.await(10, TimeUnit.SECONDS);

Assert.assertEquals("since=2" + expectedQs, mReceivedQueryString);
Assert.assertEquals("since=2&rbSince=4" + expectedQs, mReceivedQueryString);

splitFactory.destroy();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ public void queryStringIsBuiltCorrectlyWhenSetsAreConfigured() throws IOExceptio
String uri = mSplitChangesUri;

assertTrue(awaitFirst);
assertEquals("https://sdk.split.io/api/splitChanges?s=1.1&since=-1&sets=set_2,set_3,set_ww,set_x", uri);
assertEquals("https://sdk.split.io/api/splitChanges?s=1.1&since=-1&rbSince=-1&sets=set_2,set_3,set_ww,set_x", uri);
}

private SplitFactory createFactory(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
mSplitsUpdateLatch.countDown();
return createResponse(200, getSplitChanges(mSplitChangesHitCount));
}
String data = IntegrationHelper.emptySplitChanges(-1, CHANGE_NUMBER - 1000);
String data = IntegrationHelper.emptyTargetingRulesChanges(CHANGE_NUMBER - 1000, -1);
return createResponse(200, data);
} else if (uri.getPath().contains("/auth")) {
Logger.i("** SSE Auth hit");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ public HttpResponseMock getResponse(URI uri, HttpMethod method, String body) {
mSplitsUpdateLatch.countDown();
return createResponse(200, getSplitChanges(mSplitChangesHitCount - 1));
}
String data = IntegrationHelper.emptySplitChanges(CHANGE_NUMBER - 1000, CHANGE_NUMBER - 1000);
String data = IntegrationHelper.emptyTargetingRulesChanges(CHANGE_NUMBER - 1000, -1);
return createResponse(200, data);
} else if (uri.getPath().contains("/auth")) {
Logger.i("** SSE Auth hit");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ class UserConsentModeDebugTest {
mChangeHit+=1
return getSplitsMockResponse("")
}
return HttpResponseMock(200, IntegrationHelper.emptySplitChanges(99999999, 99999999))
return HttpResponseMock(200, IntegrationHelper.emptySplitChanges(99999999))
} else if (uri.path.contains("/testImpressions/bulk")) {
if (!mImpPosted) {
mImpPosted = true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -241,7 +241,7 @@ class UserConsentModeNoneTest {
}
return HttpResponseMock(
200,
IntegrationHelper.emptySplitChanges(99999999, 99999999)
IntegrationHelper.emptySplitChanges(99999999)
)
} else if (uri.path.contains("/testImpressions/bulk")) {
HttpResponseMock(200)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ class UserConsentModeOptimizedTest {
mChangeHit+=1
return getSplitsMockResponse("")
}
return HttpResponseMock(200, IntegrationHelper.emptySplitChanges(99999999, 99999999))
return HttpResponseMock(200, IntegrationHelper.emptySplitChanges(99999999))
} else if (uri.path.contains("/testImpressions/bulk")) {
if (!mImpPosted) {
mImpPosted = true
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/io/split/android/client/dtos/SplitChange.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,16 @@

import androidx.annotation.VisibleForTesting;

import com.google.gson.annotations.SerializedName;

import java.util.List;

public class SplitChange {
@SerializedName(value = "d", alternate = "splits")
public List<Split> splits;
@SerializedName(value = "s", alternate = "since")
public long since;
@SerializedName(value = "t", alternate = "till")
public long till;

@VisibleForTesting
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import io.split.android.client.service.CleanUpDatabaseTask;
import io.split.android.client.service.events.EventsRecorderTask;
import io.split.android.client.service.impressions.ImpressionsTaskFactory;
import io.split.android.client.service.rules.LoadRuleBasedSegmentsTask;
import io.split.android.client.service.splits.FilterSplitsInCacheTask;
import io.split.android.client.service.splits.LoadSplitsTask;
import io.split.android.client.service.splits.SplitInPlaceUpdateTask;
Expand All @@ -23,9 +24,11 @@ public interface SplitTaskFactory extends TelemetryTaskFactory, ImpressionsTaskF

LoadSplitsTask createLoadSplitsTask();

LoadRuleBasedSegmentsTask createLoadRuleBasedSegmentsTask();

SplitKillTask createSplitKillTask(Split split);

SplitsUpdateTask createSplitsUpdateTask(long since);
SplitsUpdateTask createSplitsUpdateTask(Long since, Long rbsSince);

SplitInPlaceUpdateTask createSplitsUpdateTask(Split featureFlag, long since);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import io.split.android.client.service.impressions.unique.SaveUniqueImpressionsTask;
import io.split.android.client.service.impressions.unique.UniqueKeysRecorderTask;
import io.split.android.client.service.impressions.unique.UniqueKeysRecorderTaskConfig;
import io.split.android.client.service.rules.LoadRuleBasedSegmentsTask;
import io.split.android.client.service.splits.FilterSplitsInCacheTask;
import io.split.android.client.service.splits.LoadSplitsTask;
import io.split.android.client.service.splits.SplitChangeProcessor;
Expand Down Expand Up @@ -133,6 +134,7 @@ public ImpressionsRecorderTask createImpressionsRecorderTask() {
@Override
public SplitsSyncTask createSplitsSyncTask(boolean checkCacheExpiration) {
return SplitsSyncTask.build(mSplitsSyncHelper, mSplitsStorageContainer.getSplitsStorage(),
mSplitsStorageContainer.getRuleBasedSegmentStorage(),
mSplitsFilterQueryStringFromConfig, mEventsManager, mSplitsStorageContainer.getTelemetryStorage());
}

Expand All @@ -141,14 +143,19 @@ public LoadSplitsTask createLoadSplitsTask() {
return new LoadSplitsTask(mSplitsStorageContainer.getSplitsStorage(), mSplitsFilterQueryStringFromConfig, mFlagsSpecFromConfig);
}

@Override
public LoadRuleBasedSegmentsTask createLoadRuleBasedSegmentsTask() {
return new LoadRuleBasedSegmentsTask(mSplitsStorageContainer.getRuleBasedSegmentStorage());
}

@Override
public SplitKillTask createSplitKillTask(Split split) {
return new SplitKillTask(mSplitsStorageContainer.getSplitsStorage(), split, mEventsManager);
}

@Override
public SplitsUpdateTask createSplitsUpdateTask(long since) {
return new SplitsUpdateTask(mSplitsSyncHelper, mSplitsStorageContainer.getSplitsStorage(), since, mEventsManager);
public SplitsUpdateTask createSplitsUpdateTask(Long since, Long rbsSince) {
return new SplitsUpdateTask(mSplitsSyncHelper, mSplitsStorageContainer.getSplitsStorage(), mSplitsStorageContainer.getRuleBasedSegmentStorage(), since, rbsSince, mEventsManager);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,5 @@ public enum SplitTaskType {
MY_SEGMENTS_UPDATE, LOAD_LOCAL_ATTRIBUTES,
TELEMETRY_CONFIG_TASK, TELEMETRY_STATS_TASK,
SAVE_UNIQUE_KEYS_TASK, UNIQUE_KEYS_RECORDER_TASK,
MY_LARGE_SEGMENTS_UPDATE,
MY_LARGE_SEGMENTS_UPDATE, LOAD_LOCAL_RULE_BASED_SEGMENTS,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.split.android.client.service.rules;

import static io.split.android.client.utils.Utils.checkNotNull;

import androidx.annotation.NonNull;

import io.split.android.client.service.executor.SplitTask;
import io.split.android.client.service.executor.SplitTaskExecutionInfo;
import io.split.android.client.service.executor.SplitTaskType;
import io.split.android.client.storage.rbs.RuleBasedSegmentStorage;
import io.split.android.client.utils.logger.Logger;

public class LoadRuleBasedSegmentsTask implements SplitTask {

private final RuleBasedSegmentStorage mRuleBasedSegmentStorage;

public LoadRuleBasedSegmentsTask(@NonNull RuleBasedSegmentStorage ruleBasedSegmentStorage) {
mRuleBasedSegmentStorage = checkNotNull(ruleBasedSegmentStorage);
}

@NonNull
@Override
public SplitTaskExecutionInfo execute() {
try {
mRuleBasedSegmentStorage.loadLocal();
return SplitTaskExecutionInfo.success(SplitTaskType.LOAD_LOCAL_RULE_BASED_SEGMENTS);
} catch (Exception e) {
Logger.e("Error loading rule based segments: " + e.getLocalizedMessage());
return SplitTaskExecutionInfo.error(SplitTaskType.LOAD_LOCAL_RULE_BASED_SEGMENTS);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class SplitsSyncHelper {

private static final String SINCE_PARAM = "since";
private static final String TILL_PARAM = "till";
private static final String RBS_SINCE_PARAM = "rbSince";
private static final int ON_DEMAND_FETCH_BACKOFF_MAX_WAIT = ServiceConstants.ON_DEMAND_FETCH_BACKOFF_MAX_WAIT;

private final HttpFetcher<TargetingRulesChange> mSplitFetcher;
Expand Down Expand Up @@ -80,15 +81,15 @@ public SplitsSyncHelper(@NonNull HttpFetcher<TargetingRulesChange> splitFetcher,
mFlagsSpec = flagsSpec;
}

public SplitTaskExecutionInfo sync(long till, int onDemandFetchBackoffMaxRetries) {
public SplitTaskExecutionInfo sync(SinceChangeNumbers till, int onDemandFetchBackoffMaxRetries) {
return sync(till, false, true, false, onDemandFetchBackoffMaxRetries);
}

public SplitTaskExecutionInfo sync(long till, boolean clearBeforeUpdate, boolean resetChangeNumber, int onDemandFetchBackoffMaxRetries) {
public SplitTaskExecutionInfo sync(SinceChangeNumbers till, boolean clearBeforeUpdate, boolean resetChangeNumber, int onDemandFetchBackoffMaxRetries) {
return sync(till, clearBeforeUpdate, false, resetChangeNumber, onDemandFetchBackoffMaxRetries);
}

private SplitTaskExecutionInfo sync(long till, boolean clearBeforeUpdate, boolean avoidCache, boolean resetChangeNumber, int onDemandFetchBackoffMaxRetries) {
private SplitTaskExecutionInfo sync(SinceChangeNumbers till, boolean clearBeforeUpdate, boolean avoidCache, boolean resetChangeNumber, int onDemandFetchBackoffMaxRetries) {
try {
boolean successfulSync = attemptSplitSync(till, clearBeforeUpdate, avoidCache, false, resetChangeNumber, onDemandFetchBackoffMaxRetries);

Expand Down Expand Up @@ -127,16 +128,16 @@ private SplitTaskExecutionInfo sync(long till, boolean clearBeforeUpdate, boolea
* @param onDemandFetchBackoffMaxRetries max backoff retries for CDN bypass
* @return whether sync finished successfully
*/
private boolean attemptSplitSync(long till, boolean clearBeforeUpdate, boolean avoidCache, boolean withCdnBypass, boolean resetChangeNumber, int onDemandFetchBackoffMaxRetries) throws Exception {
private boolean attemptSplitSync(SinceChangeNumbers till, boolean clearBeforeUpdate, boolean avoidCache, boolean withCdnBypass, boolean resetChangeNumber, int onDemandFetchBackoffMaxRetries) throws Exception {
int remainingAttempts = onDemandFetchBackoffMaxRetries;
mBackoffCounter.resetCounter();
while (true) {
remainingAttempts--;

long changeNumber = fetchUntil(till, clearBeforeUpdate, avoidCache, withCdnBypass, resetChangeNumber);
SinceChangeNumbers changeNumber = fetchUntil(till, clearBeforeUpdate, avoidCache, withCdnBypass, resetChangeNumber);
resetChangeNumber = false;

if (till <= changeNumber) {
if (till.getFlagsSince() <= changeNumber.getFlagsSince() && till.getRbsSince() <= changeNumber.getRbsSince()) {
return true;
}

Expand All @@ -154,39 +155,41 @@ private boolean attemptSplitSync(long till, boolean clearBeforeUpdate, boolean a
}
}

private long fetchUntil(long till, boolean clearBeforeUpdate, boolean avoidCache, boolean withCdnByPass, boolean resetChangeNumber) throws Exception {
private SinceChangeNumbers fetchUntil(SinceChangeNumbers till, boolean clearBeforeUpdate, boolean avoidCache, boolean withCdnByPass, boolean resetChangeNumber) throws Exception {
boolean shouldClearBeforeUpdate = clearBeforeUpdate;

long newTill = till;
SinceChangeNumbers newTill = till;
while (true) {
long changeNumber = (resetChangeNumber) ? -1 : mSplitsStorage.getTill();
long rbsChangeNumber = (resetChangeNumber) ? -1 : mRuleBasedSegmentStorage.getChangeNumber();
resetChangeNumber = false;
if (newTill < changeNumber) {
return changeNumber;
if (newTill.getFlagsSince() < changeNumber && newTill.getRbsSince() < rbsChangeNumber) {
return new SinceChangeNumbers(changeNumber, rbsChangeNumber);
}

TargetingRulesChange targetingRulesChange = fetchSplits(changeNumber, avoidCache, withCdnByPass);
TargetingRulesChange targetingRulesChange = fetchSplits(new SinceChangeNumbers(changeNumber, rbsChangeNumber), avoidCache, withCdnByPass);
SplitChange splitChange = targetingRulesChange.getFeatureFlagsChange();
RuleBasedSegmentChange ruleBasedSegmentChange = targetingRulesChange.getRuleBasedSegmentsChange();
updateStorage(shouldClearBeforeUpdate, splitChange, ruleBasedSegmentChange);
shouldClearBeforeUpdate = false;

newTill = splitChange.till;
if (splitChange.till == splitChange.since) {
return splitChange.till;
newTill = new SinceChangeNumbers(splitChange.till, ruleBasedSegmentChange.getTill());
if (splitChange.till == splitChange.since && ruleBasedSegmentChange.getTill() == ruleBasedSegmentChange.getSince()) {
return new SinceChangeNumbers(splitChange.till, ruleBasedSegmentChange.getTill());
}
}
}

private TargetingRulesChange fetchSplits(long till, boolean avoidCache, boolean withCdnByPass) throws HttpFetcherException {
private TargetingRulesChange fetchSplits(SinceChangeNumbers till, boolean avoidCache, boolean withCdnByPass) throws HttpFetcherException {
Map<String, Object> params = new LinkedHashMap<>();
if (mFlagsSpec != null && !mFlagsSpec.trim().isEmpty()) {
params.put(FLAGS_SPEC_PARAM, mFlagsSpec);
}
params.put(SINCE_PARAM, till);
params.put(SINCE_PARAM, till.getFlagsSince());
params.put(RBS_SINCE_PARAM, till.getRbsSince());

if (withCdnByPass) {
params.put(TILL_PARAM, till);
params.put(TILL_PARAM, till.getFlagsSince());
}

return mSplitFetcher.execute(params, getHeaders(avoidCache));
Expand Down Expand Up @@ -225,4 +228,29 @@ private void logError(String message) {
}
return null;
}

public static class SinceChangeNumbers {
private final long mFlagsSince;
private final long mRbsSince;

public SinceChangeNumbers(long flagsSince, long rbsSince) {
mFlagsSince = flagsSince;
mRbsSince = rbsSince;
}

public long getFlagsSince() {
return mFlagsSince;
}

public long getRbsSince() {
return mRbsSince;
}

@Override
public boolean equals(@Nullable Object obj) {
return obj instanceof SinceChangeNumbers &&
mFlagsSince == ((SinceChangeNumbers) obj).mFlagsSince &&
mRbsSince == ((SinceChangeNumbers) obj).mRbsSince;
}
}
}
Loading
Loading