Політика повторних спроб у мобільній розробці — суть, стратегії та принципи

Автор: IT Sectr Опубліковано: 2026-03-11 Час читання: 10 хв

Політика повторних спроб — набір правил, які визначають, коли і як мобільний застосунок автоматично повторює невдалі мережеві виклики. При нестабільному з’єднанні або тимчасових помилках сервера грамотна політика повторів підвищує надійність застосунку без участі користувача. За даними дослідження Google Developer Relations (2025), правильна реалізація політики повторних спроб знижує відсоток втрачених запитів на 40–60% у мобільних застосунках із частими мережевими операціями.

Головне

  • Політика повторних спроб — стратегія автоматичних повторів запитів при збоях мережі або тимчасових помилках сервера.
  • Exponential backoff — метод збільшення затримки між повторами для зниження навантаження на сервер.
  • Jitter — випадкове відхилення затримки, що запобігає ефекту “грому серед ясного неба” (thundering herd).
  • Ідемпотентність — ключова вимога для безпечних повторів: повторний запит не повинен викликати побічних ефектів.
  • Circuit Breaker — механізм зупинки повторів при тривалій недоступності сервісу для збереження ресурсів.

Що таке політика повторних спроб?

Політика повторних спроб — це програмна стратегія, що визначає поведінку клієнта при збої мережевого запиту: які помилки варто повторювати, скільки разів, з якою затримкою і коли припинити спроби. У мобільних застосунках політика повторів критично важлива через нестабільність мобільних мереж і можливих тимчасових збоїв на стороні сервера.

Базова політика повторних спроб включає три параметри: максимальна кількість повторів (maxRetries), початкова затримка (baseDelay) і стратегія збільшення затримки (backoff strategy). Додатково може бути заданий список HTTP-статусів, на які слід реагувати повтором, і таймаут для переривання всіх спроб.

За даними книги “Designing Data-Intensive Applications” Мартіна Клеппмана, 50% збоїв у розподілених системах є тимчасовими і усуваються при повторній спробі. Це робить політику повторних спроб одним із найефективніших і найдешевших способів підвищення відмовостійкості мобільного застосунку без змін серверної архітектури.

Які помилки варто повторювати

Тимчасові помилки (retriable) — єдиний тип збоїв, на які політика повторних спроб повинна реагувати. До них належать таймаути з’єднання (SocketTimeoutException), тимчасова недоступність сервера (HTTP 503, 502) і помилки DNS. Постійні помилки — HTTP 400, 401, 403, 404 — повторювати безглуздо, оскільки вони вказують на проблему в запиті, а не в мережі або сервері.

За даними дослідження AWS Architecture Blog, правильна класифікація помилок на retriable та non-retriable — найважливіше рішення при проектуванні політики повторних спроб. Повтор неідемпотентного запиту на HTTP 401 може призвести до блокування облікового запису, а повтор HTTP 400 — до створення дублікатів даних. Завжди конфігуруйте список кодів для повтору явно.

Основні стратегії повторів

Fixed interval — найпростіша стратегія: кожен повтор виконується через однаковий проміжок часу. Наприклад, при затримці 2 секунди застосунок повторює запит через 2, 2, 2 секунди. Fixed interval простий у реалізації та передбачуваний, але створює рівномірне навантаження на сервер при масових збоях.

Incremental interval — затримка збільшується лінійно з кожним повтором: перший повтор через 1 секунду, другий — через 2, третій — через 3 і так далі. Ця стратегія дає серверу більше часу на відновлення при повторних збоях, але все ще передбачувана для безлічі одночасно падаючих клієнтів.

СтратегіяФормула затримкиКумулятивний час (3 спроби)Застосування
Fixeddelay = D3 × DПрості сценарії, локальні таймаути
Incrementaldelay = N × D6 × DПоступове зниження навантаження
Exponentialdelay = D × 2^N7 × DМасові збої, хмарні сервіси
Exponential + Jitterdelay = random(0, D × 2^N)variesВисоке навантаження, мікросервіси

Вибір стратегії залежить від характеру застосунку. Для фонових задач синхронізації даних на мобільних пристроях оптимальна exponential стратегія з jitter — вона дає найбільшу ймовірність успіху при мінімальному навантаженні на сервер і пристрій користувача.

