Skip to content
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

Rollout cache manager in SDK init #718

Merged
merged 11 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/androidTest/java/fake/SynchronizerSpyImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

import io.split.android.client.api.Key;
import io.split.android.client.dtos.Event;
import io.split.android.client.impressions.Impression;
import io.split.android.client.service.synchronizer.Synchronizer;
Expand Down Expand Up @@ -122,12 +123,12 @@ public void unregisterAttributesSynchronizer(String userKey) {
}

@Override
public void registerMySegmentsSynchronizer(String userKey, MySegmentsSynchronizer mySegmentsSynchronizer) {
((MySegmentsSynchronizerRegistry) mSynchronizer).registerMySegmentsSynchronizer(userKey, mySegmentsSynchronizer);
public void registerMySegmentsSynchronizer(Key key, MySegmentsSynchronizer mySegmentsSynchronizer) {
((MySegmentsSynchronizerRegistry) mSynchronizer).registerMySegmentsSynchronizer(key, mySegmentsSynchronizer);
}

@Override
public void unregisterMySegmentsSynchronizer(String userKey) {
((MySegmentsSynchronizerRegistry) mSynchronizer).unregisterMySegmentsSynchronizer(userKey);
public void unregisterMySegmentsSynchronizer(Key key) {
((MySegmentsSynchronizerRegistry) mSynchronizer).unregisterMySegmentsSynchronizer(key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ public void usingNullPrefixResultsInIgnoredPrefix() {

private static String[] getDbList(Context context) {
// remove -journal dbs since we're not interested in them
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return Arrays.stream(context.databaseList()).filter(db -> !db.endsWith("-journal")).toArray(String[]::new);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ public void testPersistentAttributes2() throws InterruptedException {
// 3. Perform clear and verify there are no attributes on DB
client.clearAttributes();

countDownLatch.await(1, TimeUnit.SECONDS);
countDownLatch.await(7, TimeUnit.SECONDS);

Assert.assertNull(mRoomDb.attributesDao().getByUserKey(userKey));
}
Expand Down Expand Up @@ -153,7 +153,7 @@ public void testPersistentAttributesWithMultiClient2() throws InterruptedExcepti

// 2. Clear second client's attributes and check DB entry has been cleared
client2.clearAttributes();
countDownLatch.await(1, TimeUnit.SECONDS); // waiting since DB operations are async
countDownLatch.await(7, TimeUnit.SECONDS); // waiting since DB operations are async
Assert.assertNull(mRoomDb.attributesDao().getByUserKey("new_key"));

// 3. Verify evaluation with first client uses attribute
Expand All @@ -162,7 +162,7 @@ public void testPersistentAttributesWithMultiClient2() throws InterruptedExcepti
// 4. Perform clear and verify there are no attributes on DB
client.clearAttributes();

countDownLatch.await(1, TimeUnit.SECONDS);
countDownLatch.await(7, TimeUnit.SECONDS);

Assert.assertNull(mRoomDb.attributesDao().getByUserKey(matchingKey));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ public void onPostExecutionView(SplitClient client) {
}
});

assertTrue(latch.await(2, TimeUnit.SECONDS));
assertTrue(latch.await(5, TimeUnit.SECONDS));
mLifecycleManager.simulateOnPause();
Thread.sleep(200);
mLifecycleManager.simulateOnResume();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,8 @@ public void setUp() throws IOException, InterruptedException {
public void unboundedLargeSegmentsUpdateTriggersSdkUpdate() throws IOException, InterruptedException {
TestSetup testSetup = getTestSetup();

boolean mySegmentsAwait = mLatches.get(MY_SEGMENTS).await(10, TimeUnit.SECONDS);
boolean splitsAwait = mLatches.get(SPLIT_CHANGES).await(10, TimeUnit.SECONDS);
boolean mySegmentsAwait = mLatches.get(MY_SEGMENTS).await(15, TimeUnit.SECONDS);
boolean splitsAwait = mLatches.get(SPLIT_CHANGES).await(15, TimeUnit.SECONDS);
String initialSegmentList = testSetup.database.myLargeSegmentDao().getByUserKey(IntegrationHelper.dummyUserKey().matchingKey()).getSegmentList();
mRandomizeMyLargeSegments.set(true);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ public void onPostExecutionView(SplitClient client) {
}
});
insertSplitsIntoDb();
readyLatch.await(10, TimeUnit.SECONDS);
readyLatch2.await(10, TimeUnit.SECONDS);
readyLatch.await(15, TimeUnit.SECONDS);
readyLatch2.await(15, TimeUnit.SECONDS);

assertEquals(1, readyCount.get());
assertEquals(1, readyCount2.get());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@
import org.junit.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

import helper.TestableSplitConfigBuilder;
import io.split.android.client.SplitClientConfig;
import io.split.android.client.storage.db.StorageFactory;
import io.split.android.client.telemetry.model.streaming.OccupancyPriStreamingEvent;
import io.split.android.client.telemetry.model.streaming.OccupancySecStreamingEvent;
import io.split.android.client.telemetry.model.streaming.StreamingEvent;
import io.split.android.client.telemetry.model.streaming.TokenRefreshStreamingEvent;
import io.split.android.client.telemetry.storage.TelemetryStorage;
import tests.integration.streaming.OccupancyBaseTest;

public class TelemetryOccupancyTest extends OccupancyBaseTest {
Expand All @@ -35,12 +35,19 @@ public class TelemetryOccupancyTest extends OccupancyBaseTest {

@Test
public void telemetryOccupancyPriStreamingEvent() throws InterruptedException, IOException {
new CountDownLatch(1);
getSplitFactory(mTelemetryEnabledConfig);

pushOccupancy(PRIMARY_CHANNEL, 1);
sleep(2000);

List<StreamingEvent> streamingEvents = mTelemetryStorage.popStreamingEvents();
long startTime = System.currentTimeMillis();
List<StreamingEvent> streamingEvents = new ArrayList<>();
streamingEvents = mTelemetryStorage.popStreamingEvents();
while (System.currentTimeMillis() - startTime < 5000 &&
!streamingEvents.stream().anyMatch(event -> event instanceof OccupancyPriStreamingEvent)) {
Thread.sleep(100);
streamingEvents = mTelemetryStorage.popStreamingEvents();
}
assertTrue(streamingEvents.stream().anyMatch(event -> event instanceof OccupancyPriStreamingEvent));
}

Expand All @@ -51,7 +58,14 @@ public void telemetryOccupancySecStreamingEvent() throws InterruptedException, I
pushOccupancy(SECONDARY_CHANNEL, 1);
sleep(2000);

List<StreamingEvent> streamingEvents = mTelemetryStorage.popStreamingEvents();
long startTime = System.currentTimeMillis();
List<StreamingEvent> streamingEvents = new ArrayList<>();
streamingEvents = mTelemetryStorage.popStreamingEvents();
while (System.currentTimeMillis() - startTime < 5000 &&
!streamingEvents.stream().anyMatch(event -> event instanceof OccupancySecStreamingEvent)) {
Thread.sleep(100);
streamingEvents = mTelemetryStorage.popStreamingEvents();
}
assertTrue(streamingEvents.stream().anyMatch(event -> event instanceof OccupancySecStreamingEvent));
}

Expand Down
97 changes: 82 additions & 15 deletions src/main/java/io/split/android/client/SplitFactoryHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.util.Pair;
import androidx.work.WorkManager;

Expand All @@ -22,11 +23,15 @@

import io.split.android.client.common.CompressionUtilProvider;
import io.split.android.client.events.EventsManagerCoordinator;
import io.split.android.client.events.SplitInternalEvent;
import io.split.android.client.lifecycle.SplitLifecycleManager;
import io.split.android.client.network.HttpClient;
import io.split.android.client.network.SdkTargetPath;
import io.split.android.client.network.SplitHttpHeadersBuilder;
import io.split.android.client.service.ServiceFactory;
import io.split.android.client.service.SplitApiFacade;
import io.split.android.client.service.executor.SplitSingleThreadTaskExecutor;
import io.split.android.client.service.executor.SplitTaskExecutionInfo;
import io.split.android.client.service.executor.SplitTaskExecutionListener;
import io.split.android.client.service.executor.SplitTaskExecutor;
import io.split.android.client.service.executor.SplitTaskFactory;
Expand Down Expand Up @@ -56,6 +61,8 @@
import io.split.android.client.service.sseclient.sseclient.SseHandler;
import io.split.android.client.service.sseclient.sseclient.SseRefreshTokenTimer;
import io.split.android.client.service.sseclient.sseclient.StreamingComponents;
import io.split.android.client.service.synchronizer.RolloutCacheManager;
import io.split.android.client.service.synchronizer.RolloutCacheManagerImpl;
import io.split.android.client.service.synchronizer.SyncGuardian;
import io.split.android.client.service.synchronizer.SyncGuardianImpl;
import io.split.android.client.service.synchronizer.SyncManager;
Expand All @@ -70,7 +77,6 @@
import io.split.android.client.shared.ClientComponentsRegisterImpl;
import io.split.android.client.shared.UserConsent;
import io.split.android.client.storage.attributes.PersistentAttributesStorage;
import io.split.android.client.storage.cipher.EncryptionMigrationTask;
import io.split.android.client.storage.cipher.SplitCipher;
import io.split.android.client.storage.cipher.SplitCipherFactory;
import io.split.android.client.storage.cipher.SplitEncryptionLevel;
Expand All @@ -86,6 +92,7 @@
import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer;
import io.split.android.client.telemetry.storage.TelemetryStorage;
import io.split.android.client.utils.Utils;
import io.split.android.client.utils.logger.Logger;

class SplitFactoryHelper {
private static final int DB_MAGIC_CHARS_COUNT = 4;
Expand Down Expand Up @@ -399,21 +406,10 @@ public ProcessStrategy getImpressionStrategy(SplitTaskExecutor splitTaskExecutor
.getStrategy(config.impressionsMode());
}

SplitCipher migrateEncryption(String apiKey,
SplitRoomDatabase splitDatabase,
SplitTaskExecutor splitTaskExecutor,
final boolean encryptionEnabled,
SplitTaskExecutionListener executionListener) {

SplitCipher toCipher = SplitCipherFactory.create(apiKey, encryptionEnabled ? SplitEncryptionLevel.AES_128_CBC :
@Nullable
SplitCipher getCipher(String apiKey, boolean encryptionEnabled) {
return SplitCipherFactory.create(apiKey, encryptionEnabled ? SplitEncryptionLevel.AES_128_CBC :
SplitEncryptionLevel.NONE);
splitTaskExecutor.submit(new EncryptionMigrationTask(apiKey,
splitDatabase,
encryptionEnabled,
toCipher),
executionListener);

return toCipher;
}

@Nullable
Expand Down Expand Up @@ -486,4 +482,75 @@ public URI build(String matchingKey) throws URISyntaxException {
return SdkTargetPath.mySegments(mEndpoint, matchingKey);
}
}

static class Initializer implements Runnable {

private final RolloutCacheManager mRolloutCacheManager;
private final SplitTaskExecutionListener mListener;

Initializer(
String apiToken,
SplitClientConfig config,
SplitTaskFactory splitTaskFactory,
SplitRoomDatabase splitDatabase,
SplitCipher splitCipher,
EventsManagerCoordinator eventsManagerCoordinator,
SplitTaskExecutor splitTaskExecutor,
SplitSingleThreadTaskExecutor splitSingleThreadTaskExecutor,
SplitStorageContainer storageContainer,
SyncManager syncManager,
SplitLifecycleManager lifecycleManager) {

this(new RolloutCacheManagerImpl(config,
storageContainer,
splitTaskFactory.createCleanUpDatabaseTask(System.currentTimeMillis() / 1000),
splitTaskFactory.createEncryptionMigrationTask(apiToken, splitDatabase, config.encryptionEnabled(), splitCipher)),
new Listener(eventsManagerCoordinator, splitTaskExecutor, splitSingleThreadTaskExecutor, syncManager, lifecycleManager));
}

@VisibleForTesting
Initializer(RolloutCacheManager rolloutCacheManager, SplitTaskExecutionListener listener) {
mRolloutCacheManager = rolloutCacheManager;
mListener = listener;
}

@Override
public void run() {
mRolloutCacheManager.validateCache(mListener);
}

static class Listener implements SplitTaskExecutionListener {

private final EventsManagerCoordinator mEventsManagerCoordinator;
private final SplitTaskExecutor mSplitTaskExecutor;
private final SplitSingleThreadTaskExecutor mSplitSingleThreadTaskExecutor;
private final SyncManager mSyncManager;
private final SplitLifecycleManager mLifecycleManager;

Listener(EventsManagerCoordinator eventsManagerCoordinator,
SplitTaskExecutor splitTaskExecutor,
SplitSingleThreadTaskExecutor splitSingleThreadTaskExecutor,
SyncManager syncManager,
SplitLifecycleManager lifecycleManager) {
mEventsManagerCoordinator = eventsManagerCoordinator;
mSplitTaskExecutor = splitTaskExecutor;
mSplitSingleThreadTaskExecutor = splitSingleThreadTaskExecutor;
mSyncManager = syncManager;
mLifecycleManager = lifecycleManager;
}

@Override
public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) {
mEventsManagerCoordinator.notifyInternalEvent(SplitInternalEvent.ENCRYPTION_MIGRATION_DONE);

mSplitTaskExecutor.resume();
mSplitSingleThreadTaskExecutor.resume();

mSyncManager.start();
mLifecycleManager.register(mSyncManager);

Logger.i("Android SDK initialized!");
}
}
}
}
Loading
Loading