Skip to content

Commit 5bd9741

Browse files
Fix log retry never ends (#224)
Wrong logic checking. postEvents enters infinite loop
1 parent 06ccd5d commit 5bd9741

File tree

3 files changed

+68
-9
lines changed

3 files changed

+68
-9
lines changed

src/main/kotlin/com/statsig/sdk/StatsigNetwork.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ private const val BACKOFF_MULTIPLIER: Int = 10
2525
private const val MS_IN_S: Long = 1000
2626
const val STATSIG_API_URL_BASE: String = "https://statsigapi.net/v1"
2727
private const val STATSIG_CDN_URL_BASE: String = "https://api.statsigcdn.com/v1"
28-
28+
const val LOG_EVENT_RETRY_COUNT = 5
2929
internal class StatsigNetwork(
3030
private val sdkKey: String,
3131
private val options: StatsigOptions,
@@ -227,7 +227,7 @@ internal class StatsigNetwork(
227227
}
228228

229229
suspend fun postLogs(events: List<StatsigEvent>, statsigMetadata: StatsigMetadata) {
230-
retryPostLogs(events, statsigMetadata, 5, 1)
230+
retryPostLogs(events, statsigMetadata, LOG_EVENT_RETRY_COUNT, 1)
231231
}
232232

233233
suspend fun retryPostLogs(
@@ -255,7 +255,7 @@ internal class StatsigNetwork(
255255
statsigHttpClient.newCall(request).await().use { response ->
256256
if (response.isSuccessful) {
257257
return@coroutineScope
258-
} else if (!retryCodes.contains(response.code) || retries == 0) {
258+
} else if (!retryCodes.contains(response.code) || currRetry == 0) {
259259
return@coroutineScope
260260
}
261261
}

src/test/java/com/statsig/sdk/NetworkTest.kt

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,26 @@ import io.mockk.coVerifySequence
44
import io.mockk.spyk
55
import io.mockk.unmockkAll
66
import kotlinx.coroutines.runBlocking
7+
import okhttp3.mockwebserver.Dispatcher
78
import okhttp3.mockwebserver.MockResponse
89
import okhttp3.mockwebserver.MockWebServer
10+
import okhttp3.mockwebserver.RecordedRequest
911
import org.junit.After
1012
import org.junit.Assert.assertEquals
13+
import org.junit.Before
1114
import org.junit.Test
1215

13-
class NetworkTest {
16+
internal class NetworkTest {
17+
var server = MockWebServer()
18+
var logEventCount = 0
19+
val metadata = StatsigMetadata()
20+
val options = StatsigOptions()
21+
val eb = ErrorBoundary("", options, metadata)
22+
@Before
23+
fun setup() {
24+
server = MockWebServer()
25+
logEventCount = 0
26+
}
1427

1528
@After
1629
fun tearDown() {
@@ -19,7 +32,6 @@ class NetworkTest {
1932

2033
@Test
2134
fun testRetry() = runBlocking {
22-
val server = MockWebServer()
2335
server.start()
2436

2537
val errResponse = MockResponse()
@@ -32,11 +44,8 @@ class NetworkTest {
3244
successResponse.setBody("{}")
3345
server.enqueue(successResponse)
3446

35-
val metadata = StatsigMetadata()
36-
val options = StatsigOptions()
3747
options.api = server.url("/v1").toString()
3848

39-
val eb = ErrorBoundary("", options, metadata)
4049
val net = spyk(StatsigNetwork("secret-123", options, metadata, eb, 1))
4150

4251
net.postLogs(listOf(StatsigEvent("TestEvent")), metadata)
@@ -49,4 +58,54 @@ class NetworkTest {
4958
net.retryPostLogs(any(), any(), any(), any())
5059
}
5160
}
61+
62+
@Test
63+
fun testLogEventRetry() = runBlocking {
64+
server.apply {
65+
dispatcher = object : Dispatcher() {
66+
@Throws(InterruptedException::class)
67+
override fun dispatch(request: RecordedRequest): MockResponse {
68+
if ("/v1/log_event" in request.path!!) {
69+
val logBody = request.body.readUtf8()
70+
if (request.getHeader("Content-Type") != "application/json; charset=utf-8") {
71+
throw Exception("No content type set!")
72+
}
73+
++logEventCount
74+
return MockResponse().setResponseCode(503)
75+
}
76+
return MockResponse().setResponseCode(400)
77+
}
78+
}
79+
}
80+
options.api = server.url("/v1").toString()
81+
val net = spyk(StatsigNetwork("secret-123", options, metadata, eb))
82+
net.postLogs(listOf(StatsigEvent("TestEvent")), metadata)
83+
println(logEventCount)
84+
assert(logEventCount === LOG_EVENT_RETRY_COUNT + 1)
85+
}
86+
87+
@Test
88+
fun testLogEventNoRetry() = runBlocking {
89+
server.apply {
90+
dispatcher = object : Dispatcher() {
91+
@Throws(InterruptedException::class)
92+
override fun dispatch(request: RecordedRequest): MockResponse {
93+
if ("/v1/log_event" in request.path!!) {
94+
val logBody = request.body.readUtf8()
95+
if (request.getHeader("Content-Type") != "application/json; charset=utf-8") {
96+
throw Exception("No content type set!")
97+
}
98+
++logEventCount
99+
return MockResponse().setResponseCode(400)
100+
}
101+
return MockResponse().setResponseCode(400)
102+
}
103+
}
104+
}
105+
options.api = server.url("/v1").toString()
106+
val net = spyk(StatsigNetwork("secret-123", options, metadata, eb))
107+
net.postLogs(listOf(StatsigEvent("TestEvent")), metadata)
108+
println(logEventCount)
109+
assert(logEventCount === 1)
110+
}
52111
}

src/test/java/com/statsig/sdk/StatsigE2ETest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ class StatsigE2ETest {
8080
throw Exception("No content type set!")
8181
}
8282
eventLogInputCompletable.complete(gson.fromJson(logBody, LogEventInput::class.java))
83-
return MockResponse().setResponseCode(200).setBody(downloadConfigSpecsResponse)
83+
return MockResponse().setResponseCode(200)
8484
}
8585
if ("/v1/get_id_lists" in request.path!!) {
8686
download_id_list_count++

0 commit comments

Comments
 (0)