Exponential Backoff і Jitter

Exponential backoff — стратегія, при якій затримка між повторами подвоюється з кожною спробою. Якщо початкова затримка становить 1 секунду, то послідовність затримок буде 1, 2, 4, 8, 16 секунд. Це дає серверу експоненційно зростаючий час на відновлення.

Jitter — випадкове відхилення затримки, яке запобігає синхронним повторним запитам від безлічі клієнтів (thundering herd problem). Без jitter тисяча клієнтів з однаковою політикою повторних спроб буде повторювати запити одночасно, створюючи пікове навантаження на сервер. Jitter розносить повтори в часі.

Реалізація на Kotlin з корутинами

Корутини Kotlin дозволяють реалізувати exponential backoff з jitter без блокування основного потоку. Функція retry з kotlinx-coroutines приймає умову повтору та блок з тілом запиту, автоматично керуючи затримками та кількістю спроб.

kotlin
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!!)
}

Функція executeWithRetry приймає лямбду з мережевим викликом і виконує її з exponential backoff та jitter. Якщо помилка не підлягає повтору (не retriable) або перевищено максимальну кількість спроб, функція повертає помилку. Затримка домножується на випадковий коефіцієнт від 0.5 до 1.5 для рівномірного розподілу повторів.

Circuit Breaker і відмова від повторів

Circuit Breaker — патерн проектування, який запобігає нескінченним повторним запитам при тривалій недоступності сервісу. Коли кількість помилок перевищує поріг, Circuit Breaker переходить у стан OPEN і негайно повертає помилку без виконання запиту, даючи серверу час на відновлення.

У мобільних застосунках Circuit Breaker особливо корисний при недоступності API через планове обслуговування або мережеві збої оператора. Без нього застосунок буде споживати батарею та трафік на нескінченні повторні спроби, погіршуючи користувацький досвід і скорочуючи час автономної роботи пристрою.

Схема станів Circuit Breaker

Три стани Circuit Breaker: CLOSED (нормальна робота, запити виконуються), OPEN (відмова, запити блокуються) і HALF_OPEN (пробний запит для перевірки відновлення). Після заданого таймауту в стані OPEN перемикач переходить у HALF_OPEN і виконує один запит — при успіху повертається в CLOSED, при невдачі — в OPEN.

kotlin
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
        }
    }
}

Реалізація Circuit Breaker на Kotlin містить лічильник помилок і таймер відновлення. Метод protect перевіряє поточний стан перед виконанням блокованого запиту та оновлює лічильник помилок при збоях. Після досягнення порогу failureThreshold всі запити негайно відхиляються до закінчення timeoutMs.

Політика повторних спроб у мобільних застосунках

Мобільні мережі мають особливості, які роблять політику повторних спроб особливо важливою. Перемикання між Wi-Fi та мобільними даними, втрата сигналу в метро та тунелях, тимчасові блокування на рівні оператора — всі ці сценарії призводять до збоїв запитів, які успішно обробляються повторними спробами.

На Android бібліотека Retrofit та OkHttp надають вбудований механізм RetryPolicy через Interceptor. На iOS задача вирішується через URLSessionConfiguration та кастомне делегування. Для крос-платформенної розробки Ktor (KMP) містить вбудовану підтримку retry з налаштовуваними стратегіями.

Реалізація на iOS з Combine

Combine — фреймворк Apple для реактивного програмування. Оператор retry в Combine повторює паблішер вказану кількість разів при помилці, але не дозволяє налаштовувати затримку між повторами. Для повноцінної політики повторних спроб використовується кастомна комбінація catch та flatMap із затримкою.

swift
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()
    }
}

Розширення retryWithBackoff для Publisher в Combine реалізує exponential backoff через рекурсивний виклик із зменшенням лічильника та подвоєнням затримки. Оператор delay створює паузу між повторами, а catch перехоплює помилку та вирішує, чи намагатися знову чи повернути failure.

Типові помилки при політиці повторних спроб

Перша помилка — повторювати запити без перевірки ідемпотентності. Якщо сервер створив ресурс, але не повернув підтвердження через збій мережі, повторний запит створить дублікат. Для POST-запитів завжди використовуйте ідемпотентний ключ (Idempotency-Key) у заголовку або переходьте на retry тільки для GET, PUT та DELETE.

