Skip to content

Commit

Permalink
Rollout config (#719)
Browse files Browse the repository at this point in the history
  • Loading branch information
gthea authored Dec 4, 2024
1 parent 943229c commit 61c484a
Show file tree
Hide file tree
Showing 10 changed files with 264 additions and 58 deletions.
10 changes: 9 additions & 1 deletion src/androidTest/java/helper/TestableSplitConfigBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import java.lang.reflect.Constructor;

import io.split.android.client.RolloutCacheConfiguration;
import io.split.android.client.ServiceEndpoints;
import io.split.android.client.SplitClientConfig;
import io.split.android.client.SyncConfig;
Expand Down Expand Up @@ -64,6 +65,7 @@ public class TestableSplitConfigBuilder {
private String mPrefix = "";
private CertificatePinningConfiguration mCertificatePinningConfiguration;
private long mImpressionsDedupeTimeInterval = ServiceConstants.DEFAULT_IMPRESSIONS_DEDUPE_TIME_INTERVAL;
private RolloutCacheConfiguration mRolloutCacheConfiguration = RolloutCacheConfiguration.builder().build();

public TestableSplitConfigBuilder() {
mServiceEndpoints = ServiceEndpoints.builder().build();
Expand Down Expand Up @@ -274,6 +276,11 @@ public TestableSplitConfigBuilder impressionsDedupeTimeInterval(long impressions
return this;
}

public TestableSplitConfigBuilder rolloutCacheConfiguration(RolloutCacheConfiguration rolloutCacheConfiguration) {
this.mRolloutCacheConfiguration = rolloutCacheConfiguration;
return this;
}

public SplitClientConfig build() {
Constructor constructor = SplitClientConfig.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
Expand Down Expand Up @@ -329,7 +336,8 @@ public SplitClientConfig build() {
mPrefix,
mObserverCacheExpirationPeriod,
mCertificatePinningConfiguration,
mImpressionsDedupeTimeInterval);
mImpressionsDedupeTimeInterval,
mRolloutCacheConfiguration);

Logger.instance().setLevel(mLogLevel);
return config;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import helper.IntegrationHelper;
import helper.SplitEventTaskHelper;
import helper.TestableSplitConfigBuilder;
import io.split.android.client.ServiceEndpoints;
import io.split.android.client.SplitClient;
import io.split.android.client.SplitClientConfig;
import io.split.android.client.SplitFactory;
Expand All @@ -32,15 +33,21 @@
import io.split.android.client.storage.db.SplitRoomDatabase;
import io.split.android.client.storage.db.attributes.AttributesEntity;
import io.split.android.client.utils.Json;
import okhttp3.mockwebserver.Dispatcher;
import okhttp3.mockwebserver.MockResponse;
import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;

public class AttributesIntegrationTest {

private Context mContext;
private SplitRoomDatabase mRoomDb;
private SplitFactory mSplitFactory;
private MockWebServer mWebServer;

@Before
public void setup() {
setupServer();
mContext = InstrumentationRegistry.getInstrumentation().getContext();
mRoomDb = DatabaseHelper.getTestDatabase(mContext);
mRoomDb.clearAllTables();
Expand All @@ -51,7 +58,7 @@ public void testPersistentAttributes() throws InterruptedException {
insertSplitsFromFileIntoDB();
CountDownLatch readyLatch = new CountDownLatch(1);
SplitClient client = getSplitClient(readyLatch, true, null);
readyLatch.await(5, TimeUnit.SECONDS);
readyLatch.await(10, TimeUnit.SECONDS);

// 1. Evaluate without attrs
Assert.assertEquals("on", client.getTreatment("workm"));
Expand Down Expand Up @@ -224,8 +231,12 @@ private void setAttributesInClientAndEvaluate(SplitClient client) {

private SplitClient getSplitClient(CountDownLatch readyLatch, boolean persistenceEnabled, String matchingKey) {
if (mSplitFactory == null) {
final String url = mWebServer.url("/").url().toString();
ServiceEndpoints endpoints = ServiceEndpoints.builder()
.apiEndpoint(url).eventsEndpoint(url).build();
SplitClientConfig config = new TestableSplitConfigBuilder()
.enableDebug()
.serviceEndpoints(endpoints)
.featuresRefreshRate(9999)
.segmentsRefreshRate(9999)
.impressionsRefreshRate(9999)
Expand Down Expand Up @@ -272,4 +283,24 @@ private List<Split> getSplitListFromJson() {

return changes.splits;
}

private void setupServer() {
mWebServer = new MockWebServer();

final Dispatcher dispatcher = new Dispatcher() {

@Override
public MockResponse dispatch(RecordedRequest request) throws InterruptedException {
if (request.getPath().contains("/" + IntegrationHelper.ServicePath.MEMBERSHIPS)) {
return new MockResponse().setResponseCode(200).setBody(IntegrationHelper.dummyAllSegments());
} else if (request.getPath().contains("/splitChanges")) {
return new MockResponse().setResponseCode(200)
.setBody(IntegrationHelper.emptySplitChanges(-1, 10000));
} else {
return new MockResponse().setResponseCode(404);
}
}
};
mWebServer.setDispatcher(dispatcher);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.split.android.client;

import io.split.android.client.service.ServiceConstants;
import io.split.android.client.utils.logger.Logger;

public class RolloutCacheConfiguration {

private final int mExpiration;
private final boolean mClearOnInit;

private RolloutCacheConfiguration(int expiration, boolean clearOnInit) {
mExpiration = expiration;
mClearOnInit = clearOnInit;
}

public int getExpiration() {
return mExpiration;
}

public boolean clearOnInit() {
return mClearOnInit;
}

public static Builder builder() {
return new Builder();
}

public static class Builder {

private static final int MIN_EXPIRATION_DAYS = 1;

private int mExpiration = ServiceConstants.DEFAULT_ROLLOUT_CACHE_EXPIRATION;
private boolean mClearOnInit = false;

private Builder() {

}

/**
* Set the expiration time for the rollout definitions cache, in days. Default is 10 days.
* @param expiration in days
* @return This builder
*/
public Builder expiration(int expiration) {
if (expiration < MIN_EXPIRATION_DAYS) {
Logger.w("Cache expiration must be at least 1 day. Using default value.");
mExpiration = ServiceConstants.DEFAULT_ROLLOUT_CACHE_EXPIRATION;
} else {
mExpiration = expiration;
}

return this;
}

/**
* Set if the rollout definitions cache should be cleared on initialization. Default is false.
* @param clearOnInit whether to clear cache on initialization.
* @return This builder
*/
public Builder clearOnInit(boolean clearOnInit) {
mClearOnInit = clearOnInit;
return this;
}

public RolloutCacheConfiguration build() {
return new RolloutCacheConfiguration(mExpiration, mClearOnInit);
}
}
}
31 changes: 27 additions & 4 deletions src/main/java/io/split/android/client/SplitClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ public class SplitClientConfig {
private final long mObserverCacheExpirationPeriod;
private final CertificatePinningConfiguration mCertificatePinningConfiguration;
private final long mImpressionsDedupeTimeInterval;
@NonNull
private final RolloutCacheConfiguration mRolloutCacheConfiguration;

public static Builder builder() {
return new Builder();
Expand Down Expand Up @@ -185,7 +187,8 @@ private SplitClientConfig(String endpoint,
String prefix,
long observerCacheExpirationPeriod,
CertificatePinningConfiguration certificatePinningConfiguration,
long impressionsDedupeTimeInterval) {
long impressionsDedupeTimeInterval,
RolloutCacheConfiguration rolloutCacheConfiguration) {
mEndpoint = endpoint;
mEventsEndpoint = eventsEndpoint;
mTelemetryEndpoint = telemetryEndpoint;
Expand Down Expand Up @@ -243,6 +246,7 @@ private SplitClientConfig(String endpoint,
mObserverCacheExpirationPeriod = observerCacheExpirationPeriod;
mCertificatePinningConfiguration = certificatePinningConfiguration;
mImpressionsDedupeTimeInterval = impressionsDedupeTimeInterval;
mRolloutCacheConfiguration = rolloutCacheConfiguration;
}

public String trafficType() {
Expand Down Expand Up @@ -486,8 +490,8 @@ public long impressionsDedupeTimeInterval() {
return mImpressionsDedupeTimeInterval;
}

public boolean clearOnInit() {
return false; // TODO: to be implemented in the future
public RolloutCacheConfiguration rolloutCacheConfiguration() {
return mRolloutCacheConfiguration;
}

public static final class Builder {
Expand Down Expand Up @@ -566,6 +570,8 @@ public static final class Builder {

private long mImpressionsDedupeTimeInterval = ServiceConstants.DEFAULT_IMPRESSIONS_DEDUPE_TIME_INTERVAL;

private RolloutCacheConfiguration mRolloutCacheConfiguration = RolloutCacheConfiguration.builder().build();

public Builder() {
mServiceEndpoints = ServiceEndpoints.builder().build();
}
Expand Down Expand Up @@ -1106,6 +1112,22 @@ public Builder impressionsDedupeTimeInterval(long impressionsDedupeTimeInterval)
return this;
}

/**
* Configuration for rollout definitions cache.
*
* @param rolloutCacheConfiguration Configuration object
* @return This builder
*/
public Builder rolloutCacheConfiguration(@NonNull RolloutCacheConfiguration rolloutCacheConfiguration) {
if (rolloutCacheConfiguration == null) {
Logger.w("Rollout cache configuration is null. Setting to default value.");
mRolloutCacheConfiguration = RolloutCacheConfiguration.builder().build();
} else {
mRolloutCacheConfiguration = rolloutCacheConfiguration;
}
return this;
}

public SplitClientConfig build() {
Logger.instance().setLevel(mLogLevel);

Expand Down Expand Up @@ -1237,7 +1259,8 @@ public SplitClientConfig build() {
mPrefix,
mObserverCacheExpirationPeriod,
mCertificatePinningConfiguration,
mImpressionsDedupeTimeInterval);
mImpressionsDedupeTimeInterval,
mRolloutCacheConfiguration);
}

private HttpProxy parseProxyHost(String proxyUri) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class ServiceConstants {
public static final long MIN_INITIAL_DELAY = 5L;
public static final int DEFAULT_RECORDS_PER_PUSH = 100;
public static final long DEFAULT_SPLITS_CACHE_EXPIRATION_IN_SECONDS = TimeUnit.DAYS.toSeconds(10); // 10 days
public static final int DEFAULT_ROLLOUT_CACHE_EXPIRATION = 10; // 10 days

public static final int MAX_ROWS_PER_QUERY = 100;

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import java.util.concurrent.TimeUnit;

import io.split.android.client.RolloutCacheConfiguration;
import io.split.android.client.SplitClientConfig;
import io.split.android.client.service.CleanUpDatabaseTask;
import io.split.android.client.service.executor.SplitTask;
Expand All @@ -27,19 +28,20 @@ public class RolloutCacheManagerImpl implements RolloutCacheManager, SplitTask {
@NonNull
private final GeneralInfoStorage mGeneralInfoStorage;
@NonNull
private final RolloutCacheManagerConfig mConfig;
private final RolloutCacheConfiguration mConfig;
@NonNull
private final RolloutDefinitionsCache[] mStorages;
@NonNull
private final CleanUpDatabaseTask mCleanUpDatabaseTask;
@NonNull
private final EncryptionMigrationTask mEncryptionMigrationTask;

public RolloutCacheManagerImpl(@NonNull SplitClientConfig splitClientConfig, @NonNull SplitStorageContainer storageContainer,
public RolloutCacheManagerImpl(@NonNull SplitClientConfig splitClientConfig,
@NonNull SplitStorageContainer storageContainer,
@NonNull CleanUpDatabaseTask cleanUpDatabaseTask,
@NonNull EncryptionMigrationTask encryptionMigrationTask) {
this(storageContainer.getGeneralInfoStorage(),
RolloutCacheManagerConfig.from(splitClientConfig),
splitClientConfig.rolloutCacheConfiguration(),
cleanUpDatabaseTask,
encryptionMigrationTask,
storageContainer.getSplitsStorage(),
Expand All @@ -49,12 +51,12 @@ public RolloutCacheManagerImpl(@NonNull SplitClientConfig splitClientConfig, @No

@VisibleForTesting
RolloutCacheManagerImpl(@NonNull GeneralInfoStorage generalInfoStorage,
@NonNull RolloutCacheManagerConfig config,
@NonNull CleanUpDatabaseTask clean,
@NonNull RolloutCacheConfiguration config,
@NonNull CleanUpDatabaseTask cleanUpDatabaseTask,
@NonNull EncryptionMigrationTask encryptionMigrationTask,
@NonNull RolloutDefinitionsCache... storages) {
mGeneralInfoStorage = checkNotNull(generalInfoStorage);
mCleanUpDatabaseTask = checkNotNull(clean);
mCleanUpDatabaseTask = checkNotNull(cleanUpDatabaseTask);
mEncryptionMigrationTask = checkNotNull(encryptionMigrationTask);
mStorages = checkNotNull(storages);
mConfig = checkNotNull(config);
Expand All @@ -70,7 +72,7 @@ public void validateCache(SplitTaskExecutionListener listener) {
execute();
Logger.v("Rollout cache manager: Migrating encryption");
mEncryptionMigrationTask.execute();
Logger.v("Rollout cache manager: validation finished");
Logger.v("Rollout cache manager: Validation finished");
listener.taskExecuted(SplitTaskExecutionInfo.success(SplitTaskType.GENERIC_TASK));
} catch (Exception ex) {
Logger.e("Error occurred validating cache: " + ex.getMessage());
Expand All @@ -95,15 +97,18 @@ public SplitTaskExecutionInfo execute() {
}

private boolean validateExpiration() {
// calculate elapsed time since last update
long lastUpdateTimestamp = mGeneralInfoStorage.getSplitsUpdateTimestamp();
// calculate elapsed time since last update
long daysSinceLastUpdate = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - lastUpdateTimestamp);

if (daysSinceLastUpdate > mConfig.getCacheExpirationInDays()) {
if (lastUpdateTimestamp > 0 && daysSinceLastUpdate > mConfig.getExpiration()) {
Logger.v("Clearing rollout definitions cache due to expiration");
return true;
} else if (mConfig.isClearOnInit()) {
} else if (mConfig.clearOnInit()) {
long lastCacheClearTimestamp = mGeneralInfoStorage.getRolloutCacheLastClearTimestamp();
if (lastCacheClearTimestamp < 1) {
return true;
}
long daysSinceCacheClear = TimeUnit.MILLISECONDS.toDays(System.currentTimeMillis() - lastCacheClearTimestamp);

// don't clear too soon
Expand Down
Loading

0 comments on commit 61c484a

Please sign in to comment.