Skip to content

Commit

Permalink
Process changes according to sets (#528)
Browse files Browse the repository at this point in the history
  • Loading branch information
gthea authored Sep 1, 2023
2 parents b17c4a1 + c029304 commit 53c5f2a
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 46 deletions.
32 changes: 30 additions & 2 deletions src/main/java/io/split/android/client/SplitFactoryHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,17 @@

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

import java.io.File;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingDeque;

Expand Down Expand Up @@ -184,9 +189,9 @@ SplitApiFacade buildApiFacade(SplitClientConfig splitClientConfig,
}

WorkManagerWrapper buildWorkManagerWrapper(Context context, SplitClientConfig splitClientConfig,
String apiKey, String databaseName) {
String apiKey, String databaseName, Set<String> configuredFlagSets) {
return new WorkManagerWrapper(
WorkManager.getInstance(context), splitClientConfig, apiKey, databaseName);
WorkManager.getInstance(context), splitClientConfig, apiKey, databaseName, configuredFlagSets);

}

Expand Down Expand Up @@ -409,6 +414,29 @@ SplitUpdatesWorker getSplitUpdatesWorker(SplitClientConfig config,
return null;
}

Pair<Pair<List<SplitFilter>, String>, Set<String>> getFilterConfiguration(SyncConfig syncConfig) {
String splitsFilterQueryString = null;
List<SplitFilter> groupedFilters = new ArrayList<>();
Set<String> configuredFlagSets = new HashSet<>();

if (syncConfig != null) {
FilterBuilder filterBuilder = new FilterBuilder(syncConfig.getFilters());
groupedFilters = filterBuilder.getGroupedFilter();
splitsFilterQueryString = filterBuilder.buildQueryString();

if (!groupedFilters.isEmpty()) {
SplitFilter splitFilter = groupedFilters.get(0);

// In the case of BY_SET, all filters will be grouped into one with the {@link SplitFilter.Type#BY_SET} type
if (splitFilter.getType() == SplitFilter.Type.BY_SET) {
configuredFlagSets.addAll(splitFilter.getValues());
}
}
}

return new Pair<>(new Pair<>(groupedFilters, splitsFilterQueryString), configuredFlagSets);
}

