Skip to content

Commit 8aeedd3

Browse files
authored
[FSSDK-9103] feat(ATS): fix retries for ODP segment and event API calls. (#454)
Fix retries for ODP api access - no retry on fetchQualifiedSegments failure - max 3 retries on ODP event dispatch
1 parent 59ad2e6 commit 8aeedd3

File tree

6 files changed

+37
-20
lines changed

6 files changed

+37
-20
lines changed

odp/src/androidTest/java/com/optimizely/ab/android/odp/ODPEventClientTest.kt

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ import org.junit.Test
2525
import org.junit.runner.RunWith
2626
import org.mockito.ArgumentCaptor
2727
import org.mockito.Matchers.any
28-
import org.mockito.Matchers.anyInt
2928
import org.mockito.Matchers.contains
3029
import org.mockito.Matchers.eq
3130
import org.mockito.Mockito.`when`
@@ -76,7 +75,7 @@ class ODPEventClientTest {
7675

7776
eventClient.dispatch(apiKey, apiEndpoint, payload)
7877

79-
verify(client).execute(captor.capture(), anyInt(), anyInt())
78+
verify(client).execute(captor.capture(), eq(2), eq(3))
8079
val received = captor.value.execute() as Boolean
8180

8281
assertFalse(received)
@@ -91,7 +90,7 @@ class ODPEventClientTest {
9190

9291
eventClient.dispatch(apiKey, apiEndpoint, payload)
9392

94-
verify(client).execute(captor.capture(), anyInt(), anyInt())
93+
verify(client).execute(captor.capture(), eq(2), eq(3))
9594
val received = captor.value.execute() as Boolean
9695

9796
assertFalse(received)
@@ -107,10 +106,10 @@ class ODPEventClientTest {
107106
apiEndpoint = "invalid-url"
108107
eventClient.dispatch(apiKey, apiEndpoint, payload)
109108

110-
verify(client).execute(captor.capture(), anyInt(), anyInt())
109+
verify(client).execute(captor.capture(), eq(2), eq(3))
111110
val received = captor.value.execute() as Boolean
112111

113112
assertFalse(received)
114-
verify(logger).error(contains("Error making request"), any())
113+
verify(logger).error(contains("Error making ODP event request"), any())
115114
}
116115
}

odp/src/androidTest/java/com/optimizely/ab/android/odp/ODPSegmentClientTest.kt

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@ import org.junit.Test
2424
import org.junit.runner.RunWith
2525
import org.mockito.ArgumentCaptor
2626
import org.mockito.Matchers.any
27-
import org.mockito.Matchers.anyInt
2827
import org.mockito.Matchers.contains
2928
import org.mockito.Matchers.eq
3029
import org.mockito.Mockito.`when`
@@ -59,7 +58,7 @@ class ODPSegmentClientTest {
5958

6059
segmentClient.fetchQualifiedSegments(apiKey, apiEndpoint, payload)
6160

62-
verify(client).execute(captor.capture(), eq(2), eq(2))
61+
verify(client).execute(captor.capture(), eq(0), eq(0))
6362
val received = captor.value.execute()
6463

6564
assert(received == response)
@@ -75,11 +74,11 @@ class ODPSegmentClientTest {
7574

7675
segmentClient.fetchQualifiedSegments(apiKey, apiEndpoint, payload)
7776

78-
verify(client).execute(captor.capture(), anyInt(), anyInt())
77+
verify(client).execute(captor.capture(), eq(0), eq(0))
7978
val received = captor.value.execute()
8079

8180
assertNull(received)
82-
verify(logger).error("Unexpected response from event endpoint, status: 400")
81+
verify(logger).error("Unexpected response from ODP segment endpoint, status: 400")
8382
verify(urlConnection).disconnect()
8483
}
8584

@@ -89,11 +88,11 @@ class ODPSegmentClientTest {
8988

9089
segmentClient.fetchQualifiedSegments(apiKey, apiEndpoint, payload)
9190

92-
verify(client).execute(captor.capture(), anyInt(), anyInt())
91+
verify(client).execute(captor.capture(), eq(0), eq(0))
9392
val received = captor.value.execute()
9493

9594
assertNull(received)
96-
verify(logger).error("Unexpected response from event endpoint, status: 500")
95+
verify(logger).error("Unexpected response from ODP segment endpoint, status: 500")
9796
verify(urlConnection).disconnect()
9897
}
9998

@@ -104,10 +103,10 @@ class ODPSegmentClientTest {
104103
apiEndpoint = "invalid-url"
105104
segmentClient.fetchQualifiedSegments(apiKey, apiEndpoint, payload)
106105

107-
verify(client).execute(captor.capture(), anyInt(), anyInt())
106+
verify(client).execute(captor.capture(), eq(0), eq(0))
108107
val received = captor.value.execute()
109108

110109
assertNull(received)
111-
verify(logger).error(contains("Error making request"), any())
110+
verify(logger).error(contains("Error making ODP segment request"), any())
112111
}
113112
}

odp/src/main/java/com/optimizely/ab/android/odp/ODPEventClient.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ open class ODPEventClient(private val client: Client, private val logger: Logger
6969
return@Request false
7070
}
7171
} catch (e: Exception) {
72-
logger.error("Error making request", e)
72+
logger.error("Error making ODP event request", e)
7373
return@Request false
7474
} finally {
7575
if (urlConnection != null) {
@@ -90,6 +90,10 @@ open class ODPEventClient(private val client: Client, private val logger: Logger
9090
var CONNECTION_TIMEOUT = 10 * 1000
9191
var READ_TIMEOUT = 60 * 1000
9292

93+
// OdpEventManager (java-sdk core) is supposed to handle retries on failures.
94+
// android-sdk returns success immediately for sendOdpEvent() from OdpEventManager and schedules it via WorkManager.
95+
// so retries on failure are supported here in OdpEventClient for android-sdk.
96+
9397
// the numerical base for the exponential backoff
9498
const val REQUEST_BACKOFF_TIMEOUT = 2
9599
// power the number of retries

odp/src/main/java/com/optimizely/ab/android/odp/ODPSegmentClient.kt

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,14 @@ open class ODPSegmentClient(private val client: Client, private val logger: Logg
5757
val status = urlConnection.responseCode
5858
if (status in 200..399) {
5959
val json = client.readStream(urlConnection)
60-
logger.debug("Successfully fetched segments: {}", json)
60+
logger.debug("Successfully fetched ODP segments: {}", json)
6161
return@Request json
6262
} else {
63-
logger.error("Unexpected response from event endpoint, status: $status")
63+
logger.error("Unexpected response from ODP segment endpoint, status: $status")
6464
return@Request null
6565
}
6666
} catch (e: Exception) {
67-
logger.error("Error making request", e)
67+
logger.error("Error making ODP segment request", e)
6868
return@Request null
6969
} finally {
7070
if (urlConnection != null) {
@@ -92,9 +92,12 @@ open class ODPSegmentClient(private val client: Client, private val logger: Logg
9292
var CONNECTION_TIMEOUT = 10 * 1000
9393
var READ_TIMEOUT = 60 * 1000
9494

95+
// No retries on fetchQualifiedSegments() errors.
96+
// We want to return failure immediately to callers.
97+
9598
// the numerical base for the exponential backoff
96-
const val REQUEST_BACKOFF_TIMEOUT = 2
97-
// power the number of retries (2 = retry once)
98-
const val REQUEST_RETRIES_POWER = 2
99+
const val REQUEST_BACKOFF_TIMEOUT = 0
100+
// power the number of retries
101+
const val REQUEST_RETRIES_POWER = 0
99102
}
100103
}

shared/src/androidTest/java/com/optimizely/ab/android/shared/ClientTest.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,4 +144,13 @@ public void testExpBackoffFailure() {
144144
assertTrue(timeouts.contains(8));
145145
assertTrue(timeouts.contains(16));
146146
}
147+
148+
@Test
149+
public void testExpBackoffFailure_noRetriesWhenBackoffSetToZero() {
150+
Client.Request request = mock(Client.Request.class);
151+
when(request.execute()).thenReturn(null);
152+
assertNull(client.execute(request, 0, 0));
153+
verify(logger, never()).info(eq("Request failed, waiting {} seconds to try again"), any(Integer.class));
154+
}
155+
147156
}

shared/src/main/java/com/optimizely/ab/android/shared/Client.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ public <T> T execute(Request<T> request, int timeout, int power) {
162162
}
163163

164164
if (response == null || response == Boolean.FALSE) {
165+
// retry is disabled when timeout set to 0
166+
if (timeout == 0) break;
167+
165168
try {
166169
logger.info("Request failed, waiting {} seconds to try again", timeout);
167170
Thread.sleep(TimeUnit.MILLISECONDS.convert(timeout, TimeUnit.SECONDS));

0 commit comments

Comments
 (0)