Друга помилка — нескінченні повтори (retry forever). Завжди встановлюйте максимальну кількість спроб (3–5 для мобільних застосунків) і загальний таймаут на всі спроби. Нескінченні повтори розряджають батарею та створюють паразитне навантаження на сервер, особливо при міграції баз даних або зміні API.

Третя помилка — ігнорувати контекст застосунку. Якщо користувач закрив застосунок або перейшов у фоновий режим, активні політики повторних спроб повинні коректно скасовуватися. Використовуйте корутини з SupervisorScope або Combine з життєвим циклом UI для автоматичного скасування повторів при закритті екрана.

Четверта помилка — не логувати повторні спроби. Без логування ви не дізнаєтеся, скільки запитів було повторено, які помилки виникали і наскільки ефективна ваша політика повторних спроб. Додайте метрики: кількість повторів, успішність після повтору, розподіл затримок. Ці дані допоможуть налаштувати оптимальні параметри стратегії під конкретний застосунок.

Часті запитання

Скільки разів повторювати запит у мобільному застосунку?

Оптимальна кількість повторів — 3–5 спроб для більшості сценаріїв. Для фонової синхронізації допустимо 5–7 спроб, для інтерактивних запитів (наприклад, відправка форми) — не більше 3. Більша кількість повторів не збільшує ймовірність успіху, але витрачає батарею та трафік користувача.

Що таке exponential backoff простими словами?

Exponential backoff — це подвоєння затримки між повторними спробами: 1 секунда, 2, 4, 8, 16 і так далі. Якщо сервер перевантажений, коротка пауза між першими повторами дозволяє йому швидко відповісти, а зростаюча пауза з кожним наступним повтором дає серверу все більше часу на відновлення.

Які HTTP-статуси варто повторювати?

Повторюйте лише тимчасові помилки: 408 (Request Timeout), 429 (Too Many Requests), 502 (Bad Gateway), 503 (Service Unavailable), 504 (Gateway Timeout). Помилки 4xx (крім 408 та 429) вказують на проблеми клієнта — повторювати їх безглуздо і може бути небезпечно для даних користувача.

Чим відрізняється політика повторних спроб від Circuit Breaker?

Політика повторних спроб керує повторенням одного запиту при збої. Circuit Breaker керує станом з’єднання з сервісом: при накопиченні помилок він розмикає ланцюг (OPEN) і не допускає нові запити. Retry працює на рівні окремого виклику, Circuit Breaker — на рівні інтеграції з сервісом.

Як тестувати політику повторних спроб на мобільних пристроях?

Для тестування політики повторних спроб використовуйте NetworkInterceptor (OkHttp) на Android та URLProtocol (URLSession) на iOS для симуляції збоїв мережі. Задайте параметри: частота помилок, тривалість недоступності та коди відповідей. Юніт-тести з MockWebServer (OkHttp) або OHHTTPStubs (iOS) перевіряють логіку повторів без реальної мережі.

Підсумки

  • Політика повторних спроб — стратегія автоматичних повторів мережевих запитів при тимчасових збоях з налаштовуваними параметрами затримки та кількості спроб.
  • Exponential backoff з jitter — базова стратегія для мобільних застосунків, що знижує навантаження на сервер при масових збоях та запобігає ефекту thundering herd.
  • Ідемпотентність — обов’язкова умова для безпечного повторення не-GET запитів: без неї повтор створює дублікати даних або небажані побічні ефекти.
  • Circuit Breaker доповнює політику повторних спроб, запобігаючи нескінченним повторам при тривалій недоступності сервісу та економлячи ресурси пристрою.
  • Класифікація помилок — розділення на retriable (503, 502, timeout) та non-retriable (400, 401, 403) критично важливе для коректної роботи політики повторів.
  • Максимум 3–5 повторів в інтерактивних сценаріях і до 7 для фонової синхронізації — оптимальне значення для мобільних застосунків за даними Google Developer Relations.
  • Рекомендація — реалізуйте політику повторних спроб з exponential backoff, Circuit Breaker та логуванням для всіх мережевих запитів у мобільному застосунку.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також