private TelemetryStorage getTelemetryStorage(boolean shouldRecordTelemetry, TelemetryStorage telemetryStorage) {
if (telemetryStorage != null) {
return telemetryStorage;
Expand Down
19 changes: 8 additions & 11 deletions src/main/java/io/split/android/client/SplitFactoryImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import android.content.Context;

import androidx.annotation.NonNull;
import androidx.core.util.Pair;

import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import io.split.android.client.api.Key;
import io.split.android.client.common.CompressionUtilProvider;
Expand All @@ -31,7 +33,6 @@
import io.split.android.client.service.impressions.ImpressionManager;
import io.split.android.client.service.impressions.StrategyImpressionManager;
import io.split.android.client.service.sseclient.sseclient.StreamingComponents;
import io.split.android.client.service.synchronizer.FeatureFlagsSynchronizerImpl;
import io.split.android.client.service.synchronizer.SyncManager;
import io.split.android.client.service.synchronizer.Synchronizer;
import io.split.android.client.service.synchronizer.SynchronizerImpl;
Expand Down Expand Up @@ -170,24 +171,20 @@ public void taskExecuted(@NonNull SplitTaskExecutionInfo taskInfo) {
mStorageContainer = factoryHelper.buildStorageContainer(config.userConsent(),
splitDatabase, config.shouldRecordTelemetry(), splitCipher, telemetryStorage);

SyncConfig syncConfig = config.syncConfig();
String splitsFilterQueryString = null;
List<SplitFilter> filters = null;
if (syncConfig != null) {
FilterBuilder filterBuilder = new FilterBuilder(syncConfig.getFilters());
filters = filterBuilder.getGroupedFilter();
splitsFilterQueryString = filterBuilder.buildQueryString();
}
Pair<Pair<List<SplitFilter>, String>, Set<String>> filtersConfig = factoryHelper.getFilterConfiguration(config.syncConfig());
List<SplitFilter> filters = filtersConfig.first.first;
String splitsFilterQueryString = filtersConfig.first.second;
Set<String> configuredFlagSets = filtersConfig.second;

SplitApiFacade splitApiFacade = factoryHelper.buildApiFacade(
config, defaultHttpClient, splitsFilterQueryString);

SplitTaskFactory splitTaskFactory = new SplitTaskFactoryImpl(
config, splitApiFacade, mStorageContainer, splitsFilterQueryString, mEventsManagerCoordinator,
filters, testingConfig);
filters, configuredFlagSets, testingConfig);

cleanUpDabase(splitTaskExecutor, splitTaskFactory);
WorkManagerWrapper workManagerWrapper = factoryHelper.buildWorkManagerWrapper(context, config, apiToken, databaseName);
WorkManagerWrapper workManagerWrapper = factoryHelper.buildWorkManagerWrapper(context, config, apiToken, databaseName, configuredFlagSets);
SplitSingleThreadTaskExecutor splitSingleThreadTaskExecutor = new SplitSingleThreadTaskExecutor();

ImpressionManager impressionManager = new StrategyImpressionManager(factoryHelper.getImpressionStrategy(splitTaskExecutor, splitTaskFactory, mStorageContainer, config));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ public class ServiceConstants {
public static final String WORKER_PARAM_UNIQUE_KEYS_PER_PUSH = "unique_keys_per_push";
public static final String WORKER_PARAM_UNIQUE_KEYS_ESTIMATED_SIZE_IN_BYTES = "unique_keys_estimated_size_in_bytes";
public static final String WORKER_PARAM_ENCRYPTION_ENABLED = "encryptionEnabled";
public static final String WORKER_PARAM_CONFIGURED_SETS = "configuredSets";

public static final long LAST_SEEN_IMPRESSION_CACHE_SIZE = 500;
public static final int MY_SEGMENT_V2_DATA_SIZE = 1024 * 10;// bytes
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -52,12 +53,11 @@ public class SplitTaskFactoryImpl implements SplitTaskFactory {
private final SplitStorageContainer mSplitsStorageContainer;
private final SplitClientConfig mSplitClientConfig;
private final SplitsSyncHelper mSplitsSyncHelper;
private final String mSplitsFilterQueryString;
private final String mSplitsFilterQueryStringFromConfig;
private final ISplitEventsManager mEventsManager;
private final TelemetryTaskFactory mTelemetryTaskFactory;
private final SplitChangeProcessor mSplitChangeProcessor;
private final TelemetryRuntimeProducer mTelemetryRuntimeProducer;
@Nullable
private final List<SplitFilter> mFilters;

@SuppressLint("VisibleForTests")
Expand All @@ -67,14 +67,15 @@ public SplitTaskFactoryImpl(@NonNull SplitClientConfig splitClientConfig,
@Nullable String splitsFilterQueryString,
ISplitEventsManager eventsManager,
@Nullable List<SplitFilter> filters,
@NonNull Set<String> configuredFlagSets,
@Nullable TestingConfig testingConfig) {

mSplitClientConfig = checkNotNull(splitClientConfig);
mSplitApiFacade = checkNotNull(splitApiFacade);
mSplitsStorageContainer = checkNotNull(splitStorageContainer);
mSplitsFilterQueryString = splitsFilterQueryString;
mSplitsFilterQueryStringFromConfig = splitsFilterQueryString;
mEventsManager = eventsManager;
mSplitChangeProcessor = new SplitChangeProcessor();
mSplitChangeProcessor = new SplitChangeProcessor(configuredFlagSets);

TelemetryStorage telemetryStorage = mSplitsStorageContainer.getTelemetryStorage();
mTelemetryRuntimeProducer = telemetryStorage;
Expand All @@ -87,7 +88,7 @@ public SplitTaskFactoryImpl(@NonNull SplitClientConfig splitClientConfig,
} else {
mSplitsSyncHelper = new SplitsSyncHelper(mSplitApiFacade.getSplitFetcher(),
mSplitsStorageContainer.getSplitsStorage(),
new SplitChangeProcessor(),
mSplitChangeProcessor,
mTelemetryRuntimeProducer);
}

Expand All @@ -98,7 +99,7 @@ public SplitTaskFactoryImpl(@NonNull SplitClientConfig splitClientConfig,
mSplitsStorageContainer.getSplitsStorage(),
mSplitsStorageContainer.getMySegmentsStorageContainer());

mFilters = filters;
mFilters = (filters == null) ? new ArrayList<>() : filters;
}

@Override
Expand All @@ -119,13 +120,13 @@ public ImpressionsRecorderTask createImpressionsRecorderTask() {
mSplitClientConfig.impressionsPerPush(),
ServiceConstants.ESTIMATED_IMPRESSION_SIZE_IN_BYTES,
mSplitClientConfig.shouldRecordTelemetry()),
mSplitsStorageContainer.getTelemetryStorage());
mSplitsStorageContainer.getTelemetryStorage());
}

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

@Override
Expand All @@ -146,7 +147,7 @@ public SplitsUpdateTask createSplitsUpdateTask(long since) {
@Override
public FilterSplitsInCacheTask createFilterSplitsInCacheTask() {
return new FilterSplitsInCacheTask(mSplitsStorageContainer.getPersistentSplitsStorage(),
mFilters, mSplitsFilterQueryString);
mFilters, mSplitsFilterQueryStringFromConfig);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,14 @@ public class FilterSplitsInCacheTask implements SplitTask {
private final static String PREFIX_SEPARATOR = "__";
private final PersistentSplitsStorage mSplitsStorage;
private final List<SplitFilter> mSplitsFilter;
private final String mSplitsFilterQueryString;
private final String mSplitsFilterQueryStringFromConfig;

public FilterSplitsInCacheTask(@NonNull PersistentSplitsStorage splitsStorage,
@NonNull List<SplitFilter> splitsFilter,
@Nullable String splitsFilterQueryString) {
mSplitsStorage = checkNotNull(splitsStorage);
mSplitsFilter = checkNotNull(splitsFilter);
mSplitsFilterQueryString = splitsFilterQueryString;
mSplitsFilterQueryStringFromConfig = splitsFilterQueryString;
}

@Override
Expand Down Expand Up @@ -108,7 +108,7 @@ private String getPrefix(String splitName) {
}

private boolean queryStringHasChanged() {
return !sanitizeString(mSplitsStorage.getFilterQueryString()).equals(sanitizeString(mSplitsFilterQueryString));
return !sanitizeString(mSplitsStorage.getFilterQueryString()).equals(sanitizeString(mSplitsFilterQueryStringFromConfig));
}

private String sanitizeString(String string) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
package io.split.android.client.service.splits;

import static com.google.common.base.Preconditions.checkNotNull;

import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import io.split.android.client.dtos.Split;
import io.split.android.client.dtos.SplitChange;
import io.split.android.client.dtos.Status;
import io.split.android.client.storage.splits.ProcessedSplitChange;

public class SplitChangeProcessor {

private final Set<String> mConfiguredSets;

@VisibleForTesting
SplitChangeProcessor() {
this(Collections.emptySet());
}

public SplitChangeProcessor(@NonNull Set<String> configuredSets) {
mConfiguredSets = checkNotNull(configuredSets);
}

public ProcessedSplitChange process(SplitChange splitChange) {
if (splitChange == null || splitChange.splits == null) {
return new ProcessedSplitChange(new ArrayList<>(), new ArrayList<>(), -1L, 0);
Expand All @@ -20,25 +36,62 @@ public ProcessedSplitChange process(SplitChange splitChange) {
return buildProcessedSplitChange(splitChange.splits, splitChange.till);
}

public ProcessedSplitChange process(Split split, long changeNumber) {
return buildProcessedSplitChange(Collections.singletonList(split), changeNumber);
public ProcessedSplitChange process(Split featureFlag, long changeNumber) {
return buildProcessedSplitChange(Collections.singletonList(featureFlag), changeNumber);
}

@NonNull
private static ProcessedSplitChange buildProcessedSplitChange(List<Split> featureFlags, long changeNumber) {
List<Split> activeSplits = new ArrayList<>();
List<Split> archivedSplits = new ArrayList<>();
for (Split split : featureFlags) {
if (split.name == null) {
private ProcessedSplitChange buildProcessedSplitChange(List<Split> featureFlags, long changeNumber) {
List<Split> activeFeatureFlags = new ArrayList<>();
List<Split> archivedFeatureFlags = new ArrayList<>();
for (Split featureFlag : featureFlags) {
if (featureFlag.name == null) {
continue;
}
if (split.status == Status.ACTIVE) {
activeSplits.add(split);

if (mConfiguredSets.isEmpty()) {
processAccordingToStatus(activeFeatureFlags, archivedFeatureFlags, featureFlag);
} else {
archivedSplits.add(split);
processAccordingToSets(activeFeatureFlags, archivedFeatureFlags, featureFlag);
}
}

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

/**
* Process the feature flag according to its status
*/
private void processAccordingToStatus(List<Split> activeFeatureFlags, List<Split> archivedFeatureFlags, Split featureFlag) {
if (featureFlag.status == Status.ACTIVE) {
activeFeatureFlags.add(featureFlag);
} else {
archivedFeatureFlags.add(featureFlag);
}
}

/**
* Process the feature flag according to its sets
*/
private void processAccordingToSets(List<Split> activeFeatureFlags, List<Split> archivedFeatureFlags, Split featureFlag) {
if (featureFlag.sets == null || featureFlag.sets.isEmpty()) {
archivedFeatureFlags.add(featureFlag);
return;
}

boolean shouldArchive = true;
for (String set : featureFlag.sets) {
if (mConfiguredSets.contains(set)) {
// If the feature flag has at least one set that matches the configured sets,
// we process it according to its status
processAccordingToStatus(activeFeatureFlags, archivedFeatureFlags, featureFlag);
shouldArchive = false;
break;
}
}

if (shouldArchive) {
archivedFeatureFlags.add(featureFlag);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

public class SplitsSyncTask implements SplitTask {

private final String mSplitsFilterQueryString;
private final String mSplitsFilterQueryStringFromConfig;

private final SplitsStorage mSplitsStorage;
private final boolean mCheckCacheExpiration;
Expand Down Expand Up @@ -59,7 +59,7 @@ private SplitsSyncTask(@NonNull SplitsSyncHelper splitsSyncHelper,
mSplitsSyncHelper = checkNotNull(splitsSyncHelper);
mCacheExpirationInSeconds = cacheExpirationInSeconds;
mCheckCacheExpiration = checkCacheExpiration;
mSplitsFilterQueryString = splitsFilterQueryString;
mSplitsFilterQueryStringFromConfig = splitsFilterQueryString;
mEventsManager = eventsManager;
mChangeChecker = new SplitsChangeChecker();
mTelemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
Expand All @@ -77,7 +77,7 @@ public SplitTaskExecutionInfo execute() {
boolean splitsFilterHasChanged = splitsFilterHasChanged(mSplitsStorage.getSplitsFilterQueryString());

if (splitsFilterHasChanged) {
mSplitsStorage.updateSplitsFilterQueryString(mSplitsFilterQueryString);
mSplitsStorage.updateSplitsFilterQueryString(mSplitsFilterQueryStringFromConfig);
storedChangeNumber = -1;
}

Expand Down Expand Up @@ -107,7 +107,7 @@ private void notifyInternalEvent(long storedChangeNumber) {
}

private boolean splitsFilterHasChanged(String storedSplitsFilterQueryString) {
return !sanitizeString(mSplitsFilterQueryString).equals(sanitizeString(storedSplitsFilterQueryString));
return !sanitizeString(mSplitsFilterQueryStringFromConfig).equals(sanitizeString(storedSplitsFilterQueryString));
}

private String sanitizeString(String string) {
Expand Down
Loading

0 comments on commit 53c5f2a

Please sign in to comment.