Offline Queue — це механізм, який зберігає операції користувача локально, коли пристрій перебуває поза мережею, і надсилає їх на сервер після відновлення з'єднання. Без офлайн-черги користувач втрачає всі дії, виконані без інтернету, що в мобільних додатках неприпустимо. За даними Google Developers (2025), впровадження offline-first архітектури підвищує утримання користувачів на 30% у регіонах з нестабільним інтернетом.
Головне
Offline Queue — це впорядкована колекція операцій (створення, оновлення, видалення), які додаток зберігає локально, коли пристрій не має доступу до мережі. Як тільки з'єднання відновлюється, черга надсилає операції на сервер у тому ж порядку, в якому користувач їх виконав.
Уявіть сценарій: користувач месенджера набирає повідомлення в метро без інтернету. Кожне натискання «Надіслати» додається до Offline Queue. Коли поїзд виїжджає з тунелю і мережа з'являється, всі повідомлення надсилаються автоматично. Користувацький досвід — безшовний: він не помічає, що був в офлайні, окрім легкої затримки при надсиланні.
За даними Uber Engineering (2024), їхня офлайн-черга обробляє понад 2 мільйони операцій на день у регіонах з низькою якістю зв'язку. Черга використовує локальне сховище Room з FIFO-порядком та механізмом гарантованої доставки exactly-once.
data class QueuedOperation(
val id: String,
val type: OperationType,
val endpoint: String,
val payload: String,
val timestamp: Long,
val retryCount: Int = 0,
val idempotencyKey: String
)
Кожна операція містить всі дані, необхідні для повторного надсилання: ендпоінт, тіло запиту, часову мітку та idempotencyKey. Room-БД гарантує збереження черги при перезапуску додатку та збоях ОС.
Гарантія доставки — основне завдання черги. Користувач повинен бути впевненим, що його дія (надсилання повідомлення, лайк, замовлення) буде виконана, навіть якщо мережа недоступна в момент виконання. Offline Queue з механізмом повторних спроб забезпечує доставку зрештою.
Покращення UX в умовах поганого зв'язку — за даними GSMA Mobile Economy Report (2025), близько 40% мобільних користувачів у світі мають нестабільне інтернет-з'єднання. Offline Queue робить додаток придатним для використання в метро, ліфтах, віддалених районах — усюди, де зв'язок переривчастий.
Зменшення втрати даних — без черги всі дії, виконані в офлайні, втрачаються. Користувач може заповнити довгу форму, натиснути «Надіслати» і побачити помилку мережі — весь ввід пропадає. Offline Queue зберігає дані та надсилає їх при першій нагоді. Автоматичне збереження в Google Docs — класичний приклад офлайн-черги для документів.
Асинхронна синхронізація — черга дозволяє додатку не блокувати інтерфейс під час надсилання. Користувач продовжує роботу, а менеджер синхронізації обробляє чергу у фоні. Це відповідає принципам Реактивної архітектури та покращує чуйність інтерфейсу.
Три шари черги: сховище (persistence), диспетчер (scheduler) та процесор (executor). Сховище — Room з таблицею QueuedOperation. Диспетчер — WorkManager (Android) або BGTaskScheduler (iOS), який запускає синхронізацію при появі мережі. Процесор — послідовний ітератор FIFO, що надсилає операції по одній.
Порядок обробки — критично важливий для консистентності даних. Якщо користувач створив запис, а потім відредагував його, обидві операції повинні надіслатися в тому ж порядку. Інакше сервер спочатку отримає оновлення неіснуючого запису — помилка. Послідовний FIFO — строгий порядок з контролем залежностей між операціями.
Стратегія злиття — якщо в черзі є CREATE і одразу за ним DELETE одного об'єкта, можна видалити обидві операції без надсилання: кінцевий стан — об'єкт не створено. Аналогічно, CREATE + UPDATE можна злити в один CREATE з останніми даними. Оптимізація черги скорочує кількість HTTP-запитів та прискорює синхронізацію.
За даними Android Developers (2025), WorkManager — найкращий спосіб обробки Offline Queue на Android: він гарантує виконання навіть після перезапуску пристрою, підтримує обмеження на наявність мережі та дозволяє задати політику повторних спроб через NetworkType.CONNECTED.
class SyncWorker(
private val context: Context,
private val params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result = runCatching {
queueRepository.processNextBatch(batchSize = 10)
Result.success()
}.getOrDefault(Result.retry())
}
CoroutineWorker обробляє батчі операцій та повертає Result.retry() при збої — WorkManager автоматично повторює запуск з експоненційною затримкою. Це найпростіший спосіб отримати надійну Offline Queue на Android.
Exponential Backoff — стандартна стратегія повторів зі збільшенням інтервалу: 2 сек, 4 сек, 8 сек, 16 сек і так далі до максимального порогу. Це запобігає повторному перевантаженню сервера, якщо він тимчасово недоступний. Java-бібліотека Resilience4j (2024) надає готову реалізацію Retry з налаштовуваним backoff.
Максимальна кількість спроб — критичний параметр. Якщо після 5–10 спроб операція не вдалася, подальші повтори марні та неефективні. Рекомендується dead letter queue: після вичерпання спроб операція переміщується в окрему таблицю для ручного аналізу. За даними Microsoft Patterns & Practices (2024), dead letter queue спрощує налагодження проблем синхронізації та запобігає блокуванню черги помилковими операціями.
Jitter — випадкова варіація — додавання випадкового числа до інтервалу backoff. Якщо у тисячі пристроїв одночасно з'явилася мережа після відключення, всі вони почнуть синхронізацію одночасно. Jitter розносить їх у часі, запобігаючи Cache Stampede на сервері. Повний jitter: delay = random(0, backoff) — рекомендований AWS (2024) для API-клієнтів.
Last Write Wins (LWW) — найпростіша стратегія: при конфлікті перемагає операція з пізнішою часовою міткою. LWW вимагає синхронізації часу — часова мітка повинна генеруватися на сервері або використовувати логічний годинник (Lamport clocks). Мінус: дані одного користувача можуть бути перезаписані даними іншого без попередження.
OT (Operational Transformation) — алгоритм, який використовують Google Docs та Figma для спільного редагування в реальному часі, включаючи офлайн-режим. OT перетворює операції так, щоб вони застосовувалися до будь-якого стану документа, забезпечуючи консистентність без блокувань. CRDT (Conflict-Free Replicated Data Types) — альтернатива OT, що набирає популярність у мобільних додатках: дані структуровані так, що конфлікти вирішуються математично, без центрального сервера.
Користувацьке злиття — для додатків з простою моделлю даних (нотатки, контакти) можна реалізувати власні правила злиття. Наприклад, для нотатки: якщо текст змінено у двох версіях, об'єднати їх як конкатенацію з роздільником. Конфлікт, вирішений користувачем — якщо автоматичне злиття неможливе, показати користувачу обидві версії та запропонувати вибрати. Dropbox (2024) використовує такий підхід для конфліктів у офлайн-файлах, створюючи копії з префіксом «Conflicted Copy».
Idempotency Key — унікальний ідентифікатор операції, який сервер використовує для виявлення дублюючих запитів. Якщо клієнт надсилає той самий запит з тим самим ключем, сервер повертає результат вже виконаної операції, не виконуючи її повторно. Це критично важливо для Offline Queue, де можливі повторні надсилання при мережевих помилках.
Формат idempotency key — UUID або хеш від параметрів запиту. Сервер повинен зберігати виконані ключі разом з результатом протягом деякого часу (зазвичай 24 години) для виявлення дублікатів. Stripe API (2024) — еталонний приклад: ключ передається в заголовку Idempotency-Key, і повторні запити з тим самим ключем повертають кешовану відповідь.
Генерація на клієнті — ключ створюється на клієнті до надсилання операції та зберігається в таблиці QueuedOperation. При повторній спробі ключ не змінюється. Архітектура exactly-once — комбінація idempotency key на клієнті та дедуплікації на сервері — єдиний спосіб гарантувати, що операція не буде виконана двічі.
fun createOperation(type: OperationType, payload: String): QueuedOperation =
QueuedOperation(
id = UUID.randomUUID().toString(),
type = type,
endpoint = type.endpoint,
payload = payload,
timestamp = currentTimeMillis(),
idempotencyKey = UUID.randomUUID().toString()
)
Кожна операція отримує два UUID: один — ідентифікатор запису в черзі, другий — idempotency key для сервера. Серверна дедуплікація за idempotencyKey гарантує, що навіть при повторному надсиланні замовлення не буде дубльовано.
Часті запитання
Кеш зберігає копії даних для швидкого читання в офлайні. Offline Queue зберігає операції користувача для подальшого запису на сервер. Кеш працює на читання, черга — на запис. Обидва компоненти можуть співіснувати в offline-first архітектурі.
Рекомендований ліміт — 100–500 операцій. Більше — ризик переповнення пам'яті та довгої синхронізації при відновленні мережі. При перевищенні ліміту додаток повинен попередити користувача та запропонувати пріоритезувати операції. Розумне обмеження — 50 операцій на оновлення + 10 на створення.
Операції старші 7 днів з нульовим успіхом переміщуються в dead letter queue. Аналізуйте їх вручну: можливо, змінився API, і ендпоінт більше не існує. Автоматичне очищення — HealthCheck-задача щодня видаляє або архівує прострочені операції.
Використовуйте граф залежностей (DAG): кожна операція містить список parentOperationId, які повинні завершитися до її надсилання. Room-запит з ORDER BY parent поверне операції в правильній послідовності. Каскадне надсилання — після завершення кожної операції перевіряйте, чи не розблоковані дочірні.
Використовуйте Network Less Tool в Android Emulator або Network Link Conditioner в iOS Simulator для емуляції втрати мережі. Пишіть тести, які додають операції в чергу в офлайн-режимі, відновлюють з'єднання та перевіряють, що всі операції надіслано та оброблено сервером.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.