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

View File

@ -7,7 +7,7 @@ import android.content.IntentFilter
import android.os.BatteryManager import android.os.BatteryManager
import android.util.Log import android.util.Log
import org.unifiedpush.distributor.nextpush.AppCompanion 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.RestartWorker
import org.unifiedpush.distributor.nextpush.services.StartService import org.unifiedpush.distributor.nextpush.services.StartService
import org.unifiedpush.distributor.nextpush.utils.TAG import org.unifiedpush.distributor.nextpush.utils.TAG
@ -37,7 +37,7 @@ class BatteryCallback : BroadcastReceiver() {
} }
private fun restartServer(context: Context) { private fun restartServer(context: Context) {
if (!FailureHandler.hasFailed()) { if (FailureCounter.isRunningWithoutFailure) {
StartService.stopService { StartService.stopService {
RestartWorker.run(context, delay = 0) 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.AtomicBoolean
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
import org.unifiedpush.distributor.nextpush.AppCompanion 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.RegistrationCountCache
import org.unifiedpush.distributor.nextpush.services.RestartWorker import org.unifiedpush.distributor.nextpush.services.RestartWorker
import org.unifiedpush.distributor.nextpush.utils.TAG import org.unifiedpush.distributor.nextpush.utils.TAG
@ -49,7 +49,7 @@ class NetworkCallback(val context: Context) : ConnectivityManager.NetworkCallbac
*/ */
private fun noInternet() { private fun noInternet() {
AppCompanion.hasInternet.set(false) AppCompanion.hasInternet.set(false)
FailureHandler.once() FailureCounter.setFailOnce()
} }
/** /**
@ -58,7 +58,7 @@ class NetworkCallback(val context: Context) : ConnectivityManager.NetworkCallbac
*/ */
private fun backOnline() { private fun backOnline() {
// We first check if there is a fail registered, else a worker will run in 2 seconds // 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") Log.d(TAG, "Restarting worker")
RestartWorker.run(context, delay = 0) RestartWorker.run(context, delay = 0)
} // else, it retries in max 2sec } // 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.NoPingNotification
import org.unifiedpush.distributor.nextpush.utils.TAG import org.unifiedpush.distributor.nextpush.utils.TAG
object FailureHandler { object FailureCounter {
private val ttlFails = AtomicInteger(0) private object Atomics {
private val nFails = AtomicInteger(0) val ttlFails = AtomicInteger(0)
private val nFailsBeforePing = AtomicInteger(0) val nFails = AtomicInteger(0)
val nFailsBeforePing = AtomicInteger(0)
// This is the last eventSource opened // This is the last eventSource opened
private val eventSource: AtomicReference<EventSource?> = AtomicReference(null) val eventSource: AtomicReference<EventSource?> = AtomicReference(null)
}
/** /**
* Is the [eventSource] the current one? * Is the [eventSource] the current one?
* Any eventSource is considered as the current if none is saved. * Any eventSource is considered as the current if none is saved.
*/ */
private fun isCurrentEventSource(eventSource: EventSource?): Boolean { private fun isCurrentEventSource(eventSource: EventSource?): Boolean {
return this.eventSource.get()?.let { return Atomics.eventSource.get()?.let {
it == eventSource it == eventSource
} ?: true } ?: 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) { fun newEventSource(context: Context, eventSource: EventSource) {
Log.d(TAG, "newEvent/Eventsource: $eventSource") Log.d(TAG, "newEvent/Eventsource: $eventSource")
this.eventSource.getAndSet(eventSource)?.cancel() Atomics.eventSource.getAndSet(eventSource)?.cancel()
nFails.set(0) Atomics.nFails.set(0)
DisconnectedNotification(context).delete() DisconnectedNotification(context).delete()
} }
/**
* When a new ping is recorded, we set [Atomics.nFailsBeforePing] to 0
* and remove [NoPingNotification]
*/
fun newPing(context: Context) { fun newPing(context: Context) {
nFailsBeforePing.set(0) Atomics.nFailsBeforePing.set(0)
NoPingNotification(context).delete() NoPingNotification(context).delete()
} }
fun nFails(): Int { val nFails: Int
return nFails.get() get() = Atomics.nFails.get()
}
/** /**
* Check the [eventSource] and increase the counter of failed events. * 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 * @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") Log.d(TAG, "newFail/Eventsource: $eventSource")
// ignore fails from a possible old eventSource // ignore fails from a possible old eventSource
// if we are already reconnected // if we are already reconnected
return if (isCurrentEventSource(eventSource)) { return if (isCurrentEventSource(eventSource)) {
Log.d(TAG, "EventSource is known or null") Log.d(TAG, "EventSource is known or null")
ttlFails.getAndIncrement() Atomics.ttlFails.getAndIncrement()
if (nFails.incrementAndGet() == 2) { if (Atomics.nFails.incrementAndGet() == 2) {
DisconnectedNotification(context).showSingle() DisconnectedNotification(context).showSingle()
} }
if (AppCompanion.started.get() && !AppCompanion.pinged.get()) { if (AppCompanion.started.get() && !AppCompanion.pinged.get()) {
Log.d(TAG, "The service has started and it has never received a ping.") 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() NoPingNotification(context).showSingle()
} }
} }
this.eventSource.getAndSet(null)?.cancel() Atomics.eventSource.getAndSet(null)?.cancel()
true true
} else { } else {
Log.d(TAG, "This is an old EventSource.") Log.d(TAG, "This is an old EventSource.")
@ -88,37 +97,35 @@ object FailureHandler {
* *
* Used when there is no Internet access. * Used when there is no Internet access.
*/ */
fun once() { fun setFailOnce() {
Log.d(TAG, "once/Eventsource: $eventSource") Log.d(TAG, "setFailOnce")
nFails.set(1) Atomics.nFails.set(1)
this.eventSource.getAndSet(null)?.cancel() Atomics.eventSource.getAndSet(null)?.cancel()
} }
fun clearFails() { fun clearFails() {
ttlFails.set(0) Atomics.ttlFails.set(0)
nFails.set(0) Atomics.nFails.set(0)
nFailsBeforePing.set(0) Atomics.nFailsBeforePing.set(0)
this.eventSource.getAndSet(null)?.cancel() Atomics.eventSource.getAndSet(null)?.cancel()
} }
/** /**
* Is the service started and and has already failed once ? * Is an eventSource recorded, and there isn't any failure ?
*
* If [orNeverStart] is true (default) we consider a non-started service as a failed service
*/ */
fun hasFailed(orNeverStart: Boolean = true): Boolean { val isRunningWithoutFailure: Boolean
// nFails > 0 to be sure it is not actually restarting get() = Atomics.eventSource.get() != null && !hasFailed
return if (orNeverStart) {
eventSource.get() == null /**
} else { * Has the service failed at least once ?
false */
} || nFails.get() > 0 val hasFailed: Boolean
} get() = nFails > 0
fun getDebugInfo(): String { fun getDebugInfo(): String {
return "ttlFails: ${ttlFails.get()}\n" + return "ttlFails: ${Atomics.ttlFails.get()}\n" +
"nFails: $nFails\n" + "nFails: $nFails\n" +
"nFailsBeforePing: $nFailsBeforePing\n" + "nFailsBeforePing: ${Atomics.nFailsBeforePing.get()}\n" +
"eventSource null: ${eventSource.get() == null}" "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)) { if (currentDate.after(restartDate)) {
Log.d(TAG, "We should have received an event before ${restartDate.time}. Restarting") 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. * it will be done by the next requests.
* Else we add one failure to be sure the service restarts. * Else we add one failure to be sure the service restarts.
*/ */
if (!FailureHandler.hasFailed()) { if (FailureCounter.isRunningWithoutFailure) {
FailureHandler.once() FailureCounter.setFailOnce()
} }
StartService.startListener(applicationContext) StartService.startListener(applicationContext)
// We consider this run as the first event // We consider this run as the first event

View File

@ -74,7 +74,7 @@ class StartService : Service() {
private fun startService() { private fun startService() {
// If the service is running and we don't have any fail // If the service is running and we don't have any fail
// In case somehow startService is called when everything is fine // In case somehow startService is called when everything is fine
if (isServiceStarted && !FailureHandler.hasFailed()) return if (isServiceStarted && FailureCounter.isRunningWithoutFailure) return
getAccount(this) ?: run { getAccount(this) ?: run {
Log.d(TAG, "No account found") Log.d(TAG, "No account found")
return return
@ -103,7 +103,7 @@ class StartService : Service() {
fun startListener(context: Context) { fun startListener(context: Context) {
val hasRegistration = RegistrationCountCache.oneOrMore(context) val hasRegistration = RegistrationCountCache.oneOrMore(context)
if (isServiceStarted && if (isServiceStarted &&
!FailureHandler.hasFailed() && FailureCounter.isRunningWithoutFailure &&
// We return only if there is one or more registration // We return only if there is one or more registration
// because if there is no registration, this is a request to stop the SSE // because if there is no registration, this is a request to stop the SSE
hasRegistration hasRegistration
@ -117,7 +117,7 @@ class StartService : Service() {
} }
Log.d(TAG, "Starting the Listener") Log.d(TAG, "Starting the Listener")
Log.d(TAG, "Service is started: $isServiceStarted") 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") Log.d(TAG, "Has registration: $hasRegistration")
val serviceIntent = Intent(context, StartService::class.java) val serviceIntent = Intent(context, StartService::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 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 java.text.SimpleDateFormat
import org.unifiedpush.distributor.nextpush.AppCompanion 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 import org.unifiedpush.distributor.nextpush.services.StartService
fun getDebugInfo(): String { fun getDebugInfo(): String {
@ -14,5 +14,5 @@ fun getDebugInfo(): String {
"Keepalive: ${AppCompanion.keepalive.get()}\n" + "Keepalive: ${AppCompanion.keepalive.get()}\n" +
"SSE started: ${AppCompanion.started}\n" + "SSE started: ${AppCompanion.started}\n" +
"SSE pinged: ${AppCompanion.pinged}\n" + "SSE pinged: ${AppCompanion.pinged}\n" +
FailureHandler.getDebugInfo() FailureCounter.getDebugInfo()
} }