Handle race condition during sync

This commit is contained in:
Jonas Lochmann 2021-05-17 02:00:00 +02:00
parent 70f9a3df24
commit da572df89a
No known key found for this signature in database
GPG key ID: 8B8C9AEE10FA5B36
3 changed files with 46 additions and 5 deletions

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 Jonas Lochmann
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -51,6 +51,9 @@ interface PendingSyncActionDao {
@Query("SELECT COUNT(*) FROM pending_sync_action")
fun countAllActionsLive(): LiveData<Long>
@Query("SELECT COUNT(*) FROM pending_sync_action")
fun countAllActions(): Long
@Query("SELECT * FROM pending_sync_action WHERE scheduled_for_upload = 0 ORDER BY sequence_number DESC")
fun getLatestUnscheduledActionSync(): PendingSyncAction?

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -36,6 +36,15 @@ object ApplyServerDataStatus {
fun applyServerDataStatusSync(status: ServerDataStatus, database: Database, platformIntegration: PlatformIntegration) {
database.runInTransaction {
// this would override some local data which was not sent yet
// so it's better to cancel in this case (or, more complicated,
// apply the delta which was not sent locally)
//
// locking the database during a sync is no option as the network
// connection could be really bad
if (database.pendingSyncAction().countAllActions() > 0)
throw PendingSyncActionException()
run {
// apply ful version until and message
@ -539,4 +548,6 @@ object ApplyServerDataStatus {
}
}
}
class PendingSyncActionException: RuntimeException()
}

View file

@ -1,5 +1,5 @@
/*
* TimeLimit Copyright <C> 2019 - 2020 Jonas Lochmann
* TimeLimit Copyright <C> 2019 - 2021 Jonas Lochmann
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@ -33,6 +33,7 @@ import io.timelimit.android.sync.network.api.UnauthorizedHttpError
import io.timelimit.android.ui.IsAppInForeground
import io.timelimit.android.work.SyncInBackgroundWorker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
@ -190,8 +191,7 @@ class SyncUtil (private val logic: AppLogic) {
}
try {
pushActions(server = server)
pullStatus(server = server)
doSyncRoundRetryOnNewActions(server)
logic.syncNotificationLogic.sync(forceUiSync = false)
} catch (ex: UnauthorizedHttpError) {
@ -214,6 +214,33 @@ class SyncUtil (private val logic: AppLogic) {
}
}
private suspend fun doSyncRoundRetryOnNewActions(server: ServerLogic.ServerConfig) {
// retry two times
for (i in 1..2) {
try {
doSyncRound(server = server)
return
} catch (ex: ApplyServerDataStatus.PendingSyncActionException) {
if (BuildConfig.DEBUG) {
Log.d(LOG_TAG, "got new actions while receiving the status => retry")
}
delay(250)
continue
}
}
// and do not catch at the last time
doSyncRound(server = server)
}
private suspend fun doSyncRound(server: ServerLogic.ServerConfig) {
pushActions(server = server)
pullStatus(server = server)
}
private suspend fun pushActions(server: ServerLogic.ServerConfig) {
upload.uploadActions(server)
}