mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 06:55:41 +00:00
Do not automatically restart the sync in case it is Terminated.
Also simplify the computation of the backoff delay.
This commit is contained in:
committed by
Benoit Marty
parent
b28b2ceeed
commit
25505d6e8a
@@ -138,7 +138,7 @@ class SyncOrchestrator(
|
|||||||
SyncStateAction.StopSync
|
SyncStateAction.StopSync
|
||||||
} else if (syncState == SyncState.Idle && isAppActive && isNetworkAvailable) {
|
} else if (syncState == SyncState.Idle && isAppActive && isNetworkAvailable) {
|
||||||
SyncStateAction.StartSync
|
SyncStateAction.StartSync
|
||||||
} else if (syncState.isDead() && isAppActive && isNetworkAvailable) {
|
} else if (syncState == SyncState.Error && isAppActive && isNetworkAvailable) {
|
||||||
// The sync service has given up, for instance because the homeserver has expired our sliding sync
|
// The sync service has given up, for instance because the homeserver has expired our sliding sync
|
||||||
// session and answered 400 M_UNKNOWN_POS. Nothing else will ever restart it, so do it from here,
|
// session and answered 400 M_UNKNOWN_POS. Nothing else will ever restart it, so do it from here,
|
||||||
// else the application is stuck until its data is cleared.
|
// else the application is stuck until its data is cleared.
|
||||||
@@ -184,8 +184,18 @@ class SyncOrchestrator(
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
internal val FIRST_RESTART_DELAY = 1.seconds
|
internal val FIRST_RESTART_DELAY = 1.seconds
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exponential backoff, from [FIRST_RESTART_DELAY], doubling on every attempt, up to [FIRST_RESTART_DELAY * 32].
|
||||||
|
*/
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
internal val MAX_RESTART_DELAY = 30.seconds
|
internal val RESTART_BACKOFF_DELAYS = listOf(
|
||||||
|
FIRST_RESTART_DELAY,
|
||||||
|
FIRST_RESTART_DELAY * 2,
|
||||||
|
FIRST_RESTART_DELAY * 4,
|
||||||
|
FIRST_RESTART_DELAY * 8,
|
||||||
|
FIRST_RESTART_DELAY * 16,
|
||||||
|
FIRST_RESTART_DELAY * 32,
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Duration during which the sync service has to stay running before the restart backoff is reset.
|
* Duration during which the sync service has to stay running before the restart backoff is reset.
|
||||||
@@ -193,17 +203,8 @@ internal val MAX_RESTART_DELAY = 30.seconds
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
internal val RESTART_BACKOFF_RESET_DELAY = 30.seconds
|
internal val RESTART_BACKOFF_RESET_DELAY = 30.seconds
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the sync service has stopped and will not restart by itself.
|
|
||||||
*/
|
|
||||||
private fun SyncState.isDead() = this == SyncState.Error || this == SyncState.Terminated
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Exponential backoff, from [FIRST_RESTART_DELAY], doubling on every attempt, up to [MAX_RESTART_DELAY].
|
|
||||||
*/
|
|
||||||
private fun restartDelay(attempt: Int): Duration {
|
private fun restartDelay(attempt: Int): Duration {
|
||||||
val exponent = attempt.coerceIn(0, 5)
|
return RESTART_BACKOFF_DELAYS[attempt.coerceIn(0, RESTART_BACKOFF_DELAYS.lastIndex)]
|
||||||
return (FIRST_RESTART_DELAY * (1 shl exponent)).coerceAtMost(MAX_RESTART_DELAY)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum class SyncStateAction {
|
private enum class SyncStateAction {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
package io.element.android.appnav
|
package io.element.android.appnav
|
||||||
|
|
||||||
import io.element.android.appnav.session.FIRST_RESTART_DELAY
|
import io.element.android.appnav.session.FIRST_RESTART_DELAY
|
||||||
import io.element.android.appnav.session.MAX_RESTART_DELAY
|
import io.element.android.appnav.session.RESTART_BACKOFF_DELAYS
|
||||||
import io.element.android.appnav.session.RESTART_BACKOFF_RESET_DELAY
|
import io.element.android.appnav.session.RESTART_BACKOFF_RESET_DELAY
|
||||||
import io.element.android.appnav.session.SyncOrchestrator
|
import io.element.android.appnav.session.SyncOrchestrator
|
||||||
import io.element.android.features.networkmonitor.api.NetworkStatus
|
import io.element.android.features.networkmonitor.api.NetworkStatus
|
||||||
@@ -441,25 +441,6 @@ class SyncOrchestratorTest {
|
|||||||
startSyncRecorder.assertions().isCalledOnce()
|
startSyncRecorder.assertions().isCalledOnce()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `when the sync service is terminated, it will be restarted after a delay`() = runTest {
|
|
||||||
val startSyncRecorder = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
|
||||||
val syncService = FakeSyncService(initialSyncState = SyncState.Terminated).apply {
|
|
||||||
startSyncLambda = startSyncRecorder
|
|
||||||
}
|
|
||||||
val syncOrchestrator = createSyncOrchestrator(
|
|
||||||
matrixClient = FakeMatrixClient(syncService = syncService),
|
|
||||||
networkMonitor = FakeNetworkMonitor(initialStatus = NetworkStatus.Connected),
|
|
||||||
appForegroundStateService = FakeAppForegroundStateService(initialForegroundValue = true),
|
|
||||||
)
|
|
||||||
|
|
||||||
// We start observing
|
|
||||||
syncOrchestrator.observeStates()
|
|
||||||
|
|
||||||
advanceTimeBy(FIRST_RESTART_DELAY * 2)
|
|
||||||
startSyncRecorder.assertions().isCalledOnce()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `when the sync service keeps dying, the delay between the restarts increases`() = runTest {
|
fun `when the sync service keeps dying, the delay between the restarts increases`() = runTest {
|
||||||
val startSyncRecorder = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
val startSyncRecorder = lambdaRecorder<Result<Unit>> { Result.success(Unit) }
|
||||||
@@ -538,7 +519,7 @@ class SyncOrchestratorTest {
|
|||||||
// We start observing
|
// We start observing
|
||||||
syncOrchestrator.observeStates()
|
syncOrchestrator.observeStates()
|
||||||
|
|
||||||
advanceTimeBy(MAX_RESTART_DELAY * 2)
|
advanceTimeBy(RESTART_BACKOFF_DELAYS.last() * 2)
|
||||||
startSyncRecorder.assertions().isNeverCalled()
|
startSyncRecorder.assertions().isNeverCalled()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -557,7 +538,7 @@ class SyncOrchestratorTest {
|
|||||||
// We start observing
|
// We start observing
|
||||||
syncOrchestrator.observeStates()
|
syncOrchestrator.observeStates()
|
||||||
|
|
||||||
advanceTimeBy(MAX_RESTART_DELAY * 2)
|
advanceTimeBy(RESTART_BACKOFF_DELAYS.last() * 2)
|
||||||
startSyncRecorder.assertions().isNeverCalled()
|
startSyncRecorder.assertions().isNeverCalled()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user