Skip to content

Commit

Permalink
Release 5.0.1 (#715)
Browse files Browse the repository at this point in the history
  • Loading branch information
gthea authored Nov 22, 2024
2 parents 4c9c4e5 + c8001de commit f39d3fc
Show file tree
Hide file tree
Showing 28 changed files with 235 additions and 111 deletions.
3 changes: 3 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
5.0.1 (Nov 22, 2024)
- Optimized persistence of impressions deduplication cache.

5.0.0 (Nov 1, 2024)
- Added support for targeting rules based on large segments.
- BREAKING: Dropped support for Split Proxy below version 5.9.0. The SDK now requires Split Proxy 5.9.0 or above.
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ apply plugin: 'kotlin-android'
apply from: 'spec.gradle'

ext {
splitVersion = '5.0.0'
splitVersion = '5.0.1'
}

android {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import org.junit.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -24,6 +25,8 @@
import fake.HttpClientMock;
import fake.HttpResponseMock;
import fake.HttpResponseMockDispatcher;
import fake.LifecycleManagerStub;
import fake.SynchronizerSpyImpl;
import helper.DatabaseHelper;
import helper.FileHelper;
import helper.IntegrationHelper;
Expand All @@ -36,8 +39,10 @@
import io.split.android.client.impressions.Impression;
import io.split.android.client.impressions.ImpressionListener;
import io.split.android.client.service.impressions.ImpressionsMode;
import io.split.android.client.service.synchronizer.SynchronizerSpy;
import io.split.android.client.storage.db.ImpressionEntity;
import io.split.android.client.storage.db.SplitRoomDatabase;
import io.split.android.client.storage.db.impressions.observer.ImpressionsObserverCacheEntity;
import io.split.android.client.utils.Json;
import tests.integration.shared.TestingHelper;

Expand All @@ -47,13 +52,18 @@ public class DedupeIntegrationTest {
private AtomicInteger mImpressionsListenerCount;
private HttpClientMock mHttpClient;
private SplitRoomDatabase mDatabase;
private LifecycleManagerStub mLifecycleManager;
private SynchronizerSpy mSynchronizerSpy;

@Before
public void setUp() throws IOException {
mImpressionsListenerCount = new AtomicInteger(0);
mDatabase = DatabaseHelper.getTestDatabase(mContext);
mDatabase.clearAllTables();
mHttpClient = new HttpClientMock(getDispatcher());
mLifecycleManager = new LifecycleManagerStub();
mSynchronizerSpy = new SynchronizerSpyImpl();
mLifecycleManager.register(mSynchronizerSpy);
}

@Test
Expand Down Expand Up @@ -203,26 +213,34 @@ public void close() {
@Test
public void expiredObserverCacheValuesExistingInDatabaseAreRemovedOnStartup() throws InterruptedException {
// prepopulate DB with 2000 entries
List<ImpressionsObserverCacheEntity> entities = new ArrayList<>();
for (int i = 0; i < 2000; i++) {
mDatabase.impressionsObserverCacheDao().insert((long) i, (long) i, System.currentTimeMillis());
entities.add(new ImpressionsObserverCacheEntity(i, i, System.currentTimeMillis()));
}
mDatabase.impressionsObserverCacheDao().insert(entities);

// wait for them to expire
Thread.sleep(1000);
Thread.sleep(2000);

// initialize SDK
SplitClient client = initSplitFactory(new TestableSplitConfigBuilder()
.impressionsMode(ImpressionsMode.DEBUG)
.streamingEnabled(false)
.enableDebug()
.impressionsDedupeTimeInterval(1)
.observerCacheExpirationPeriod(100), mHttpClient).client();
Thread.sleep(200);

client.getTreatment("FACUNDO_TEST");
Thread.sleep(2000);
Thread.sleep(100);
mLifecycleManager.simulateOnPause();
Thread.sleep(500);
mLifecycleManager.simulateOnResume();

while (mDatabase.impressionsObserverCacheDao().getAll(5).size() > 1) {
Thread.sleep(100);
}
Thread.sleep(100);
Thread.sleep(1000);

int count = mDatabase.impressionsObserverCacheDao().getAll(3000).size();
assertEquals(1, count);
Expand Down Expand Up @@ -309,7 +327,8 @@ private SplitFactory initSplitFactory(TestableSplitConfigBuilder builder, HttpCl
builder.build(),
mContext,
httpClient,
mDatabase);
mDatabase, mSynchronizerSpy, null,
mLifecycleManager);

SplitClient client = factory.client();
client.on(SplitEvent.SDK_READY, new TestingHelper.TestEventTask(innerLatch));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class ImpressionsObserverCacheImplIntegrationTest {
@Before
public void setUp() {
ImpressionsObserverCacheDao impressionsObserverCacheDao = DatabaseHelper.getTestDatabase(InstrumentationRegistry.getInstrumentation().getContext()).impressionsObserverCacheDao();
mPersistentStorage = new SqlitePersistentImpressionsObserverCacheStorage(impressionsObserverCacheDao, 2000, 1, Executors.newSingleThreadScheduledExecutor(), new AtomicBoolean(false));
mPersistentStorage = new SqlitePersistentImpressionsObserverCacheStorage(impressionsObserverCacheDao, 2000, Executors.newSingleThreadScheduledExecutor(), new AtomicBoolean(false));
mCache = new ListenableLruCache<>(5, mPersistentStorage);
mImpressionsObserverCacheImpl = new ImpressionsObserverCacheImpl(mPersistentStorage, mCache);
}
Expand Down Expand Up @@ -88,6 +88,7 @@ public void getPutsValueInCacheWhenRetrievedFromPersistentStorage() throws Inter
@Test
public void putPutsValueInCacheAndPersistentStorage() throws InterruptedException {
mImpressionsObserverCacheImpl.put(1L, 2L);
mImpressionsObserverCacheImpl.persist();
Thread.sleep(100);

assertEquals(2L, mPersistentStorage.get(1L).longValue());
Expand All @@ -98,6 +99,7 @@ public void putPutsValueInCacheAndPersistentStorage() throws InterruptedExceptio
public void putUpdatesValueInCacheAndPersistentStorage() throws InterruptedException {
mImpressionsObserverCacheImpl.put(1L, 2L);
mImpressionsObserverCacheImpl.put(1L, 3L);
mImpressionsObserverCacheImpl.persist();
Thread.sleep(100);

assertEquals(3L, mPersistentStorage.get(1L).longValue());
Expand All @@ -106,6 +108,7 @@ public void putUpdatesValueInCacheAndPersistentStorage() throws InterruptedExcep

private void putInStorageAndWait() throws InterruptedException {
mPersistentStorage.put(1L, 2L);
mPersistentStorage.persist();
Thread.sleep(100);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import androidx.test.platform.app.InstrumentationRegistry;

Expand Down Expand Up @@ -32,7 +34,7 @@ public class ImpressionsObserverTest {
@Before
public void setUp() {
ImpressionsObserverCacheDao dao = DatabaseHelper.getTestDatabase(InstrumentationRegistry.getInstrumentation().getContext()).impressionsObserverCacheDao();
mStorage = new SqlitePersistentImpressionsObserverCacheStorage(dao, 2000, 1, Executors.newSingleThreadScheduledExecutor(), new AtomicBoolean(false));
mStorage = new SqlitePersistentImpressionsObserverCacheStorage(dao, 2000, Executors.newSingleThreadScheduledExecutor(), new AtomicBoolean(false));
}

private List<Impression> generateImpressions(long count) {
Expand Down Expand Up @@ -89,6 +91,7 @@ public void testValuesArePersistedAcrossInstances() throws InterruptedException
// These are not in the cache, so they should return null
Long firstImp = observer.testAndSet(imp);
Long firstImp2 = observer.testAndSet(imp2);
observer.persist();
Thread.sleep(2);

// These are in the cache, so they should return a value
Expand Down Expand Up @@ -155,6 +158,15 @@ public void testAndSetWithNullImpressionReturnsNullPreviousTime() {
assertNull(observer.testAndSet(null));
}

@Test
public void persistCallsPersistOnStorage() {
ImpressionsObserverCache cache = mock(ImpressionsObserverCache.class);
ImpressionsObserver observer = new ImpressionsObserverImpl(cache);
observer.persist();

verify(cache).persist();
}

private void caller(ImpressionsObserver o, int count, ConcurrentLinkedQueue<Impression> imps) {

while (count-- > 0) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.junit.Before;
import org.junit.Test;

import java.util.Arrays;
import java.util.List;

import helper.DatabaseHelper;
Expand All @@ -34,8 +35,7 @@ public void tearDown() {

@Test
public void valuesAreInsertedCorrectly() {
mImpressionsObserverCacheDao.insert(1L, 2L, 3L);
mImpressionsObserverCacheDao.insert(3L, 2L, 5L);
insertIntoDao();

List<ImpressionsObserverCacheEntity> all = mImpressionsObserverCacheDao.getAll(3);

Expand All @@ -50,10 +50,17 @@ public void valuesAreInsertedCorrectly() {
assertEquals(secondEntity.getCreatedAt(), 5L);
}

private void insertIntoDao() {
List<ImpressionsObserverCacheEntity> entities = Arrays.asList(
new ImpressionsObserverCacheEntity(1L, 2L, 3L),
new ImpressionsObserverCacheEntity(3L, 2L, 5L));
mImpressionsObserverCacheDao.insert(entities);
}

@Test
public void valueWithNewHashReplacesOldOne() {
mImpressionsObserverCacheDao.insert(1L, 2L, 3L);
mImpressionsObserverCacheDao.insert(1L, 4L, 5L);
insertIntoDaoOnce(1L, 2L, 3L);
insertIntoDaoOnce(1L, 4L, 5L);

List<ImpressionsObserverCacheEntity> all = mImpressionsObserverCacheDao.getAll(3);

Expand All @@ -64,10 +71,14 @@ public void valueWithNewHashReplacesOldOne() {
assertEquals(firstEntity.getCreatedAt(), 5L);
}

private void insertIntoDaoOnce(long hash, long time, long createdAt) {
ImpressionsObserverCacheEntity entity = new ImpressionsObserverCacheEntity(hash, time, createdAt);
mImpressionsObserverCacheDao.insert(Arrays.asList(entity));
}

@Test
public void deleteRemovesCorrectHash() {
mImpressionsObserverCacheDao.insert(1L, 2L, 3L);
mImpressionsObserverCacheDao.insert(3L, 2L, 5L);
insertIntoDao();
mImpressionsObserverCacheDao.delete(1L);

List<ImpressionsObserverCacheEntity> all = mImpressionsObserverCacheDao.getAll(3);
Expand All @@ -81,10 +92,9 @@ public void deleteRemovesCorrectHash() {

@Test
public void getAllWithLimitReturnsTheCorrectAmount() {
mImpressionsObserverCacheDao.insert(1L, 2L, 3L);
mImpressionsObserverCacheDao.insert(3L, 2L, 5L);
mImpressionsObserverCacheDao.insert(4L, 2L, 6L);
mImpressionsObserverCacheDao.insert(5L, 2L, 7L);
insertIntoDao();
insertIntoDaoOnce(4L, 2L, 6L);
insertIntoDaoOnce(5L, 2L, 7L);

List<ImpressionsObserverCacheEntity> all = mImpressionsObserverCacheDao.getAll(2);

Expand All @@ -93,10 +103,10 @@ public void getAllWithLimitReturnsTheCorrectAmount() {

@Test
public void getAllReturnsElementsOrderedByCreatedAtAsc() {
mImpressionsObserverCacheDao.insert(3L, 2L, 3L);
mImpressionsObserverCacheDao.insert(4L, 6L, 5L);
mImpressionsObserverCacheDao.insert(5L, 4L, 6L);
mImpressionsObserverCacheDao.insert(1L, 1L, 7L);
insertIntoDaoOnce(3L, 2L, 3L);
insertIntoDaoOnce(4L, 6L, 5L);
insertIntoDaoOnce(5L, 4L, 6L);
insertIntoDaoOnce(1L, 1L, 7L);

List<ImpressionsObserverCacheEntity> all = mImpressionsObserverCacheDao.getAll(4);

Expand All @@ -109,13 +119,13 @@ public void getAllReturnsElementsOrderedByCreatedAtAsc() {

@Test
public void deleteOldestRemovesCorrectValues() {
mImpressionsObserverCacheDao.insert(3L, 2L, 3L);
mImpressionsObserverCacheDao.insert(4L, 6L, 5L);
insertIntoDaoOnce(3L, 2L, 3L);
insertIntoDaoOnce(4L, 6L, 5L);

// only these ones should remain
mImpressionsObserverCacheDao.insert(5L, 4L, 6L);
mImpressionsObserverCacheDao.insert(12L, 4L, 7L);
mImpressionsObserverCacheDao.insert(21L, 3L, 8L);
insertIntoDaoOnce(5L, 4L, 6L);
insertIntoDaoOnce(12L, 4L, 7L);
insertIntoDaoOnce(21L, 3L, 8L);

mImpressionsObserverCacheDao.deleteOldest(6);

Expand All @@ -129,8 +139,8 @@ public void deleteOldestRemovesCorrectValues() {

@Test
public void getSingleValueReturnsCorrectValue() {
mImpressionsObserverCacheDao.insert(3L, 2L, 3L);
mImpressionsObserverCacheDao.insert(4L, 6L, 5L);
insertIntoDaoOnce(3L, 2L, 3L);
insertIntoDaoOnce(4L, 6L, 5L);

ImpressionsObserverCacheEntity entity = mImpressionsObserverCacheDao.get(3L);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package tests.integration.streaming;

import static java.lang.Thread.sleep;

import android.content.Context;

import androidx.core.util.Pair;
Expand All @@ -12,6 +14,7 @@

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
Expand Down Expand Up @@ -43,13 +46,11 @@
import io.split.android.client.storage.db.StorageRecordStatus;
import io.split.android.client.storage.db.impressions.observer.ImpressionsObserverCacheDao;
import io.split.android.client.storage.db.impressions.observer.ImpressionsObserverCacheEntity;
import io.split.android.client.utils.logger.Logger;
import io.split.android.client.storage.db.impressions.unique.UniqueKeyEntity;
import io.split.android.client.storage.db.impressions.unique.UniqueKeysDao;
import io.split.android.client.utils.logger.Logger;
import io.split.android.client.utils.logger.SplitLogLevel;

import static java.lang.Thread.sleep;

public class CleanUpDatabaseTest {
Context mContext;
BlockingQueue<String> mStreamingData;
Expand Down Expand Up @@ -109,9 +110,11 @@ public void testCleanUp() throws IOException, InterruptedException {
mUniqueKeysDao.insert(createUniqueKeyEntity(now() + 10, StorageRecordStatus.ACTIVE, "active"));
mUniqueKeysDao.insert(createUniqueKeyEntity(expiratedTime(), StorageRecordStatus.ACTIVE, "expirated"));

mImpressionsObserverCacheDao.insert(1L, 2L, now());
mImpressionsObserverCacheDao.insert(5L, 6L, now());
mImpressionsObserverCacheDao.insert(3L, 4L, TimeUnit.SECONDS.toMillis(now()) - ServiceConstants.DEFAULT_OBSERVER_CACHE_EXPIRATION_PERIOD_MS);
List<ImpressionsObserverCacheEntity> entities = new ArrayList<>();
entities.add(new ImpressionsObserverCacheEntity(1L, 2L, now()));
entities.add(new ImpressionsObserverCacheEntity(5L, 6L, now()));
entities.add(new ImpressionsObserverCacheEntity(3L, 4L, TimeUnit.SECONDS.toMillis(now()) - ServiceConstants.DEFAULT_OBSERVER_CACHE_EXPIRATION_PERIOD_MS));
mImpressionsObserverCacheDao.insert(entities);

// Load records to check if inserted correctly on assert stage
List<EventEntity> insertedEvents = mEventDao.getBy(0, StorageRecordStatus.ACTIVE, 10);
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/io/split/android/client/SplitFactoryHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

Expand Down Expand Up @@ -153,7 +154,8 @@ SplitStorageContainer buildStorageContainer(UserConsent userConsentStatus,
boolean shouldRecordTelemetry,
SplitCipher splitCipher,
TelemetryStorage telemetryStorage,
long observerCacheExpirationPeriod) {
long observerCacheExpirationPeriod,
ScheduledThreadPoolExecutor impressionsObserverExecutor) {

boolean isPersistenceEnabled = userConsentStatus == UserConsent.GRANTED;
PersistentEventsStorage persistentEventsStorage =
Expand All @@ -174,7 +176,7 @@ SplitStorageContainer buildStorageContainer(UserConsent userConsentStatus,
StorageFactory.getAttributesStorage(),
StorageFactory.getPersistentAttributesStorage(splitRoomDatabase, splitCipher),
getTelemetryStorage(shouldRecordTelemetry, telemetryStorage),
StorageFactory.getImpressionsObserverCachePersistentStorage(splitRoomDatabase, observerCacheExpirationPeriod));
StorageFactory.getImpressionsObserverCachePersistentStorage(splitRoomDatabase, observerCacheExpirationPeriod, impressionsObserverExecutor));
}

SplitApiFacade buildApiFacade(SplitClientConfig splitClientConfig,
Expand Down
Loading

0 comments on commit f39d3fc

Please sign in to comment.