Offline Queue: اصول، راهبردها و مکانیسم‌های کار

نویسنده: IT Sectr منتشر شده: 2026-06-13 زمان مطالعه: 10 دقیقه

Offline Queue مکانیسمی است که عملیات کاربر را در طور محلی ذخیره می‌کند هنگامی که دستگاه آفلاین است، و پس از بازگشت اتصال آن‌ها را به سرور ارسال می‌کند. بدون صف آفلاین، کاربر تمام اقداماتی را که بدون اینترنت انجام داده از دست می‌دهد، که در نرم‌افزارهای موبایل قابل قبول نیست. به گزارش Google Developers (2025)، پیاده‌سازی معماری offline-first نگهداشت کاربران را در مناطق با اینترنت ناپایدار تا 30% افزایش می‌دهد.

نکات کلیدی

  • Offline Queue — صف FIFO از عملیات‌هایی که کاربر بدون اینترنت انجام می‌دهد برای همگام‌سازی بعدی.
  • Persistent storage — صف در پایگاه داده محلی (SQLite, Room) برای حفظ در طول راه‌اندازی مجدد نرم‌افزار ذخیره می‌شود.
  • Exponential backoff — راهبرد تکرار تلاش با فاصله افزایشی در صورت شکست ارسال.
  • Conflict resolution — مکانیسم حل تضاد هنگامی که تغییرات آفلاین با داده‌های سرور تضاد دارند.
  • Idempotency keys — کلیدهای منحصربه‌فرد عملیات برای جلوگیری از دوباره‌سازی در سرور در ارسال مجدد.

صف آفلاین چیست؟

Offline Queue یک کلکشن مرتب از عملیات‌ها (ایجاد، به‌روزرسانی، حذف) است که نرم‌افزار در طور محلی ذخیره می‌کند هنگامی که دستگاه به شبکه دسترسی ندارد. به محض بازگشت اتصال، صف عملیات را به همان ترتیبی که کاربر انجام داده به سرور ارسال می‌کند.

سناریویی را تصور کنید: کاربر پیام‌رسان در مترو بدون اینترنت پیام می‌نویسد. هر فشار دادن دکمه «ارسال» به Offline Queue اضافه می‌شود. هنگامی که قطار از تونل خارج شده و شبکه منظر شود، تمام پیام‌ها خودکار ارسال می‌شوند. تجربه کاربری بدون وقفه است: او متوجه نمی‌شود که آفلاین بوده، جز تاخیر کمی در ارسال.

به گزارش Uber Engineering (2024)، صف آفلاین آن‌ها روزانه بیش از 2 میلیون عملیات را در مناطق با کیفیت پایین ارتباط پردازش می‌کند. صف از ذخیره‌سازی محلی Room با ترتیب FIFO و مکانیسم تحویل ضمانی exactly-once استفاده می‌کند.

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

هر عملیات شامل تمام داده‌های لازم برای ارسال مجدد است: endpoint، بدنه درخواست، تایم‌استمپ و 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 را فراهم می‌کند.

kotlin
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 و retry policy

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 keys — محافظت در برابر دوباره‌سازی

Idempotency Key — یک شناسه منحصربه‌فرد عملیات است که سرور برای تشخیص درخواست‌های تکراری استفاده می‌کند. اگر مشتری همان درخواست را با همان کلید ارسال کند، سرور نتیجه عملیات قبلاً انجام شده را بازمی‌گرداند، بدون انجام مجدد. این برای Offline Queue که ارسال‌های مکرر در خطاهای شبکه ممکن است، حیاتی است.

فرمات idempotency key — UUID یا هاش پارامترهای درخواست. سرور باید کلیدهای انجام شده را همراه با نتیجه برای مدت زمانی (معمولاً 24 ساعت) ذخیره کند. Stripe API (2024) — یک مثال ارجاعی: کلید در هدر Idempotency-Key ارسال می‌شود و درخواست‌های تکراری با همان کلید پاسخ ذخیره شده را بازمی‌گردانند.

تولید در طرف مشتری — کلید در طرف مشتری قبل از ارسال عملیات تولید و در جدول QueuedOperation ذخیره می‌شود. در تلاش مجدد، کلید تغییر نمی‌کند. معماری exactly-once — ترکیب کلید idempotency در طرف مشتری و ددوپلیکیسیون در سرور — تنها راه تضمین اینکه عملیات دو بار انجام نشود.

kotlin
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 Queue عملیات کاربر را برای نوشتن به سرور در آینده ذخیره می‌کند. کش برای خواندن و صف برای نوشتن کار می‌کند. هر دو می‌توانند در یک معماری offline-first با هم وجود داشته باشند.

چه اندازه صفی برای دستگاه موبایل ایمن است؟

حد توصیه شده — 100–500 عملیات. بیشتر — ریسک سرریز حافظه و همگام‌سازی طولانی بعد از بازگشت شبکه. در صورت تجاوز از حد، نرم‌افزار باید به کاربر هشدار دهد و اولویت‌بندی عملیات را پیشنهاد کند. محدودیت معقول — 50 عملیات برای به‌روزرسانی + 10 برای ایجاد.

چگونه عملیات‌های منقضی را در صف مدیریت کنیم؟

عملیات‌های بیش از 7 روز با موفقیت صفر به dead letter queue منتقل می‌شوند. آن‌ها را دستی تحلیل کنید: احتمالاً API تغییر کرده و endpoint دیگر وجود ندارد. پاک‌سازی خودکار — وظیفه HealthCheck یکبار در روز عملیات‌های منقضی را حذف یا بایگانی می‌کند.

اگر عملیاتی به عملیات قبلی که هنوز ارسال نشده وابسته است، چه کنیم؟

از گراف وابستگی (DAG) استفاده کنید: هر عملیات شامل فهرستی از parentOperationId است که باید قبل از ارسال آن تکمیل شوند. استفسار Room با ORDER BY parent عملیات را به ترتیب درست بازمی‌گرداند. ارسال سلسله‌ای — پس از تکمیل هر عملیات، بررسی کنید که آیا عملیات‌های فرزند آزاد شده‌اند.

چگونه Offline Queue را آزمایش کنیم؟

از Network Less Tool در Android Emulator یا Network Link Conditioner در iOS Simulator برای شبیه‌سازی از دست رفتن شبکه استفاده کنید. تست‌هایی بنویسید که عملیات را در حالت آفلاین به صف اضافه می‌کنند، اتصال را بازیابی کنند و بررسی کنند که تمام عملیات ارسال و توسط سرور پردازش شده‌اند.

نتیجه‌گیری

  • Offline Queue — صف عملیات FIFO که به صورت محلی برای ارسال پس از بازگشت اتصال ذخیره می‌شود.
  • Persistent storage (Room / SQLite) — برای حفظ صف در طول راه‌اندازی مجدد نرم‌افزار الزامی است.
  • Exponential backoff با jitter — راهبرد استاندارد تکرار برای جلوگیری از بارگیری سرور.
  • Conflict resolution — LWW، OT، CRDT یا قوانین سفارشی برای حل تضادهای داده‌های آفلاین.
  • Idempotency key — UUID هر عملیات برای تضمین exactly-once در سرور.
  • Dead letter queue — جداسازی عملیات‌های مشکل‌دار پس از تمام شدن تلاش‌ها برای تحلیل دستی.
  • بهترین رویه برای Android — WorkManager + Room + ExponentialBackoff — ترکیب تایید شده Google.

ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد

IT Sectr از سال 2017 برنامه‌های iOS و Android را برای استارتاپ‌ها و کسب‌وکارها ایجاد می‌کند. ما به شما مشاوره می‌دهیم و بهترین راه‌حل را پیشنهاد خواهیم کرد.

بحث درباره پروژه

همچنین بخوانید