Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
gthea committed Aug 14, 2024
1 parent 5468055 commit c58cb4b
Show file tree
Hide file tree
Showing 28 changed files with 252 additions and 38 deletions.
14 changes: 9 additions & 5 deletions src/main/java/io/split/android/client/SplitClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,10 @@ public boolean waitForLargeSegments() {
return mLargeSegmentsEnabled && mWaitForLargeSegments;
}

public boolean forceCacheExpiration() {
return true; // TODO
}

public static final class Builder {

static final int PROXY_PORT_DEFAULT = 80;
Expand Down Expand Up @@ -1168,11 +1172,11 @@ public Builder waitForLargeSegments(boolean enabled) {
public SplitClientConfig build() {
Logger.instance().setLevel(mLogLevel);

if (mFeaturesRefreshRate < MIN_FEATURES_REFRESH_RATE) {
Logger.w("Features refresh rate is lower than allowed. " +
"Setting to default value.");
mFeaturesRefreshRate = DEFAULT_FEATURES_REFRESH_RATE_SECS;
}
// TODO if (mFeaturesRefreshRate < MIN_FEATURES_REFRESH_RATE) {
// TODO Logger.w("Features refresh rate is lower than allowed. " +
// TODO "Setting to default value.");
// TODO mFeaturesRefreshRate = DEFAULT_FEATURES_REFRESH_RATE_SECS;
// TODO }

if (mSegmentsRefreshRate < MIN_MY_SEGMENTS_REFRESH_RATE) {
Logger.w("Segments refresh rate is lower than allowed. " +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,8 @@ SplitStorageContainer buildStorageContainer(UserConsent userConsentStatus,
StorageFactory.getAttributesStorage(),
StorageFactory.getPersistentAttributesStorage(splitRoomDatabase, splitCipher),
getTelemetryStorage(shouldRecordTelemetry, telemetryStorage),
StorageFactory.getImpressionsObserverCachePersistentStorage(splitRoomDatabase, observerCacheExpirationPeriod));
StorageFactory.getImpressionsObserverCachePersistentStorage(splitRoomDatabase, observerCacheExpirationPeriod),
StorageFactory.getLastUpdateTimestampProvider(splitRoomDatabase));
}

SplitApiFacade buildApiFacade(SplitClientConfig splitClientConfig,
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/io/split/android/client/SplitFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,8 @@ public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) {
impressionManager,
mStorageContainer.getEventsStorage(),
mEventsManagerCoordinator,
streamingComponents.getPushManagerEventBroadcaster()
streamingComponents.getPushManagerEventBroadcaster(),
mStorageContainer.getLastUpdateTimestampProvider()
);
// Only available for integration tests
if (synchronizerSpy != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

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

import android.os.SystemClock;

import androidx.annotation.VisibleForTesting;

import java.util.ArrayList;
Expand Down Expand Up @@ -29,13 +31,15 @@ public class SplitEventsManager extends BaseEventsManager implements ISplitEvent
private final SplitTaskExecutor mSplitTaskExecutor;
private final AtomicBoolean mLargeSegmentsEnabled;
private final AtomicBoolean mWaitForLargeSegments;
private final long mStartTime;

public SplitEventsManager(SplitClientConfig config, SplitTaskExecutor splitTaskExecutor) {
this(splitTaskExecutor, config.blockUntilReady(), config.largeSegmentsEnabled(), config.waitForLargeSegments());
}

public SplitEventsManager(SplitTaskExecutor splitTaskExecutor, final int blockUntilReady, boolean largeSegmentsEnabled, boolean waitForLargeSegments) {
super();
mStartTime = SystemClock.uptimeMillis();
mSplitTaskExecutor = splitTaskExecutor;
mSubscriptions = new ConcurrentHashMap<>();
mExecutionTimes = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -222,7 +226,7 @@ private void trigger(SplitEvent event) {
// If executionTimes is grater than zero, maximum executions decrease 1
} else if (mExecutionTimes.get(event) > 0) {
if (event != null) {
Logger.d(event.name() + " event triggered");
Logger.d(event.name() + " event triggered in " + (SystemClock.uptimeMillis() - mStartTime) + " ms");
}
mExecutionTimes.put(event, mExecutionTimes.get(event) - 1);
} //If executionTimes is lower than zero, execute it without limitation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ public interface SplitTaskFactory extends TelemetryTaskFactory, ImpressionsTaskF

EventsRecorderTask createEventsRecorderTask();

SplitsSyncTask createSplitsSyncTask(boolean checkCacheExpiration);
SplitsSyncTask createSplitsSyncTask(boolean checkCacheExpiration, boolean forceCacheExpiration);

LoadSplitsTask createLoadSplitsTask();

Expand All @@ -29,4 +29,6 @@ public interface SplitTaskFactory extends TelemetryTaskFactory, ImpressionsTaskF
FilterSplitsInCacheTask createFilterSplitsInCacheTask();

CleanUpDatabaseTask createCleanUpDatabaseTask(long maxTimestamp);

SplitTask createExpireSplitsTask();
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
import io.split.android.client.storage.common.SplitStorageContainer;
import io.split.android.client.telemetry.storage.TelemetryRuntimeProducer;
import io.split.android.client.telemetry.storage.TelemetryStorage;
import io.split.android.client.utils.logger.Logger;

public class SplitTaskFactoryImpl implements SplitTaskFactory {

Expand Down Expand Up @@ -124,9 +125,10 @@ public ImpressionsRecorderTask createImpressionsRecorderTask() {
}

@Override
public SplitsSyncTask createSplitsSyncTask(boolean checkCacheExpiration) {
public SplitsSyncTask createSplitsSyncTask(boolean checkCacheExpiration, boolean forceCacheExpiration) {
return SplitsSyncTask.build(mSplitsSyncHelper, mSplitsStorageContainer.getSplitsStorage(), checkCacheExpiration,
mSplitClientConfig.cacheExpirationInSeconds(), mSplitsFilterQueryStringFromConfig, mEventsManager, mSplitsStorageContainer.getTelemetryStorage());
mSplitClientConfig.cacheExpirationInSeconds(), mSplitsFilterQueryStringFromConfig, mEventsManager, mSplitsStorageContainer.getTelemetryStorage(),
forceCacheExpiration);
}

@Override
Expand Down Expand Up @@ -229,4 +231,17 @@ private TelemetryTaskFactory initializeTelemetryTaskFactory(@NonNull SplitClient
invalidFlagSetCount);
return mTelemetryTaskFactory;
}

@Override
public SplitTask createExpireSplitsTask() {
return new SplitTask() {
@NonNull
@Override
public SplitTaskExecutionInfo execute() {
Logger.e("EXPIRING CACHE OF FLAGS");
mSplitsStorageContainer.getSplitsStorage().clear();
return SplitTaskExecutionInfo.success(SplitTaskType.GENERIC_TASK);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import java.util.List;
import java.util.Set;

import io.split.android.client.service.executor.SplitTask;

public interface MySegmentsTaskFactory {

MySegmentsSyncTask createMySegmentsSyncTask(boolean avoidCache);
Expand All @@ -12,4 +14,6 @@ public interface MySegmentsTaskFactory {
MySegmentsOverwriteTask createMySegmentsOverwriteTask(List<String> segments, Long changeNumber);

MySegmentsUpdateTask createMySegmentsUpdateTask(boolean add, Set<String> segmentNames, Long changeNumber);

SplitTask createExpireMySegmentsTask();
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
import java.util.List;
import java.util.Set;

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.telemetry.storage.TelemetryRuntimeProducer;
import io.split.android.client.utils.logger.Logger;

public class MySegmentsTaskFactoryImpl implements MySegmentsTaskFactory {

Expand Down Expand Up @@ -44,4 +48,17 @@ public MySegmentsOverwriteTask createMySegmentsOverwriteTask(List<String> segmen
public MySegmentsUpdateTask createMySegmentsUpdateTask(boolean add, Set<String> segmentNames, Long changeNumber) {
return new MySegmentsUpdateTask(mConfiguration.getStorage(), add, segmentNames, changeNumber, mConfiguration.getEventsManager(), mTelemetryRuntimeProducer, mConfiguration.getMySegmentsUpdateTaskConfig());
}

@Override
public SplitTask createExpireMySegmentsTask() {
return new SplitTask() {
@NonNull
@Override
public SplitTaskExecutionInfo execute() {
Logger.e("EXPIRING SEGMENTS CACHE");
mConfiguration.getStorage().clear();
return SplitTaskExecutionInfo.success(SplitTaskType.GENERIC_TASK);
}
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ private ProcessedSplitChange buildProcessedSplitChange(List<Split> featureFlags,
processStrategy.process(activeFeatureFlags, archivedFeatureFlags, featureFlag);
}

return new ProcessedSplitChange(activeFeatureFlags, archivedFeatureFlags, changeNumber, System.currentTimeMillis() / 100);
return new ProcessedSplitChange(activeFeatureFlags, archivedFeatureFlags, changeNumber, System.currentTimeMillis());
}

private FeatureFlagProcessStrategy getProcessStrategy(SplitFilter splitFilter) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,15 @@ private void updateStorage(boolean clearBeforeUpdate, SplitChange splitChange) {
}

public boolean cacheHasExpired(long storedChangeNumber, long updateTimestamp, long cacheExpirationInSeconds) {
long elapsed = now() - updateTimestamp;
return storedChangeNumber > -1
long elapsed = TimeUnit.MILLISECONDS.toSeconds(System.currentTimeMillis()) - TimeUnit.MILLISECONDS.toSeconds(updateTimestamp);
boolean expired = storedChangeNumber > -1
&& updateTimestamp > 0
&& (elapsed > cacheExpirationInSeconds);
}

private long now() {
return System.currentTimeMillis() / 1000;
if (expired) {
Logger.e("Cache is expired");
}
return expired;
}

private void logError(String message) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,6 @@ public interface FeatureFlagsSynchronizer {
void stopSynchronization();

void submitLoadingTask(SplitTaskExecutionListener listener);

void expireCache();
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public class FeatureFlagsSynchronizerImpl implements FeatureFlagsSynchronizer {
@Nullable
private final SplitTaskExecutionListener mSplitsSyncListener;
private final AtomicBoolean mIsSynchronizing = new AtomicBoolean(true);
private final AtomicBoolean mForceCacheExpiration = new AtomicBoolean(false);

public FeatureFlagsSynchronizerImpl(@NonNull SplitClientConfig splitClientConfig,
@NonNull SplitTaskExecutor taskExecutor,
Expand All @@ -62,6 +63,10 @@ public FeatureFlagsSynchronizerImpl(@NonNull SplitClientConfig splitClientConfig
public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) {
if (taskInfo.getStatus() == SplitTaskExecutionStatus.SUCCESS) {
pushManagerEventBroadcaster.pushMessage(new PushStatusEvent(PushStatusEvent.EventType.SUCCESSFUL_SYNC));

if (mForceCacheExpiration.compareAndSet(true, false)) {
mSplitsSyncRetryTimer.setTask(mSplitTaskFactory.createSplitsSyncTask(false, false), mSplitsSyncListener);
}
} else {
avoidRetries(taskInfo.getBoolValue(SplitTaskExecutionInfo.DO_NOT_RETRY));
}
Expand All @@ -77,8 +82,8 @@ public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) {
}
};
}

mSplitsSyncRetryTimer.setTask(mSplitTaskFactory.createSplitsSyncTask(true), mSplitsSyncListener);
mForceCacheExpiration.set(mSplitClientConfig.forceCacheExpiration());
mSplitsSyncRetryTimer.setTask(mSplitTaskFactory.createSplitsSyncTask(true, mForceCacheExpiration.get()), mSplitsSyncListener);
mLoadLocalSplitsListener = new LoadLocalDataListener(
splitEventsManager, SplitInternalEvent.SPLITS_LOADED_FROM_STORAGE);
}
Expand Down Expand Up @@ -134,13 +139,18 @@ public void submitLoadingTask(SplitTaskExecutionListener listener) {
listener);
}

@Override
public void expireCache() {
mTaskExecutor.submit(mSplitTaskFactory.createExpireSplitsTask(), null);
}

private void scheduleSplitsFetcherTask() {
if (mSplitsFetcherTaskId != null) {
mSplitsTaskExecutor.stopTask(mSplitsFetcherTaskId);
}

mSplitsFetcherTaskId = mSplitsTaskExecutor.schedule(
mSplitTaskFactory.createSplitsSyncTask(false),
mSplitTaskFactory.createSplitsSyncTask(false, false),
mSplitClientConfig.featuresRefreshRate(),
mSplitClientConfig.featuresRefreshRate(),
mSplitsSyncListener);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package io.split.android.client.service.synchronizer;

public interface LastUpdateTimestampProvider {

long getLastUpdateTimestamp();

void setLastUpdateTimestamp(long timestamp);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.split.android.client.service.synchronizer;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicLong;

import io.split.android.client.storage.db.GeneralInfoDao;
import io.split.android.client.storage.db.GeneralInfoEntity;
import io.split.android.client.utils.logger.Logger;

public class LastUpdateTimestampProviderImpl implements LastUpdateTimestampProvider {

private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
private final AtomicLong mTimestamp = new AtomicLong(-1);
private final Object mLock = new Object();
private final GeneralInfoDao mGeneralInfoDao;

public LastUpdateTimestampProviderImpl(GeneralInfoDao generalInfoDao) {
mGeneralInfoDao = generalInfoDao;
mExecutor.submit(() -> {
synchronized (mLock) {
Logger.e("Retrieving timestamp for initialization");
mTimestamp.set(mGeneralInfoDao.getByName(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP).getLongValue());
Logger.e("Initialized timestamp to " + mTimestamp.get());
}
});
}

@Override
public long getLastUpdateTimestamp() {
synchronized (mLock) {
return mTimestamp.get();
}
}

@Override
public void setLastUpdateTimestamp(long timestamp) {
mTimestamp.set(timestamp);
mExecutor.submit(() -> {
synchronized (mLock) {
mGeneralInfoDao.update(new GeneralInfoEntity(GeneralInfoEntity.SPLITS_UPDATE_TIMESTAMP, timestamp));
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package io.split.android.client.service.synchronizer;

import java.util.concurrent.TimeUnit;

import io.split.android.client.service.synchronizer.mysegments.MySegmentsSynchronizerRegistry;
import io.split.android.client.utils.logger.Logger;

class RolloutCacheManagerImpl implements RolloutCacheManager {

private final LastUpdateTimestampProvider mTimestampProvider;
private final FeatureFlagsSynchronizer mFeatureFlagsSynchronizer;
private final MySegmentsSynchronizerRegistry.Tasks mMySegmentsSynchronizerRegistry;
private final boolean mForceCacheExpiration;
private final long mCacheExpirationPeriod;

RolloutCacheManagerImpl(LastUpdateTimestampProvider timestampProvider,
FeatureFlagsSynchronizer featureFlagsSynchronizer,
MySegmentsSynchronizerRegistry.Tasks mySegmentsSynchronizerRegistry,
boolean forceCacheExpiration,
long cacheExpirationPeriod) {
mTimestampProvider = timestampProvider;
mFeatureFlagsSynchronizer = featureFlagsSynchronizer;
mMySegmentsSynchronizerRegistry = mySegmentsSynchronizerRegistry;
mForceCacheExpiration = forceCacheExpiration;
mCacheExpirationPeriod = cacheExpirationPeriod;
}

@Override
public void validateCache() {
long lastUpdateTimestamp = mTimestampProvider.getLastUpdateTimestamp();
long currentTime = System.currentTimeMillis() / 1000;
long elapsedTime = currentTime - lastUpdateTimestamp;

if (mForceCacheExpiration || TimeUnit.MILLISECONDS.toSeconds(elapsedTime) > mCacheExpirationPeriod) {
if (mForceCacheExpiration) {
Logger.v("Forcing cache expiration");
} else {
Logger.v("Cache expired due to time");
}
clearCache();
}
}

private void clearCache() {
mFeatureFlagsSynchronizer.expireCache();
mMySegmentsSynchronizerRegistry.expireCache();
}
}

interface RolloutCacheManager {

void validateCache();
}

Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,14 @@ public SyncManagerImpl(@NonNull SplitClientConfig splitClientConfig,

@Override
public void start() {
// mSynchronizer.validateCache();
mSynchronizer.loadAndSynchronizeSplits();
mSynchronizer.loadMySegmentsFromCache();
if (mSplitClientConfig.largeSegmentsEnabled()) mSynchronizer.loadMyLargeSegmentsFromCache();
mSynchronizer.loadAttributesFromCache();

mSynchronizer.synchronizeMySegments();
if (mSplitClientConfig.largeSegmentsEnabled()) {
mSynchronizer.loadMyLargeSegmentsFromCache();
mSynchronizer.synchronizeMyLargeSegments();
}
if (mSplitClientConfig.userConsent() == UserConsent.GRANTED) {
Expand Down
Loading

0 comments on commit c58cb4b

Please sign in to comment.