All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pubnub.internal.retry.RetryableCallback.kt Maven / Gradle / Ivy

Go to download

PubNub is a cross-platform client-to-client (1:1 and 1:many) push service in the cloud, capable of broadcasting real-time messages to millions of web and mobile clients simultaneously, in less than a quarter second!

There is a newer version: 10.3.4
Show newest version
package com.pubnub.internal.retry

import com.pubnub.api.retry.RetryConfiguration
import com.pubnub.api.retry.RetryableEndpointGroup
import com.pubnub.internal.extension.scheduleWithDelay
import org.slf4j.LoggerFactory
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.ScheduledExecutorService
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds

internal abstract class RetryableCallback(
    retryConfiguration: RetryConfiguration,
    endpointGroupName: RetryableEndpointGroup,
    private val call: Call,
    private val isEndpointRetryable: Boolean,
    private val executorService: ScheduledExecutorService,
) : Callback, RetryableBase(retryConfiguration, endpointGroupName) {
    private val log = LoggerFactory.getLogger(this.javaClass.simpleName)
    private var retryCount = 0
    private var exponentialMultiplier = 0.0

    override fun onResponse(
        call: Call,
        response: Response,
    ) {
        if (shouldRetryOnResponse(response)) {
            retryOnResponseWithError(response)
        } else {
            onFinalResponse(call, response)
        }
    }

    override fun onFailure(
        call: Call,
        t: Throwable,
    ) {
        if (shouldRetryOnFailure(t)) {
            retryOnFailure()
        } else {
            onFinalFailure(call, t)
        }
    }

    private fun shouldRetryOnResponse(response: Response): Boolean {
        return !response.isSuccessful &&
            retryCount < maxRetryNumberFromConfiguration &&
            isErrorCodeRetryable(response.raw().code) &&
            isRetryConfSetForThisRestCall &&
            isEndpointRetryable
    }

    private fun shouldRetryOnFailure(t: Throwable): Boolean {
        val exception = Exception(t)
        return retryCount < maxRetryNumberFromConfiguration &&
            isExceptionRetryable(exception) &&
            isRetryConfSetForThisRestCall &&
            isEndpointRetryable
    }

    private fun isExceptionRetryable(e: Exception): Boolean {
        return e.cause?.let { cause ->
            retryableExceptions.any { it.isInstance(cause) }
        } ?: false
    }

    private fun retryOnFailure() {
        val effectiveDelay = getDelayFromRetryConfiguration()
        retry(effectiveDelay)
    }

    private fun retryOnResponseWithError(response: Response) {
        val effectiveDelay: Duration = getDelayForRetryOnResponse(response)
        retry(effectiveDelay)
    }

    private fun retry(delay: Duration) {
        retryCount++
        val randomDelayInMillis: Int = random.nextInt(MAX_RANDOM_DELAY_IN_MILLIS)
        val effectiveDelay: Duration = delay + randomDelayInMillis.milliseconds
        log.trace("Added random delay so effective retry delay is ${effectiveDelay.inWholeMilliseconds} millis")
        // don't want to block the main thread in case of Android so using executorService
        try {
            executorService.scheduleWithDelay(effectiveDelay) {
                call.clone().enqueue(this)
            }
        } catch (_: RejectedExecutionException) {
            log.trace("Unable to schedule retry, PubNub was likely already destroyed.")
        }
    }

    private fun getDelayForRetryOnResponse(response: Response): Duration {
        return getDelayBasedOnResponse(response)
    }

    abstract fun onFinalResponse(
        call: Call,
        response: Response,
    )

    abstract fun onFinalFailure(
        call: Call,
        t: Throwable,
    )
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy