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
)

Κάθε λειτουργία περιέχει όλα τα δεδομένα που απαιτούνται για επαναποστολή: τελικό σημείο, σώμα αιτήματος, χρονική σήμανση και idempotencyKey. Room-ΒΔ εγγυάται τη διατήρηση της ουράς κατά την επανεκκίνηση της εφαρμογής και αποτυχίες ΛΣ.

Γιατί χρειάζεται ουρά λειτουργιών σε εφαρμογή για κινητά

Εγγύηση παράδοσης — ο κύριος σκοπός της ουράς. Ο χρήστης πρέπει να είναι σίγουρος ότι η ενέργειά του (αποστολή μηνύματος, like, παραγγελία) θα εκτελεστεί, ακόμα κι αν το δίκτυο δεν είναι διαθέσιμο τη στιγμή της εκτέλεσης. Η Offline Queue με μηχανισμό retry εξασφαλίζει παράδοση eventually.

Βελτίωση UX σε συνθήκες κακής σύνδεσης — σύμφωνα με GSMA Mobile Economy Report (2025), περίπου το 40% των χρηστών κινητών παγκοσμίως έχουν ασταθή σύνδεση στο διαδίκτυο. Η Offline Queue καθιστά την εφαρμογή χρησιμοποιήσιμη σε μετρό, ανελκυστήρες, απομακρυσμένες περιοχές — παντού όπου η σύνδεση είναι διακοπτόμενη.

Μείωση απώλειας δεδομένων — χωρίς ουρά, όλες οι ενέργειες που γίνονται εκτός σύνδεσης χάνονται. Ο χρήστης μπορεί να συμπληρώσει μια μεγάλη φόρμα, να πατήσει «Αποστολή» και να δει σφάλμα δικτύου — όλη η είσοδος χάνεται. Η Offline Queue αποθηκεύει τα δεδομένα και τα στέλνει με την πρώτη ευκαιρία. Auto-save στο Google Docs — κλασικό παράδειγμα ουράς εκτός σύνδεσης για έγγραφα.

Ασύγχρονος συγχρονισμός — η ουρά επιτρέπει στην εφαρμογή να μην μπλοκάρει το UI κατά την αποστολή. Ο χρήστης συνεχίζει να εργάζεται, ενώ ο διαχειριστής συγχρονισμού επεξεργάζεται την ουρά στο παρασκήνιο. Αυτό συμμορφώνεται με τις αρχές Reactive Architecture και βελτιώνει την ανταπόκριση της διεπαφής.

Αρχιτεκτονική ουράς εκτός σύνδεσης: αποθήκευση και επεξεργασία

Τρία επίπεδα ουράς: αποθήκη (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: εγγυάται εκτέλεση ακόμα και μετά από επανεκκίνηση συσκευής, υποστηρίζει constraints για ύπαρξη δικτύου και επιτρέπει ορισμό πολιτικής επαναληπτικών προσπαθειών μέσω 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

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 απαιτεί συγχρονισμό χρόνου — η χρονική σήμανση πρέπει να δημιουργείται στον διακομιστή ή να χρησιμοποιεί Logical Clock (Lamport clocks). Μειονέκτημα: δεδομένα ενός χρήστη μπορεί να αντικατασταθούν από δεδομένα άλλου χωρίς προειδοποίηση.

OT (Operational Transformation) — αλγόριθμος που χρησιμοποιείται από Google Docs και Figma για συνεργατική επεξεργασία σε πραγματικό χρόνο, συμπεριλαμβανομένης της λειτουργίας εκτός σύνδεσης. Το OT μετασχηματίζει λειτουργίες ώστε να εφαρμόζονται σε οποιαδήποτε κατάσταση εγγράφου, εξασφαλίζοντας συνέπεια χωρίς κλειδώματα. CRDT (Conflict-Free Replicated Data Types) — εναλλακτική του OT, που κερδίζει δημοτικότητα σε εφαρμογές για κινητά: τα δεδομένα είναι δομημένα ώστε οι συγκρούσεις να επιλύονται μαθηματικά, χωρίς κεντρικό διακομιστή.

Custom Merge — για εφαρμογές με απλό μοντέλο δεδομένων (σημειώσεις, επαφές) μπορούν να υλοποιηθούν προσαρμοσμένοι κανόνες συγχώνευσης. Για παράδειγμα, για μια σημείωση: αν το κείμενο έχει αλλάξει σε δύο εκδόσεις, να συνδυαστούν ως παράθεση με διαχωριστικό. Σύγκρουση επιλυόμενη από χρήστη — αν η αυτόματη συγχώνευση είναι αδύνατη, εμφανίστε και τις δύο εκδόσεις στον χρήστη και προτείνετε να επιλέξει. Η Dropbox (2024) χρησιμοποιεί αυτήν την προσέγγιση για συγκρούσεις σε αρχεία εκτός σύνδεσης, δημιουργώντας αντίγραφα με πρόθεμα “Conflicted Copy”.

Idempotency keys — προστασία από διπλοεγγραφή

Idempotency Key — μοναδικό αναγνωριστικό λειτουργίας που χρησιμοποιεί ο διακομιστής για ανίχνευση διπλότυπων αιτημάτων. Αν ο πελάτης στείλει το ίδιο αίτημα με το ίδιο κλειδί, ο διακομιστής επιστρέφει το αποτέλεσμα της ήδη εκτελεσμένης λειτουργίας χωρίς να την εκτελέσει ξανά. Αυτό είναι κρίσιμο για την Offline Queue, όπου είναι πιθανές επαναποστολές σε σφάλματα δικτύου.

Μορφή idempotency key — UUID ή hash των παραμέτρων αιτήματος. Ο διακομιστής πρέπει να αποθηκεύει τα εκτελεσμένα κλειδιά μαζί με το αποτέλεσμα για κάποιο χρονικό διάστημα (συνήθως 24 ώρες) για ανίχνευση διπλότυπων. Το Stripe API (2024) — υποδειγματικό παράδειγμα: το κλειδί μεταφέρεται στην κεφαλίδα Idempotency-Key και επαναλαμβανόμενα αιτήματα με το ίδιο κλειδί επιστρέφουν προσωρινά αποθηκευμένη απάντηση.

Δημιουργία από πελάτη — το κλειδί δημιουργείται στον πελάτη πριν από την αποστολή της λειτουργίας και αποθηκεύεται στον πίνακα QueuedOperation. Σε επαναπροσπάθεια, το κλειδί δεν αλλάζει. Αρχιτεκτονική exactly-once — συνδυασμός idempotency key στον πελάτη και απαλοιφής διπλότυπων στον διακομιστή — ο μόνος τρόπος να εγγυηθείτε ότι μια λειτουργία δεν θα εκτελεστεί δύο φορές.

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 άλλαξε και το τελικό σημείο δεν υπάρχει πλέον. Αυτόματος καθαρισμός — εργασία 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 δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης