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

Process changes according to sets #528

Merged
merged 6 commits into from
Sep 1, 2023
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
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);
}
gthea marked this conversation as resolved.
Show resolved Hide resolved
}
}
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