Skip to content

Commit 5624dd6

Browse files
committed
WIP proxy handling
1 parent f83d6f8 commit 5624dd6

6 files changed

Lines changed: 160 additions & 37 deletions

File tree

src/main/java/io/split/android/client/service/executor/SplitTaskFactoryImpl.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,8 @@ public SplitTaskFactoryImpl(@NonNull SplitClientConfig splitClientConfig,
110110
mRuleBasedSegmentChangeProcessor,
111111
ruleBasedSegmentStorageProducer,
112112
mTelemetryRuntimeProducer,
113-
flagsSpecFromConfig);
113+
flagsSpecFromConfig,
114+
false);
114115
}
115116

116117
mFilters = (filters == null) ? new ArrayList<>() : new ArrayList<>(filters.values());

src/main/java/io/split/android/client/service/http/HttpStatus.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ public static boolean isNotRetryable(Integer code) {
5252
return isNotRetryable(fromCode(code));
5353
}
5454

55-
public static boolean isProxyOutdated(Integer code) {
56-
return fromCode(code) == HttpStatus.INTERNAL_PROXY_OUTDATED;
55+
public static boolean isProxyOutdated(HttpStatus status) {
56+
return status == HttpStatus.INTERNAL_PROXY_OUTDATED;
5757
}
5858
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package io.split.android.client.service.splits;
2+
3+
import java.util.concurrent.TimeUnit;
4+
import java.util.concurrent.atomic.AtomicLong;
5+
import java.util.concurrent.atomic.AtomicReference;
6+
7+
import io.split.android.client.utils.logger.Logger;
8+
9+
public class OutdatedSplitProxyHandler {
10+
11+
private final String mLatestSpec;
12+
private final String mPreviousSpec;
13+
private static final long PROXY_CHECK_INTERNAL_MILLIS = TimeUnit.HOURS.toMillis(1);
14+
private final AtomicReference<String> mCurrentSpec;
15+
private final AtomicLong mLastProxyCheckTimestamp = new AtomicLong(0); // TODO, persist
16+
private final boolean mForBackgroundSync;
17+
18+
OutdatedSplitProxyHandler(String flagSpec, String previousSpec, boolean forBackgroundSync) {
19+
mLatestSpec = flagSpec;
20+
mPreviousSpec = previousSpec;
21+
mCurrentSpec = new AtomicReference<>(flagSpec);
22+
mForBackgroundSync = forBackgroundSync;
23+
}
24+
25+
ProxyHandlingType handle() {
26+
if (mForBackgroundSync) {
27+
Logger.i("Background sync fetch; skipping proxy handling");
28+
return ProxyHandlingType.NONE;
29+
}
30+
31+
if (mCurrentSpec.get().equals(mLatestSpec)) {
32+
Logger.i("Switching to previous spec: " + mPreviousSpec);
33+
34+
mLastProxyCheckTimestamp.set(System.currentTimeMillis());
35+
36+
mCurrentSpec.set(mPreviousSpec);
37+
38+
return ProxyHandlingType.FALLBACK;
39+
} else if (mCurrentSpec.get().equals(mPreviousSpec)) {
40+
if (System.currentTimeMillis() - mLastProxyCheckTimestamp.get() > PROXY_CHECK_INTERNAL_MILLIS) {
41+
Logger.i("Attempting recovery with latest spec: " + mLatestSpec);
42+
return ProxyHandlingType.RECOVERY;
43+
}
44+
}
45+
46+
return ProxyHandlingType.NONE;
47+
}
48+
49+
String getCurrentSpec() {
50+
return mCurrentSpec.get();
51+
}
52+
53+
enum ProxyHandlingType {
54+
// no action
55+
NONE,
56+
// switch to previous spec
57+
FALLBACK,
58+
// attempt recovery
59+
RECOVERY,
60+
}
61+
}

src/main/java/io/split/android/client/service/splits/SplitsSyncHelper.java

Lines changed: 70 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,26 +46,27 @@ public class SplitsSyncHelper {
4646
private final RuleBasedSegmentStorageProducer mRuleBasedSegmentStorage;
4747
private final TelemetryRuntimeProducer mTelemetryRuntimeProducer;
4848
private final BackoffCounter mBackoffCounter;
49-
private final String mFlagsSpec;
49+
private final OutdatedSplitProxyHandler mOutdatedSplitProxyHandler;
5050

5151
public SplitsSyncHelper(@NonNull HttpFetcher<TargetingRulesChange> splitFetcher,
5252
@NonNull SplitsStorage splitsStorage,
5353
@NonNull SplitChangeProcessor splitChangeProcessor,
5454
@NonNull RuleBasedSegmentChangeProcessor ruleBasedSegmentChangeProcessor,
5555
@NonNull RuleBasedSegmentStorageProducer ruleBasedSegmentStorage,
5656
@NonNull TelemetryRuntimeProducer telemetryRuntimeProducer,
57-
@Nullable String flagsSpec) {
57+
@Nullable String flagsSpec,
58+
boolean forBackgroundSync) {
5859
this(splitFetcher,
5960
splitsStorage,
6061
splitChangeProcessor,
6162
ruleBasedSegmentChangeProcessor,
6263
ruleBasedSegmentStorage,
6364
telemetryRuntimeProducer,
6465
new ReconnectBackoffCounter(1, ON_DEMAND_FETCH_BACKOFF_MAX_WAIT),
65-
flagsSpec);
66+
flagsSpec,
67+
forBackgroundSync);
6668
}
6769

68-
@VisibleForTesting
6970
public SplitsSyncHelper(@NonNull HttpFetcher<TargetingRulesChange> splitFetcher,
7071
@NonNull SplitsStorage splitsStorage,
7172
@NonNull SplitChangeProcessor splitChangeProcessor,
@@ -74,14 +75,36 @@ public SplitsSyncHelper(@NonNull HttpFetcher<TargetingRulesChange> splitFetcher,
7475
@NonNull TelemetryRuntimeProducer telemetryRuntimeProducer,
7576
@NonNull BackoffCounter backoffCounter,
7677
@Nullable String flagsSpec) {
78+
this(splitFetcher,
79+
splitsStorage,
80+
splitChangeProcessor,
81+
ruleBasedSegmentChangeProcessor,
82+
ruleBasedSegmentStorage,
83+
telemetryRuntimeProducer,
84+
backoffCounter,
85+
flagsSpec,
86+
false);
87+
}
88+
89+
@VisibleForTesting
90+
public SplitsSyncHelper(@NonNull HttpFetcher<TargetingRulesChange> splitFetcher,
91+
@NonNull SplitsStorage splitsStorage,
92+
@NonNull SplitChangeProcessor splitChangeProcessor,
93+
@NonNull RuleBasedSegmentChangeProcessor ruleBasedSegmentChangeProcessor,
94+
@NonNull RuleBasedSegmentStorageProducer ruleBasedSegmentStorage,
95+
@NonNull TelemetryRuntimeProducer telemetryRuntimeProducer,
96+
@NonNull BackoffCounter backoffCounter,
97+
@Nullable String flagsSpec,
98+
boolean forBackgroundSync) {
7799
mSplitFetcher = checkNotNull(splitFetcher);
78100
mSplitsStorage = checkNotNull(splitsStorage);
79101
mSplitChangeProcessor = checkNotNull(splitChangeProcessor);
80102
mRuleBasedSegmentChangeProcessor = checkNotNull(ruleBasedSegmentChangeProcessor);
81103
mRuleBasedSegmentStorage = checkNotNull(ruleBasedSegmentStorage);
82104
mTelemetryRuntimeProducer = checkNotNull(telemetryRuntimeProducer);
83105
mBackoffCounter = checkNotNull(backoffCounter);
84-
mFlagsSpec = flagsSpec;
106+
String mPreviousSpec = "1.2";
107+
mOutdatedSplitProxyHandler = new OutdatedSplitProxyHandler(flagsSpec, mPreviousSpec, forBackgroundSync);
85108
}
86109

87110
public SplitTaskExecutionInfo sync(SinceChangeNumbers till, int onDemandFetchBackoffMaxRetries) {
@@ -113,6 +136,14 @@ private SplitTaskExecutionInfo sync(SinceChangeNumbers till, boolean clearBefore
113136
Collections.singletonMap(SplitTaskExecutionInfo.DO_NOT_RETRY, true));
114137
}
115138

139+
if (HttpStatus.isProxyOutdated(httpStatus)) {
140+
try {
141+
return handleOutdatedProxy(till, avoidCache, resetChangeNumber, onDemandFetchBackoffMaxRetries);
142+
} catch (Exception e1) {
143+
logError("Unexpected while handling outdated proxy" + e1.getLocalizedMessage());
144+
}
145+
}
146+
116147
return SplitTaskExecutionInfo.error(SplitTaskType.SPLITS_SYNC);
117148
} catch (Exception e) {
118149
logError("Unexpected while fetching feature flags" + e.getLocalizedMessage());
@@ -123,6 +154,25 @@ private SplitTaskExecutionInfo sync(SinceChangeNumbers till, boolean clearBefore
123154
return SplitTaskExecutionInfo.success(SplitTaskType.SPLITS_SYNC);
124155
}
125156

157+
private SplitTaskExecutionInfo handleOutdatedProxy(SinceChangeNumbers till, boolean avoidCache, boolean resetChangeNumber, int onDemandFetchBackoffMaxRetries) throws Exception {
158+
OutdatedSplitProxyHandler.ProxyHandlingType handle = mOutdatedSplitProxyHandler.handle();
159+
switch (handle) {
160+
case FALLBACK: {
161+
long flagsSince = till.getFlagsSince();
162+
SinceChangeNumbers newTill = new SinceChangeNumbers(flagsSince, null);
163+
attemptSplitSync(newTill, false, false, CdnByPassType.NONE, false, onDemandFetchBackoffMaxRetries);
164+
break;
165+
}
166+
case RECOVERY: {
167+
SinceChangeNumbers newTill = new SinceChangeNumbers(-1, -1L);
168+
attemptSplitSync(newTill, true, false, CdnByPassType.NONE, true, onDemandFetchBackoffMaxRetries);
169+
break;
170+
}
171+
}
172+
173+
return SplitTaskExecutionInfo.success(SplitTaskType.SPLITS_SYNC);
174+
}
175+
126176
/**
127177
* @param targetChangeNumber target changeNumber
128178
* @param clearBeforeUpdate whether to clear splits storage before updating it
@@ -140,7 +190,8 @@ private CdnByPassType attemptSplitSync(SinceChangeNumbers targetChangeNumber, bo
140190
SinceChangeNumbers retrievedChangeNumber = fetchUntil(targetChangeNumber, clearBeforeUpdate, avoidCache, withCdnBypass, resetChangeNumber);
141191
resetChangeNumber = false;
142192

143-
if (targetChangeNumber.getFlagsSince() <= retrievedChangeNumber.getFlagsSince() && targetChangeNumber.getRbsSince() <= retrievedChangeNumber.getRbsSince()) {
193+
if (targetChangeNumber.getFlagsSince() <= retrievedChangeNumber.getFlagsSince() &&
194+
targetChangeNumber.getRbsSince() != null && retrievedChangeNumber.getRbsSince() != null && targetChangeNumber.getRbsSince() <= retrievedChangeNumber.getRbsSince()) {
144195
return CdnByPassType.NONE;
145196
}
146197

@@ -170,7 +221,7 @@ private SinceChangeNumbers fetchUntil(SinceChangeNumbers till, boolean clearBefo
170221
long changeNumber = (resetChangeNumber) ? -1 : mSplitsStorage.getTill();
171222
long rbsChangeNumber = (resetChangeNumber) ? -1 : mRuleBasedSegmentStorage.getChangeNumber();
172223
resetChangeNumber = false;
173-
if (newTill.getFlagsSince() < changeNumber && newTill.getRbsSince() < rbsChangeNumber) {
224+
if ((newTill.getFlagsSince() < changeNumber) && ((newTill.getRbsSince() == null) || (newTill.getRbsSince() < rbsChangeNumber))) {
174225
return new SinceChangeNumbers(changeNumber, rbsChangeNumber);
175226
}
176227

@@ -189,11 +240,14 @@ private SinceChangeNumbers fetchUntil(SinceChangeNumbers till, boolean clearBefo
189240

190241
private TargetingRulesChange fetchSplits(SinceChangeNumbers till, boolean avoidCache, CdnByPassType cdnByPassType) throws HttpFetcherException {
191242
Map<String, Object> params = new LinkedHashMap<>();
192-
if (mFlagsSpec != null && !mFlagsSpec.trim().isEmpty()) {
193-
params.put(FLAGS_SPEC_PARAM, mFlagsSpec);
243+
String flagsSpec = mOutdatedSplitProxyHandler.getCurrentSpec();
244+
if (flagsSpec != null && !flagsSpec.trim().isEmpty()) {
245+
params.put(FLAGS_SPEC_PARAM, flagsSpec);
194246
}
195247
params.put(SINCE_PARAM, till.getFlagsSince());
196-
params.put(RBS_SINCE_PARAM, till.getRbsSince());
248+
if (till.getRbsSince() != null) {
249+
params.put(RBS_SINCE_PARAM, till.getRbsSince());
250+
}
197251

198252
if (cdnByPassType == CdnByPassType.RBS) {
199253
params.put(TILL_PARAM, till.getRbsSince());
@@ -231,9 +285,10 @@ private void logError(String message) {
231285

232286
public static class SinceChangeNumbers {
233287
private final long mFlagsSince;
234-
private final long mRbsSince;
288+
@Nullable
289+
private final Long mRbsSince;
235290

236-
public SinceChangeNumbers(long flagsSince, long rbsSince) {
291+
public SinceChangeNumbers(long flagsSince, @Nullable Long rbsSince) {
237292
mFlagsSince = flagsSince;
238293
mRbsSince = rbsSince;
239294
}
@@ -242,15 +297,16 @@ public long getFlagsSince() {
242297
return mFlagsSince;
243298
}
244299

245-
public long getRbsSince() {
300+
@Nullable
301+
public Long getRbsSince() {
246302
return mRbsSince;
247303
}
248304

249305
@Override
250306
public boolean equals(@Nullable Object obj) {
251307
return obj instanceof SinceChangeNumbers &&
252308
mFlagsSince == ((SinceChangeNumbers) obj).mFlagsSince &&
253-
mRbsSince == ((SinceChangeNumbers) obj).mRbsSince;
309+
(mRbsSince == null && ((SinceChangeNumbers) obj).mRbsSince == null);
254310
}
255311

256312
@NonNull

src/main/java/io/split/android/client/service/workmanager/splits/SyncHelperProvider.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ SplitsSyncHelper provideSplitsSyncHelper(HttpFetcher<TargetingRulesChange> split
2424
ruleBasedSegmentChangeProcessor,
2525
ruleBasedSegmentStorage,
2626
telemetryStorage,
27-
mFlagsSpec);
27+
mFlagsSpec,
28+
true);
2829
}
2930
}

0 commit comments

Comments
 (0)