La Retry Policy — politique de reprise — est un ensemble de règles qui déterminent quand et comment une application mobile répète automatiquement les appels réseau ayant échoué. Avec des connexions instables ou des erreurs temporaires du serveur, une politique de reprise bien conçue améliore la fiabilité de l'application sans intervention de l'utilisateur. Selon une étude de Google Developer Relations (2025), une implémentation correcte de la Retry Policy réduit le pourcentage de requêtes perdues de 40 à 60 % dans les applications mobiles avec des opérations réseau fréquentes.
Points clés
Retry Policy — une stratégie logicielle qui définit le comportement du client en cas d'échec d'une requête réseau : quelles erreurs doivent être reprises, combien de fois, avec quel délai et quand arrêter les tentatives. Dans les applications mobiles, la politique de reprise est d'une importance cruciale en raison de l'instabilité des réseaux mobiles et des pannes temporaires possibles du côté serveur.
Une Retry Policy de base comprend trois paramètres : le nombre maximum de tentatives (maxRetries), le délai initial (baseDelay) et la stratégie de backoff. De plus, une liste de codes d'état HTTP qui doivent déclencher une tentative peut être spécifiée, ainsi qu'un délai d'attente pour abandonner toutes les tentatives.
Selon le livre « Designing Data-Intensive Applications » de Martin Kleppmann, 50 % des pannes dans les systèmes distribués sont temporaires et sont résolues par une nouvelle tentative. Cela fait de la Retry Policy l'un des moyens les plus efficaces et les moins coûteux d'améliorer la tolérance aux pannes d'une application mobile sans modifier l'architecture du serveur.
Les erreurs temporaires (pouvant être reprises) sont le seul type de panne auquel la Retry Policy doit répondre. Cela inclut les délais d'attente de connexion (SocketTimeoutException), l'indisponibilité temporaire du serveur (HTTP 503, 502) et les erreurs DNS. Les erreurs permanentes — HTTP 400, 401, 403, 404 — ne doivent pas être reprises, car elles indiquent un problème dans la requête, non dans le réseau ou le serveur.
Selon l'AWS Architecture Blog, la classification correcte des erreurs en récupérables et non récupérables est la décision la plus importante lors de la conception d'une Retry Policy. Reprendre une requête non idempotente avec HTTP 401 peut entraîner le blocage du compte, et reprendre HTTP 400 peut créer des données en double. Configurez toujours explicitement la liste des codes à reprendre.
Intervalle fixe — la stratégie la plus simple : chaque tentative est effectuée après le même intervalle de temps. Par exemple, avec un délai de 2 secondes, l'application répète la requête après 2, 2, 2 secondes. L'intervalle fixe est simple à implémenter et prévisible, mais crée une charge uniforme sur le serveur en cas de pannes massives.
Intervalle incrémental — le délai augmente linéairement à chaque tentative : première tentative après 1 seconde, deuxième après 2, troisième après 3, et ainsi de suite. Cette stratégie donne au serveur plus de temps pour récupérer en cas de pannes répétées, mais reste prévisible pour de nombreux clients échouant simultanément.
| Stratégie | Formule de délai | Temps cumulé (3 tentatives) | Application |
|---|---|---|---|
| Fixe | delay = D | 3 × D | Scénarios simples, timeouts locaux |
| Incrémental | delay = N × D | 6 × D | Réduction progressive de la charge |
| Exponentiel | delay = D × 2^N | 7 × D | Pannes massives, services cloud |
| Exponentiel + Jitter | delay = random(0, D × 2^N) | variable | Charge élevée, microservices |
Le choix de la stratégie dépend de la nature de l'application. Pour les tâches d'arrière-plan de synchronisation de données sur les appareils mobiles, la stratégie exponentielle avec jitter est optimale — elle offre la plus grande probabilité de succès avec une charge minimale sur le serveur et l'appareil de l'utilisateur.
L'exponential backoff — une stratégie où le délai entre les tentatives double à chaque essai. Si le délai initial est de 1 seconde, la séquence des délais sera 1, 2, 4, 8, 16 secondes. Cela donne au serveur un temps de récupération qui croît de façon exponentielle.
Le Jitter — une déviation aléatoire du délai qui empêche les requêtes de reprise synchrones de plusieurs clients (problème du thundering herd). Sans jitter, mille clients avec la même Retry Policy répéteraient les requêtes simultanément, créant une charge de pointe sur le serveur. Le jitter disperse les tentatives dans le temps.
Les coroutines Kotlin permettent d'implémenter l'exponential backoff avec jitter sans bloquer le thread principal. La fonction retry de kotlinx-coroutines prend une condition de reprise et un bloc avec le corps de la requête, gérant automatiquement les délais et le nombre de tentatives.
suspend fun RetryPolicy.executeWithRetry(
block: suspend () -> Result<T>
): Result<T> {
var lastError: Throwable? = null
repeat(maxRetries + 1) { attempt ->
try {
return block()
} catch (e: Exception) {
if (!isRetriable(e) || attempt == maxRetries) {
return Result.failure(e)
}
val delay = (baseDelayMs * (1 shl attempt))
.toLong()
val jitteredDelay = (delay * (0.5 + Random.nextDouble())).toLong()
delay(jitteredDelay)
lastError = e
}
}
return Result.failure(lastError!!)
}
La fonction executeWithRetry prend un lambda avec un appel réseau et l'exécute avec exponential backoff et jitter. Si l'erreur n'est pas récupérable ou si le nombre maximum de tentatives est dépassé, la fonction retourne une erreur. Le délai est multiplié par un facteur aléatoire de 0,5 à 1,5 pour une distribution uniforme des tentatives.
Circuit Breaker — un modèle de conception qui empêche les requêtes de reprise infinies lors d'une indisponibilité prolongée du service. Lorsque le nombre d'erreurs dépasse un seuil, le Circuit Breaker passe à l'état OPEN et retourne immédiatement une erreur sans exécuter la requête, donnant au serveur le temps de récupérer.
Dans les applications mobiles, le Circuit Breaker est particulièrement utile lorsqu'une API est indisponible en raison d'une maintenance planifiée ou de pannes réseau de l'opérateur. Sans lui, l'application consommerait batterie et trafic dans des tentatives de reprise infinies, dégradant l'expérience utilisateur et réduisant l'autonomie de l'appareil.
Le Circuit Breaker a trois états : CLOSED (fonctionnement normal, les requêtes sont exécutées), OPEN (panne, les requêtes sont bloquées) et HALF_OPEN (requête de test pour vérifier la récupération). Après un délai d'attente spécifié dans l'état OPEN, le disjoncteur passe à HALF_OPEN et exécute une requête — en cas de succès, il retourne à CLOSED ; en cas d'échec, à OPEN.
class CircuitBreaker(
private val failureThreshold: Int = 3,
private val timeoutMs: Long = 30000
) {
private var state = State.CLOSED
private var failureCount = 0
private var lastFailureTime: Long = 0
suspend fun T.protect(block: suspend () -> T): T {
checkState()
return try {
val result = block()
onSuccess()
result
} catch (e: Exception) {
onFailure()
throw e
}
}
}
L'implémentation du Circuit Breaker en Kotlin contient un compteur d'erreurs et une minuterie de récupération. La méthode protect vérifie l'état actuel avant d'exécuter la requête encapsulée et met à jour le compteur d'erreurs en cas d'échec. Après avoir atteint le failureThreshold, toutes les requêtes sont immédiatement rejetées jusqu'à l'expiration du timeoutMs.
Les réseaux mobiles ont des caractéristiques qui rendent la Retry Policy particulièrement importante. Le basculement entre Wi-Fi et données mobiles, la perte de signal dans le métro et les tunnels, les blocages temporaires au niveau de l'opérateur — tous ces scénarios entraînent des échecs de requêtes qui peuvent être traités avec succès par des tentatives.
Sur Android, la bibliothèque Retrofit et OkHttp fournissent un mécanisme de reprise intégré via Interceptor. Sur iOS, la tâche est résolue via URLSessionConfiguration et une délégation personnalisée. Pour le développement multiplateforme, Ktor (KMP) inclut un support de reprise intégré avec des stratégies configurables.
Combine — le framework d'Apple pour la programmation réactive. L'opérateur retry dans Combine répète le publisher un nombre spécifié de fois en cas d'erreur, mais ne permet pas de configurer le délai entre les tentatives. Pour une Retry Policy complète, une combinaison personnalisée de catch et flatMap avec délai est utilisée.
extension Publisher {
func retryWithBackoff(
retries: Int = 3,
baseDelay: TimeInterval = 1.0
) -> AnyPublisher<Output, Failure> {
return self.catch { error -> AnyPublisher in
guard retries > 0 else {
return Fail(error).eraseToAnyPublisher()
}
return Just(())
.delay(for: .seconds(baseDelay), scheduler: DispatchQueue.main)
.flatMap { self.retryWithBackoff(
retries: retries - 1,
baseDelay: baseDelay * 2
) }
.eraseToAnyPublisher()
}
.eraseToAnyPublisher()
}
}
L'extension retryWithBackoff pour Publisher dans Combine implémente l'exponential backoff via des appels récursifs avec compteur décroissant et délai doublé. L'opérateur delay crée une pause entre les tentatives, tandis que catch intercepte l'erreur et décide s'il faut réessayer ou retourner un échec.
La première erreur — répéter les requêtes sans vérifier l'idempotence. Si le serveur a créé une ressource mais n'a pas retourné de confirmation en raison d'un échec réseau, une nouvelle tentative créera un doublon. Pour les requêtes POST, utilisez toujours une clé d'idempotence (Idempotency-Key) dans l'en-tête ou limitez les reprises aux requêtes GET, PUT et DELETE uniquement.
La deuxième erreur — répéter indéfiniment. Définissez toujours un nombre maximum de tentatives (3 à 5 pour les applications mobiles) et un délai d'attente total pour toutes les tentatives. Les reprises infinies épuisent la batterie et créent une charge parasite sur le serveur, en particulier lors de migrations de bases de données ou de modifications d'API.
La troisième erreur — ignorer le contexte de l'application. Si l'utilisateur a fermé l'application ou est passé en arrière-plan, les Retry Policies actives doivent être correctement annulées. Utilisez des coroutines avec SupervisorScope ou Combine avec le cycle de vie de l'interface utilisateur pour l'annulation automatique des tentatives lors de la fermeture de l'écran.
La quatrième erreur — ne pas journaliser les tentatives. Sans journalisation, vous ne saurez pas combien de requêtes ont été reprises, quelles erreurs se sont produites et quelle est l'efficacité de votre Retry Policy. Ajoutez des métriques : nombre de tentatives, succès après tentative, distribution des délais. Ces données vous aideront à ajuster les paramètres optimaux de la stratégie pour votre application spécifique.
Questions fréquentes
Le nombre optimal de tentatives est de 3 à 5 pour la plupart des scénarios. Pour la synchronisation en arrière-plan, 5 à 7 tentatives sont acceptables ; pour les requêtes interactives (comme l'envoi d'un formulaire), pas plus de 3. Un nombre plus élevé de tentatives n'augmente pas la probabilité de succès mais consomme la batterie et le trafic de données de l'utilisateur.
L'exponential backoff — c'est le doublement du délai entre les tentatives : 1 seconde, 2, 4, 8, 16 et ainsi de suite. Si le serveur est surchargé, la courte pause entre les premières tentatives lui permet de répondre rapidement, tandis que la pause croissante à chaque tentative suivante donne au serveur plus de temps pour récupérer.
Ne répétez que les erreurs temporaires : 408 (Request Timeout), 429 (Too Many Requests), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout). Les erreurs 4xx (sauf 408 et 429) indiquent des problèmes côté client — les répéter n'a pas de sens et peut être dangereux pour les données de l'utilisateur.
Retry Policy gère la reprise d'une seule requête en cas d'échec. Le Circuit Breaker gère l'état de la connexion avec un service : lorsque les erreurs s'accumulent, il ouvre le circuit (OPEN) et bloque les nouvelles requêtes. La reprise fonctionne au niveau de l'appel individuel, le Circuit Breaker au niveau de l'intégration du service.
Pour tester la Retry Policy, utilisez NetworkInterceptor (OkHttp) sur Android et URLProtocol (URLSession) sur iOS pour simuler les pannes réseau. Définissez les paramètres : fréquence des erreurs, durée d'indisponibilité et codes de réponse. Les tests unitaires avec MockWebServer (OkHttp) ou OHHTTPStubs (iOS) vérifient la logique de reprise sans réseau réel.
Résumé
Nous développerons une application mobile clé en main
IT Sectr crée des applications iOS et Android pour les startups et les entreprises depuis 2017. Nous vous conseillerons et vous proposerons la meilleure solution.
Lisez aussi