Retry Policy — politika opakovaných požadavků — sada pravidel určujících, kdy a jak mobilní aplikace automaticky opakuje neúspěšná síťová volání. Při nestabilním připojení nebo dočasných chybách serveru zvyšuje správně navržená politika opakování spolehlivost aplikace bez účasti uživatele. Podle výzkumu Google Developer Relations (2025) správná implementace Retry Policy snižuje procento ztracených požadavků o 40-60 % v mobilních aplikacích s častými síťovými operacemi.
Hlavní body
Retry Policy — softwarová strategie určující chování klienta při selhání síťového požadavku: které chyby opakovat, kolikrát, s jakým zpožděním a kdy ukončit pokusy. V mobilních aplikacích je politika opakování kriticky důležitá kvůli nestabilitě mobilních sítí a možným dočasným výpadkům na straně serveru.
Základní Retry Policy zahrnuje tři parametry: maximální počet opakování (maxRetries), počáteční zpoždění (baseDelay) a strategii zvyšování zpoždění (backoff strategy). Dodatečně lze zadat seznam HTTP stavových kódů, na které je třeba reagovat opakováním, a časový limit pro přerušení všech pokusů.
Podle knihy „Designing Data-Intensive Applications" Martina Kleppmanna je 50 % selhání v distribuovaných systémech dočasných a lze je odstranit opakovaným pokusem. To činí Retry Policy jedním z nejúčinnějších a nejlevnějších způsobů, jak zvýšit odolnost mobilní aplikace bez změn architektury serveru.
Dočasné chyby (retriable) — jediný typ selhání, na který by Retry Policy měla reagovat. Patří mezi ně časové limity připojení (SocketTimeoutException), dočasná nedostupnost serveru (HTTP 503, 502) a chyby DNS. Trvalé chyby — HTTP 400, 401, 403, 404 — nemá smysl opakovat, protože ukazují na problém v požadavku, nikoli v síti nebo serveru.
Podle výzkumu AWS Architecture Blog je správná klasifikace chyb na retriable a non-retriable nejdůležitějším rozhodnutím při navrhování Retry Policy. Opakování neidempotentního požadavku s HTTP 401 může vést k zablokování účtu a opakování HTTP 400 k vytvoření duplicit dat. Vždy explicitně konfigurujte seznam kódů pro opakování.
Fixed interval — nejjednodušší strategie: každé opakování se provádí po stejném časovém intervalu. Například při zpoždění 2 sekund aplikace opakuje požadavek po 2, 2, 2 sekundách. Fixed interval je jednoduchý na implementaci a předvídatelný, ale při hromadných selháních vytváří rovnoměrné zatížení serveru.
Incremental interval — zpoždění se lineárně zvyšuje s každým opakováním: první opakování po 1 sekundě, druhé po 2, třetí po 3 a tak dále. Tato strategie dává serveru více času na zotavení při opakujících se selháních, ale je stále předvídatelná pro mnoho klientů, kteří selhávají současně.
| Strategie | Vzorec zpoždění | Kumulativní čas (3 pokusy) | Použití |
|---|---|---|---|
| Fixed | delay = D | 3 × D | Jednoduché scénáře, místní časové limity |
| Incremental | delay = N × D | 6 × D | Postupné snižování zátěže |
| Exponential | delay = D × 2^N | 7 × D | Hromadná selhání, cloudové služby |
| Exponential + Jitter | delay = random(0, D × 2^N) | proměnný | Vysoká zátěž, mikroslužby |
Výběr strategie závisí na povaze aplikace. Pro úkoly na pozadí synchronizace dat na mobilních zařízeních je optimální exponenciální strategie s jitterem — poskytuje nejvyšší pravděpodobnost úspěchu s minimálním zatížením serveru a zařízení uživatele.
Exponential backoff — strategie, při které se zpoždění mezi opakováními s každým pokusem zdvojnásobuje. Pokud je počáteční zpoždění 1 sekunda, bude sekvence zpoždění 1, 2, 4, 8, 16 sekund. To dává serveru exponenciálně rostoucí čas na zotavení.
Jitter — náhodná odchylka zpoždění, která zabraňuje synchronním opakovaným požadavkům od mnoha klientů (problém thundering herd). Bez jitteru bude tisíc klientů se stejnou Retry Policy opakovat požadavky současně, což vytvoří špičkové zatížení serveru. Jitter rozkládá opakování v čase.
Korutiny Kotlinu umožňují implementovat exponential backoff s jitterem bez blokování hlavního vlákna. Funkce retry z knihovny kotlinx-coroutines přijímá podmínku opakování a blok s tělem požadavku, automaticky spravuje zpoždění a počet pokusů.
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!!)
}
Funkce executeWithRetry přijímá lambdu se síťovým voláním a provádí ji s exponential backoff a jitterem. Pokud chyba není opakovatelná (není retriable) nebo je překročen maximální počet pokusů, funkce vrátí chybu. Zpoždění se násobí náhodným koeficientem od 0,5 do 1,5 pro rovnoměrné rozložení opakování.
Circuit Breaker — návrhový vzor, který zabraňuje nekonečným opakovaným požadavkům při dlouhodobé nedostupnosti služby. Když počet chyb překročí práh, Circuit Breaker přejde do stavu OPEN a okamžitě vrátí chybu bez provedení požadavku, čímž poskytne serveru čas na zotavení.
V mobilních aplikacích je Circuit Breaker užitečný zejména při nedostupnosti API z důvodu plánované údržby nebo selhání sítě operátora. Bez něj bude aplikace spotřebovávat baterii a přenos dat na nekonečné opakované pokusy, což zhoršuje uživatelský zážitek a zkracuje výdrž baterie zařízení.
Tři stavy Circuit Breaker: CLOSED (normální provoz, požadavky se provádějí), OPEN (odmítnutí, požadavky jsou blokovány) a HALF_OPEN (zkušební požadavek pro kontrolu zotavení). Po určitém časovém limitu ve stavu OPEN přepínač přejde do HALF_OPEN a provede jeden požadavek — při úspěchu se vrátí do CLOSED, při neúspěchu do 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
}
}
}
Implementace Circuit Breaker v Kotlinu obsahuje počítadlo chyb a časovač zotavení. Metoda protect kontroluje aktuální stav před provedením blokovaného požadavku a aktualizuje počítadlo chyb při selháních. Po dosažení prahu failureThreshold jsou všechny požadavky okamžitě odmítnuty do vypršení timeoutMs.
Mobilní sítě mají vlastnosti, které činí Retry Policy obzvláště důležitou. Přepínání mezi Wi-Fi a mobilními daty, ztráta signálu v metru a tunelech, dočasné blokace na úrovni operátora — všechny tyto scénáře vedou k selhání požadavků, která jsou úspěšně řešena opakovanými pokusy.
Na Androidu poskytují knihovna Retrofit a OkHttp vestavěný mechanismus RetryPolicy prostřednictvím Interceptoru. Na iOS je problém řešen prostřednictvím URLSessionConfiguration a vlastního delegování. Pro multiplatformní vývoj obsahuje Ktor (KMP) vestavěnou podporu retry s konfigurovatelnými strategiemi.
Combine — framework Apple pro reaktivní programování. Operátor retry v Combine opakuje publisher při chybě určitý počet opakování, ale neumožňuje konfigurovat zpoždění mezi opakováními. Pro plnohodnotnou Retry Policy se používá vlastní kombinace catch a flatMap se zpožděním.
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()
}
}
Rozšíření retryWithBackoff pro Publisher v Combine implementuje exponential backoff prostřednictvím rekurzivního volání se snižováním počítadla a zdvojnásobováním zpoždění. Operátor delay vytváří pauzu mezi opakováními a catch zachycuje chybu a rozhoduje, zda se pokusit znovu nebo vrátit failure.
První chyba — opakování požadavků bez kontroly idempotence. Pokud server vytvořil zdroj, ale nevrátil potvrzení kvůli selhání sítě, opakovaný požadavek vytvoří duplikát. Pro požadavky POST vždy používejte idempotentní klíč (Idempotency-Key) v záhlaví nebo přejděte na retry pouze pro GET, PUT a DELETE.
Druhá chyba — nekonečná opakování (retry forever). Vždy nastavte maximální počet pokusů (3-5 pro mobilní aplikace) a celkový časový limit pro všechny pokusy. Nekonečná opakování vybíjejí baterii a vytvářejí parazitní zatížení serveru, zejména při migraci databází nebo změně API.
Třetí chyba — ignorování kontextu aplikace. Pokud uživatel zavřel aplikaci nebo přešel do režimu na pozadí, aktivní Retry Policy by měly být správně zrušeny. Používejte korutiny s SupervisorScope nebo Combine s životním cyklem UI pro automatické zrušení opakování při zavření obrazovky.
Čtvrtá chyba — neprotokolování opakovaných pokusů. Bez protokolování nebudete vědět, kolik požadavků bylo opakováno, jaké chyby se vyskytly a jak účinná je vaše Retry Policy. Přidejte metriky: počet opakování, úspěšnost po opakování, rozložení zpoždění. Tato data pomohou nastavit optimální parametry strategie pro konkrétní aplikaci.
Často kladené otázky
Optimální počet opakování — 3-5 pokusů pro většinu scénářů. Pro synchronizaci na pozadí je povoleno 5-7 pokusů, pro interaktivní požadavky (např. odeslání formuláře) — ne více než 3. Větší počet opakování nezvyšuje pravděpodobnost úspěchu, ale spotřebovává baterii a přenos dat uživatele.
Exponential backoff — zdvojnásobování zpoždění mezi opakovanými pokusy: 1 sekunda, 2, 4, 8, 16 a tak dále. Pokud je server přetížený, krátká pauza mezi prvními opakováními mu umožňuje rychle odpovědět a rostoucí pauza s každým dalším pokusem dává serveru stále více času na zotavení.
Opakujte pouze dočasné chyby: 408 (Request Timeout), 429 (Too Many Requests), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout). Chyby 4xx (kromě 408 a 429) ukazují na problémy klienta — jejich opakování nemá smysl a může být nebezpečné pro data uživatele.
Retry Policy řídí opakování jednoho požadavku při selhání. Circuit Breaker řídí stav připojení ke službě: při nahromadění chyb otevře okruh (OPEN) a neumožňuje nové požadavky. Retry funguje na úrovni jednotlivého volání, Circuit Breaker — na úrovni integrace se službou.
Pro testování Retry Policy použijte NetworkInterceptor (OkHttp) na Androidu a URLProtocol (URLSession) na iOS pro simulaci selhání sítě. Nastavte parametry: frekvenci chyb, dobu nedostupnosti a kódy odpovědí. Unit testy s MockWebServer (OkHttp) nebo OHHTTPStubs (iOS) kontrolují logiku opakování bez reálné sítě.
Shrnutí
Vyvineme mobilní aplikaci na klíč
IT Sectr vytváří aplikace pro iOS a Android pro startupy a podniky od roku 2017. Poradíme vám a navrhneme nejlepší řešení.
Přečtěte si také