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 са механизмом retry обезбеђује доставу eventually.
Побољшање UX у условима лоше везе — према GSMA Mobile Economy Report (2025), око 40% мобилних корисника у свету има нестабилну интернет везу. Offline Queue чини апликацију употребљивом у метроу, лифтовима, удаљеним областима — свуда где је веза испрекидана.
Смањење губитка података — без реда, све радње обављене ван мреже се губе. Корисник може попунити дугачак образац, притиснути „Пошаљи“ и видети грешку мреже — сав унос нестаје. Offline Queue чува податке и шаље их при првој прилици. Аутоматско чување у Google Docs је класичан пример офлајн реда за документе.
Асинхрона синхронизација — ред омогућава апликацији да не блокира интерфејс током слања. Корисник наставља да ради, а менаџер синхронизације обрађује ред у позадини. Ово је у складу са принципима Реактивне архитектуре и побољшава одзивност интерфејса.
Три слоја реда: складиште (persistence), диспечер (scheduler) и процесор (executor). Складиште — Room са табелом QueuedOperation. Диспечер — WorkManager (Android) или BGTaskScheduler (iOS), који покреће синхронизацију када се појави мрежа. Процесор — секвенцијални FIFO итератор који шаље операције једну по једну.
Редослед обраде је критичан за конзистентност података. Ако је корисник креирао запис, а затим га уредио, обе операције морају бити послате истим редом. У супротном, сервер ће прво добити ажурирање непостојећег записа — грешка. Sequential FIFO — строги редослед са контролом зависности између операција.
Стратегија спајања — ако у реду постоје CREATE и одмах затим DELETE истог објекта, обе операције се могу обрисати без слања: крајње стање — објекат није креиран. Слично, CREATE + UPDATE CREATE се могу спојити у један 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 захтева синхронизацију времена — timestamp мора бити генерисан на серверу или користити Logical Clock (Lamport сатове). Недостатак: подаци једног корисника могу бити пребрисани подацима другог без упозорења.
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. године. Саветоваћемо вас и предложити најбоље решење.
Прочитајте такође