Skip to content

Commit 88b60a3

Browse files
authored
Merge pull request #2060 from OneSignal/improve/op_repo_restart_wait_on_every_enqueue
[Improvement] Further improve batching by restarting OperationRepo's delay on every enqueue call
2 parents 12e8359 + b6b60c6 commit 88b60a3

File tree

2 files changed

+46
-10
lines changed

2 files changed

+46
-10
lines changed

OneSignalSDK/onesignal/core/src/main/java/com/onesignal/core/internal/operations/impl/OperationRepo.kt

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -163,22 +163,32 @@ internal class OperationRepo(
163163
/**
164164
* Waits until a new operation is enqueued, then wait an additional
165165
* amount of time afterwards, so operations can be grouped/batched.
166+
* NOTE: Any operations that are enqueued while waiting here causes
167+
* the wait timer to restart over. This is intentional, we
168+
* are basically wait for "the dust to settle" / "the water
169+
* is calm" to ensure the app is done making updates.
170+
* FUTURE: Highly recommend not removing this "the dust to settle"
171+
* logic, as it ensures any app stuck in a loop can't
172+
* cause continuous network requests. If the delay is too
173+
* long for legitimate use-cases then allow tweaking the
174+
* opRepoExecutionInterval value or allow commitNow()
175+
* with a budget.
166176
*/
167177
private suspend fun waitForNewOperationAndExecutionInterval() {
168178
// 1. Wait for an operation to be enqueued
169179
var wakeMessage = waiter.waitForWake()
170180

171-
// 2. Wait at least the time defined in opRepoExecutionInterval
172-
// so operations can be grouped, unless one of them used
173-
// flush=true (AKA force)
174-
var lastTime = _time.currentTimeMillis
181+
// 2. Now wait opRepoExecutionInterval, restart the wait
182+
// time everytime something new is enqueued, to ensure
183+
// the dust has settled.
175184
var remainingTime = _configModelStore.model.opRepoExecutionInterval - wakeMessage.previousWaitedTime
176-
while (!wakeMessage.force && remainingTime > 0) {
177-
withTimeoutOrNull(remainingTime) {
178-
wakeMessage = waiter.waitForWake()
179-
}
180-
remainingTime -= _time.currentTimeMillis - lastTime
181-
lastTime = _time.currentTimeMillis
185+
while (!wakeMessage.force) {
186+
val waitedTheFullTime =
187+
withTimeoutOrNull(remainingTime) {
188+
wakeMessage = waiter.waitForWake()
189+
} == null
190+
if (waitedTheFullTime) break
191+
remainingTime = _configModelStore.model.opRepoExecutionInterval
182192
}
183193
}
184194

OneSignalSDK/onesignal/core/src/test/java/com/onesignal/core/internal/operations/OperationRepoTests.kt

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,32 @@ class OperationRepoTests : FunSpec({
538538
)
539539
}
540540
}
541+
542+
// We want to prevent a misbehaving app stuck in a loop from continuously
543+
// sending updates every opRepoExecutionInterval (5 seconds currently).
544+
// By waiting for the dust to settle we ensure the app is done making
545+
// updates.
546+
test("ensure each time enqueue is called it restarts the delay time") {
547+
// Given
548+
val mocks = Mocks()
549+
mocks.configModelStore.model.opRepoExecutionInterval = 100
550+
551+
// When
552+
mocks.operationRepo.start()
553+
launch {
554+
repeat(10) {
555+
mocks.operationRepo.enqueue(mockOperation(groupComparisonType = GroupComparisonType.ALTER))
556+
delay(50)
557+
}
558+
}
559+
val result =
560+
withTimeoutOrNull(500) {
561+
mocks.operationRepo.enqueueAndWait(mockOperation(groupComparisonType = GroupComparisonType.ALTER))
562+
}
563+
564+
// Then
565+
result shouldBe null
566+
}
541567
}) {
542568
companion object {
543569
private fun mockOperation(

0 commit comments

Comments
 (0)