Refactor FailureCounter

This commit is contained in:
sim 2024-11-29 12:34:30 +00:00
parent 08cd3d2f88
commit fa2d699b1e
8 changed files with 70 additions and 63 deletions

View File

@ -19,7 +19,7 @@ import org.unifiedpush.distributor.nextpush.account.AccountFactory
import org.unifiedpush.distributor.nextpush.distributor.Distributor
import org.unifiedpush.distributor.nextpush.distributor.Distributor.deleteApp
import org.unifiedpush.distributor.nextpush.distributor.Distributor.deleteDevice
import org.unifiedpush.distributor.nextpush.services.FailureHandler
import org.unifiedpush.distributor.nextpush.services.FailureCounter
import org.unifiedpush.distributor.nextpush.services.RestartWorker
import org.unifiedpush.distributor.nextpush.services.StartService
import org.unifiedpush.distributor.nextpush.utils.TAG
@ -64,7 +64,7 @@ class AppAction(private val type: Type, private val argv: Map<String, Any>? = nu
private fun restartService(context: Context) {
Log.d(TAG, "Restarting the Listener")
FailureHandler.clearFails()
FailureCounter.clearFails()
StartService.stopService {
RestartWorker.run(context, delay = 0)
}
@ -73,7 +73,7 @@ class AppAction(private val type: Type, private val argv: Map<String, Any>? = nu
private fun logout(context: Context) {
deleteDevice(context) {
StartService.stopService()
FailureHandler.clearFails()
FailureCounter.clearFails()
}
AccountFactory.logout(context)
AppStore(context).wipe()

View File

@ -21,7 +21,7 @@ import org.unifiedpush.distributor.nextpush.WakeLock
import org.unifiedpush.distributor.nextpush.api.response.SSEResponse
import org.unifiedpush.distributor.nextpush.distributor.Distributor.deleteAppFromSSE
import org.unifiedpush.distributor.nextpush.distributor.Distributor.sendMessage
import org.unifiedpush.distributor.nextpush.services.FailureHandler
import org.unifiedpush.distributor.nextpush.services.FailureCounter
import org.unifiedpush.distributor.nextpush.services.RestartWorker
import org.unifiedpush.distributor.nextpush.services.StartService
import org.unifiedpush.distributor.nextpush.utils.LowKeepAliveNotification
@ -37,7 +37,7 @@ class SSEListener(val context: Context) : EventSourceListener() {
override var timeout: Long? = AppCompanion.keepalive.get().toLong() + TIMEOUT_TOLERANCE
override fun onOpen(eventSource: EventSource, response: Response) {
FailureHandler.newEventSource(context, eventSource)
FailureCounter.newEventSource(context, eventSource)
StartingTimer.scheduleNewTimer(context, eventSource)
WakeLock.instance?.release()
try {
@ -62,7 +62,7 @@ class SSEListener(val context: Context) : EventSourceListener() {
"ping" -> {
AppCompanion.pinged.set(true)
FailureHandler.newPing(context)
FailureCounter.newPing(context)
}
"keepalive" -> {
@ -107,7 +107,7 @@ class SSEListener(val context: Context) : EventSourceListener() {
eventSource.cancel()
WakeLock.instance?.release()
if (!shouldRestart()) return
if (FailureHandler.newFail(context, eventSource)) {
if (FailureCounter.addFail(context, eventSource)) {
clearDebugVars()
RestartWorker.run(context, delay = 0)
}
@ -131,9 +131,9 @@ class SSEListener(val context: Context) : EventSourceListener() {
clearDebugVars()
return
}
if (FailureHandler.newFail(context, eventSource)) {
if (FailureCounter.addFail(context, eventSource)) {
clearDebugVars()
val delay = when (FailureHandler.nFails()) {
val delay = when (FailureCounter.nFails) {
1 -> 2 // 2sec
2 -> 5 // 5sec
3 -> 20 // 20sec
@ -186,7 +186,7 @@ class SSEListener(val context: Context) : EventSourceListener() {
!AppCompanion.booting.getAndSet(false)
) {
Timer().schedule(45_000L) { // 45 secs
if (FailureHandler.newFail(context, eventSource)) {
if (FailureCounter.addFail(context, eventSource)) {
StartService.stopService()
NoStartNotification(context).showSingle()
}

View File

@ -7,7 +7,7 @@ import android.content.IntentFilter
import android.os.BatteryManager
import android.util.Log
import org.unifiedpush.distributor.nextpush.AppCompanion
import org.unifiedpush.distributor.nextpush.services.FailureHandler
import org.unifiedpush.distributor.nextpush.services.FailureCounter
import org.unifiedpush.distributor.nextpush.services.RestartWorker
import org.unifiedpush.distributor.nextpush.services.StartService
import org.unifiedpush.distributor.nextpush.utils.TAG
@ -37,7 +37,7 @@ class BatteryCallback : BroadcastReceiver() {
}
private fun restartServer(context: Context) {
if (!FailureHandler.hasFailed()) {
if (FailureCounter.isRunningWithoutFailure) {
StartService.stopService {
RestartWorker.run(context, delay = 0)
}

View File

@ -10,7 +10,7 @@ import java.lang.Exception
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference
import org.unifiedpush.distributor.nextpush.AppCompanion
import org.unifiedpush.distributor.nextpush.services.FailureHandler
import org.unifiedpush.distributor.nextpush.services.FailureCounter
import org.unifiedpush.distributor.nextpush.services.RegistrationCountCache
import org.unifiedpush.distributor.nextpush.services.RestartWorker
import org.unifiedpush.distributor.nextpush.utils.TAG
@ -49,7 +49,7 @@ class NetworkCallback(val context: Context) : ConnectivityManager.NetworkCallbac
*/
private fun noInternet() {
AppCompanion.hasInternet.set(false)
FailureHandler.once()
FailureCounter.setFailOnce()
}
/**
@ -58,7 +58,7 @@ class NetworkCallback(val context: Context) : ConnectivityManager.NetworkCallbac
*/
private fun backOnline() {
// We first check if there is a fail registered, else a worker will run in 2 seconds
if (FailureHandler.hasFailed(orNeverStart = false) && RegistrationCountCache.oneOrMore(context)) {
if (FailureCounter.hasFailed && RegistrationCountCache.oneOrMore(context)) {
Log.d(TAG, "Restarting worker")
RestartWorker.run(context, delay = 0)
} // else, it retries in max 2sec

View File

@ -10,40 +10,49 @@ import org.unifiedpush.distributor.nextpush.utils.DisconnectedNotification
import org.unifiedpush.distributor.nextpush.utils.NoPingNotification
import org.unifiedpush.distributor.nextpush.utils.TAG
object FailureHandler {
object FailureCounter {
private val ttlFails = AtomicInteger(0)
private val nFails = AtomicInteger(0)
private val nFailsBeforePing = AtomicInteger(0)
private object Atomics {
val ttlFails = AtomicInteger(0)
val nFails = AtomicInteger(0)
val nFailsBeforePing = AtomicInteger(0)
// This is the last eventSource opened
private val eventSource: AtomicReference<EventSource?> = AtomicReference(null)
// This is the last eventSource opened
val eventSource: AtomicReference<EventSource?> = AtomicReference(null)
}
/**
* Is the [eventSource] the current one?
* Any eventSource is considered as the current if none is saved.
*/
private fun isCurrentEventSource(eventSource: EventSource?): Boolean {
return this.eventSource.get()?.let {
return Atomics.eventSource.get()?.let {
it == eventSource
} ?: true
}
/**
* When a new event source is set, we cancel the previous one
* and set [Atomics.nFails] to 0 and remove [DisconnectedNotification]
*/
fun newEventSource(context: Context, eventSource: EventSource) {
Log.d(TAG, "newEvent/Eventsource: $eventSource")
this.eventSource.getAndSet(eventSource)?.cancel()
nFails.set(0)
Atomics.eventSource.getAndSet(eventSource)?.cancel()
Atomics.nFails.set(0)
DisconnectedNotification(context).delete()
}
/**
* When a new ping is recorded, we set [Atomics.nFailsBeforePing] to 0
* and remove [NoPingNotification]
*/
fun newPing(context: Context) {
nFailsBeforePing.set(0)
Atomics.nFailsBeforePing.set(0)
NoPingNotification(context).delete()
}
fun nFails(): Int {
return nFails.get()
}
val nFails: Int
get() = Atomics.nFails.get()
/**
* Check the [eventSource] and increase the counter of failed events.
@ -58,23 +67,23 @@ object FailureHandler {
*
* @return `true` if the fail is from the current eventSource
*/
fun newFail(context: Context, eventSource: EventSource): Boolean {
fun addFail(context: Context, eventSource: EventSource): Boolean {
Log.d(TAG, "newFail/Eventsource: $eventSource")
// ignore fails from a possible old eventSource
// if we are already reconnected
return if (isCurrentEventSource(eventSource)) {
Log.d(TAG, "EventSource is known or null")
ttlFails.getAndIncrement()
if (nFails.incrementAndGet() == 2) {
Atomics.ttlFails.getAndIncrement()
if (Atomics.nFails.incrementAndGet() == 2) {
DisconnectedNotification(context).showSingle()
}
if (AppCompanion.started.get() && !AppCompanion.pinged.get()) {
Log.d(TAG, "The service has started and it has never received a ping.")
if (nFailsBeforePing.incrementAndGet() == 5) {
if (Atomics.nFailsBeforePing.incrementAndGet() == 5) {
NoPingNotification(context).showSingle()
}
}
this.eventSource.getAndSet(null)?.cancel()
Atomics.eventSource.getAndSet(null)?.cancel()
true
} else {
Log.d(TAG, "This is an old EventSource.")
@ -88,37 +97,35 @@ object FailureHandler {
*
* Used when there is no Internet access.
*/
fun once() {
Log.d(TAG, "once/Eventsource: $eventSource")
nFails.set(1)
this.eventSource.getAndSet(null)?.cancel()
fun setFailOnce() {
Log.d(TAG, "setFailOnce")
Atomics.nFails.set(1)
Atomics.eventSource.getAndSet(null)?.cancel()
}
fun clearFails() {
ttlFails.set(0)
nFails.set(0)
nFailsBeforePing.set(0)
this.eventSource.getAndSet(null)?.cancel()
Atomics.ttlFails.set(0)
Atomics.nFails.set(0)
Atomics.nFailsBeforePing.set(0)
Atomics.eventSource.getAndSet(null)?.cancel()
}
/**
* Is the service started and and has already failed once ?
*
* If [orNeverStart] is true (default) we consider a non-started service as a failed service
* Is an eventSource recorded, and there isn't any failure ?
*/
fun hasFailed(orNeverStart: Boolean = true): Boolean {
// nFails > 0 to be sure it is not actually restarting
return if (orNeverStart) {
eventSource.get() == null
} else {
false
} || nFails.get() > 0
}
val isRunningWithoutFailure: Boolean
get() = Atomics.eventSource.get() != null && !hasFailed
/**
* Has the service failed at least once ?
*/
val hasFailed: Boolean
get() = nFails > 0
fun getDebugInfo(): String {
return "ttlFails: ${ttlFails.get()}\n" +
return "ttlFails: ${Atomics.ttlFails.get()}\n" +
"nFails: $nFails\n" +
"nFailsBeforePing: $nFailsBeforePing\n" +
"eventSource null: ${eventSource.get() == null}"
"nFailsBeforePing: ${Atomics.nFailsBeforePing.get()}\n" +
"eventSource null: ${Atomics.eventSource.get() == null}"
}
}

View File

@ -38,12 +38,12 @@ class RestartWorker(ctx: Context, params: WorkerParameters) : Worker(ctx, params
if (currentDate.after(restartDate)) {
Log.d(TAG, "We should have received an event before ${restartDate.time}. Restarting")
/**
* If there is at least one failure, we do not update [FailureHandler]'s counter,
* If there is at least one failure, we do not update [FailureCounter]'s counter,
* it will be done by the next requests.
* Else we add one failure to be sure the service restarts.
*/
if (!FailureHandler.hasFailed()) {
FailureHandler.once()
if (FailureCounter.isRunningWithoutFailure) {
FailureCounter.setFailOnce()
}
StartService.startListener(applicationContext)
// We consider this run as the first event

View File

@ -74,7 +74,7 @@ class StartService : Service() {
private fun startService() {
// If the service is running and we don't have any fail
// In case somehow startService is called when everything is fine
if (isServiceStarted && !FailureHandler.hasFailed()) return
if (isServiceStarted && FailureCounter.isRunningWithoutFailure) return
getAccount(this) ?: run {
Log.d(TAG, "No account found")
return
@ -103,7 +103,7 @@ class StartService : Service() {
fun startListener(context: Context) {
val hasRegistration = RegistrationCountCache.oneOrMore(context)
if (isServiceStarted &&
!FailureHandler.hasFailed() &&
FailureCounter.isRunningWithoutFailure &&
// We return only if there is one or more registration
// because if there is no registration, this is a request to stop the SSE
hasRegistration
@ -117,7 +117,7 @@ class StartService : Service() {
}
Log.d(TAG, "Starting the Listener")
Log.d(TAG, "Service is started: $isServiceStarted")
Log.d(TAG, "nFails: ${FailureHandler.nFails()}")
Log.d(TAG, "nFails: ${FailureCounter.nFails}")
Log.d(TAG, "Has registration: $hasRegistration")
val serviceIntent = Intent(context, StartService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

View File

@ -2,7 +2,7 @@ package org.unifiedpush.distributor.nextpush.utils
import java.text.SimpleDateFormat
import org.unifiedpush.distributor.nextpush.AppCompanion
import org.unifiedpush.distributor.nextpush.services.FailureHandler
import org.unifiedpush.distributor.nextpush.services.FailureCounter
import org.unifiedpush.distributor.nextpush.services.StartService
fun getDebugInfo(): String {
@ -14,5 +14,5 @@ fun getDebugInfo(): String {
"Keepalive: ${AppCompanion.keepalive.get()}\n" +
"SSE started: ${AppCompanion.started}\n" +
"SSE pinged: ${AppCompanion.pinged}\n" +
FailureHandler.getDebugInfo()
FailureCounter.getDebugInfo()
}