Request Deduplication: τι είναι, μέθοδοι και μηχανισμοί λειτουργίας

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-06-13 Χρόνος ανάγνωσης: 9 λεπ

Request Deduplication — είναι ένας μηχανισμός συνδυασμού ταυτόσημων παράλληλων αιτημάτων σε ένα, ώστε η πηγή δεδομένων να λαμβάνει μόνο μία κλήση αντί για δεκάδες. Σε εφαρμογές κινητών, η απαλοιφή διπλοτύπων είναι ιδιαίτερα σημαντική: πολλές οθόνες μπορούν ταυτόχρονα να ζητούν το ίδιο προφίλ χρήστη ή λίστα προϊόντων. Σύμφωνα με το Square Engineering (2024), η εφαρμογή της απαλοιφής διπλοτύπων μείωσε το φορτίο του API τους κατά 30% χωρίς να αλλάξει τη λογική του διακομιστή.

Βασικά σημεία

  • Request Deduplication — τεχνική κατά την οποία τα διπλότυπα αιτήματα συνδυάζονται σε ένα, και το αποτέλεσμα αποστέλλεται σε όλους τους ενεργοποιητές.
  • Memoization — προσωρινή αποθήκευση του αποτελέσματος του αιτήματος κατά τη διάρκεια εκτέλεσης· οι επαναλαμβανόμενες κλήσεις λαμβάνουν έτοιμο αντικείμενο.
  • Request Merging — συνδυασμός πολλών αιτημάτων διαφορετικών δεδομένων σε ένα batch αίτημα προς τον διακομιστή.
  • DataLoader — βιβλιοθήκη από το GraphQL, που υλοποιεί batched request deduplication στον διακομιστή.
  • Χρονικό όριο παραθύρου — μικρή καθυστέρηση (10–50 ms) για τη συλλογή μιας ομάδας διπλότυπων αιτημάτων πριν την αποστολή.

Τι είναι η απαλοιφή διπλοτύπων αιτημάτων;

Request Deduplication — είναι μια τεχνική που αποτρέπει την εκτέλεση πολλών ταυτόσημων αιτημάτων προς μία πηγή δεδομένων εντός ενός χρονικού παραθύρου. Αντί να στέλνει 10 ταυτόσημα αιτήματα HTTP, το σύστημα στέλνει ένα, και τα υπόλοιπα 9 περιμένουν το αποτέλεσμά του.

Το πρόβλημα των διπλότυπων αιτημάτων είναι ιδιαίτερα έντονο σε εφαρμογές κινητών με αρχιτεκτονική βασισμένη σε καταστάσεις (MVVM, MVI, Redux). Όταν πολλοί παρατηρητές εγγράφονται στα ίδια δεδομένα μέσα σε σύντομο χρονικό διάστημα, κάθε ένας εκκινεί το δικό του αίτημα, δημιουργώντας υπερβολικό φορτίο. Σύμφωνα με το Uber Engineering (2024), έως και 18% όλων των αιτημάτων στους κινητούς πελάτες της Uber είναι διπλότυπα, και η απαλοιφή διπλοτύπων στον πελάτη μείωσε τον αριθμό τους κατά 4 φορές.

Η απαλοιφή διπλοτύπων δεν είναι το ίδιο με την αποθήκευση στην κρυφή μνήμη. Η κρυφή μνήμη αποθηκεύει το αποτέλεσμα του αιτήματος μετά την εκτέλεσή του. Η απαλοιφή διπλοτύπων αποτρέπει τα υπερβολικά αιτήματα πριν και κατά τη διάρκεια της εκτέλεσής τους. Μετά την ολοκλήρωση του αιτήματος, η κρυφή μνήμη τίθεται σε λειτουργία και αποθηκεύει το αποτέλεσμα.

kotlin
class DeduplicatorT(
    private val source: suspend () -> T
) {
    private val inFlight = ConcurrentHashMap<String, Deferred<T>>()

    suspend fun get(key: String): T = inFlight.getOrPut(key) {
        async {
            source().also { inFlight.remove(key) }
        }
    }.await()
}

Αυτή η κλάση Kotlin εγγυάται ότι για κάθε κλειδί εκτελείται μόνο μία κορουτίνα. Όλες οι παράλληλες κλήσεις με το ίδιο κλειδί περιμένουν ένα Deferred. Μετά την ολοκλήρωση, το κλειδί διαγράφεται και το επόμενο αίτημα εκτελείται κανονικά.

Γιατί χρειάζεται η απαλοιφή διπλοτύπων σε εφαρμογές κινητών

Μείωση φορτίου διακομιστή — ο πρώτος και πιο προφανής λόγος. Κάθε διπλότυπο αίτημα καταναλώνει πόρους του διακομιστή: CPU, μνήμη, συνδέσεις βάσης δεδομένων. Σε κλίμακα εκατομμυρίων συσκευών, ακόμα και 10–15% διπλότυπα αιτήματα δημιουργούν σημαντικό φορτίο που απαιτεί επιπλέον διακομιστές.

Εξοικονόμηση μπαταρίας και κίνησης δεδομένων — κάθε αίτημα HTTP σε μια κινητή συσκευή καταναλώνει ενέργεια από τη μονάδα ραδιοφώνου. Σύμφωνα με το Google I/O (2025), ένα αποτυχημένο ή διπλότυπο αίτημα μπορεί να καταναλώσει έως και 15% της ενέργειας μιας συνεδρίας δικτύου. Η απαλοιφή διπλοτύπων μειώνει τον αριθμό ενεργοποίησης της μονάδας ραδιοφώνου, παρατείνοντας τον χρόνο λειτουργίας της συσκευής με μπαταρία.

Αποφυγή συγκρούσεων δεδομένων — αν δύο διπλότυπα αιτήματα εγγράφουν δεδομένα στην τοπική αποθήκευση, μπορεί να προκύψουν race conditions: το δεύτερο αίτημα μπορεί να αντικαταστήσει το αποτέλεσμα του πρώτου με παλιά δεδομένα. Η απαλοιφή διπλοτύπων εγγυάται ότι η εγγραφή στην τοπική αποθήκευση γίνεται μία φορά, εξαλείφοντας τις συνθήκες ανταγωνισμού.

Βελτίωση UX — ο χρήστης δεν βλέπει πολλαπλούς δείκτες φόρτωσης για τα ίδια δεδομένα. Η κατάσταση UI (loading / success / error) διαχειρίζεται από μία μοναδική πηγή αλήθειας, όχι από πολλά ανταγωνιστικά αιτήματα.

Memoization — αποθήκευση στην κρυφή μνήμη

Memoization (μνημόνευση) — είναι η προσωρινή αποθήκευση του αποτελέσματος μιας συνάρτησης κατά τη διάρκεια της εκτέλεσής της. Αν η συνάρτηση εκτελείται ήδη με τα ίδια ορίσματα, η νέα κλήση δεν εκκινεί μια δεύτερη διεργασία, αλλά λαμβάνει το αποτέλεσμα της πρώτης. Αυτή είναι η απλούστερη μορφή απαλοιφής διπλοτύπων για σενάρια εντός διεργασίας.

Τυπική υλοποίηση σε εφαρμογές κινητών — HashMap κλειδιών σε Deferred ή Promise. Το κλειδί είναι συνήθως το URL string του αιτήματος ή η συνένωση παραμέτρων. Η διάρκεια ζωής της εγγραφής — από το πρώτο αίτημα έως την ολοκλήρωση της απόκρισης. Σύμφωνα με το Dropbox Engineering (2024), η μνημόνευση στον κινητό πελάτη του Dropbox μείωσε τον αριθμό των διπλότυπων αιτημάτων προς το API κατά 40%.

Flawed deduplication — ένα επικίνδυνο λάθος: αν δεν διαγράψετε το κλειδί μετά από ένα σφάλμα, όλα τα επόμενα αιτήματα θα επιστρέφουν πάντα το ίδιο σφάλμα. Μια σωστή υλοποίηση πρέπει να χειρίζεται σφάλματα και αποτυχίες, καθαρίζοντας την κρυφή μνήμη και επιτρέποντας επαναληπτική δοκιμή.

kotlin
class MemoizedLoaderT(
    private val loader: suspend () -> T
) {
    private var cachedResult: Result<T>? = null

    suspend fun get(): T = cachedResult ?.getOrThrow() ?: run {
        loader().let {
            Result.success(it)
        }.also { cachedResult = it }
    }.await()
}

Το MemoizedLoader χρησιμοποιεί το Result<T> για σωστό χειρισμό σφαλμάτων: σε επιτυχία — αποθηκεύει, σε σφάλμα — επιτρέπει επαναληπτική δοκιμή. Αυτή η προσέγγιση εγγυάται ότι μια προσωρινή αποτυχία δικτύου δεν θα αποκλείσει τα επόμενα αιτήματα.

Request Merging — συνδυασμός σε batch

Request Merging (συνδυασμός αιτημάτων) — τεχνική κατά την οποία πολλά διαφορετικά αιτήματα προς την ίδια πηγή συλλέγονται σε μια ομάδα και αποστέλλονται ως ένα batch αίτημα. Σε αντίθεση με την απαλοιφή διπλοτύπων, τα αιτήματα δεν είναι ταυτόσημα — διαφέρουν στις παραμέτρους, αλλά αναφέρονται στον ίδιο πόρο.

Τυπικό σενάριο: 5 οθόνες της εφαρμογής ζητούν προφίλ διαφορετικών χρηστών. Αντί για 5 μεμονωμένα αιτήματα στα /api/users/1, /api/users/2 κ.λπ., το σύστημα περιμένει 20 ms, συλλέγει όλα τα ID και στέλνει ένα αίτημα /api/users?ids=1,2,3,4,5. Χρονικό όριο παραθύρου — βασική παράμετρος: πολύ μεγάλο παράθυρο χειροτερεύει το UX, πολύ μικρό δεν επιτρέπει τη συλλογή αρκετών αιτημάτων.

Σύμφωνα με το Netflix Engineering (2023), στον συγκεντρωτή GraphQL BFF (Backend for Frontend), ο συνδυασμός αιτημάτων μείωσε τον αριθμό των κλήσεων HTTP μεταξύ στρωμάτων κατά 65% και τον μέσο χρόνο απόκρισης κατά 120 ms με την εξάλειψη των περιττών RTT. Ασύγχρονο παράθυρο (debounce) — τυπική υλοποίηση μέσω coroutines ή RxJava.

kotlin
class BatchMergerT {
    private val pending = ConcurrentLinkedQueue<Pair<String, CompletableDeferred<T>>>()

    suspend fun get(id: String): T = suspendCoroutine { cont ->
        pending.add(Pair(id, cont))
        scheduleFlush()
    }
}

Αυτό το mixin χρησιμοποιεί το suspendCoroutine για την αναστολή κάθε αιτήματος και ένα παράθυρο 30 ms για τη συλλογή της ομάδας. Μετά τη λήξη του χρονομετρητή, όλα τα συλλεγμένα ID στέλνονται με ένα batch αίτημα και κάθε coroutine λαμβάνει το δικό της αποτέλεσμα.

Απαλοιφή διπλοτύπων διακομιστή μέσω DataLoader

DataLoader — είναι μια βιβλιοθήκη (αρχικά για JavaScript/GraphQL) που υλοποιεί batching και μνημόνευση στην πλευρά του διακομιστή. Ομαδοποιεί όλα τα αιτήματα προς την ίδια πηγή δεδομένων μέσα σε ένα tick του event loop και τα εκτελεί με μία κλήση. Το DataLoader χρησιμοποιείται ευρέως με το GraphQL, αλλά μπορεί να εφαρμοστεί σε οποιαδήποτε εφαρμογή REST.

Αρχή λειτουργίας: όλες οι κλήσεις loader.load(id) μέσα σε ένα μικροκαθήκον συλλέγονται σε έναν πίνακα ID και μεταφέρονται στη συνάρτηση batch. Μετά τη λήψη των αποτελεσμάτων, κάθε ID λαμβάνει το δικό του στοιχείο του πίνακα. Η αποθήκευση στην κρυφή μνήμη στο DataLoader λειτουργεί μόνο μέσα σε ένα αίτημα HTTP — στο επόμενο αίτημα, η κρυφή μνήμη επαναφέρεται, εγγυώμενη την επικαιρότητα των δεδομένων.

Σύμφωνα με το Meta Engineering (2024), η εφαρμογή του DataLoader στο στρώμα GraphQL του Facebook εξάλειψε το πρόβλημα N+1, μειώνοντας τον αριθμό των ερωτημάτων βάσης δεδομένων από 200 σε 10 ανά τυπική σελίδα. Batch scheduling — η κεντρική καινοτομία του DataLoader — χρησιμοποιεί το process.nextTick (Node.js) ή DispatchQueue.main (iOS) για τη βελτιστοποίηση της ομαδοποίησης.

Ποια στρατηγική απαλοιφής διπλοτύπων να επιλέξετε

Memoization — βέλτιστη για μία διεργασία (εφαρμογή κινητού, μικρουπηρεσία). Απλή στην υλοποίηση και αποτελεσματική για ταυτόσημες παράλληλες κλήσεις. Μειονέκτημα — δεν λειτουργεί μεταξύ διεργασιών ή συσκευών.

Request Merging — κατάλληλο για στρώμα BFF ή υπηρεσία συγκέντρωσης. Απαιτεί υποστήριξη batch endpoints στον διακομιστή. Η καλύτερη επιλογή όταν το frontend κάνει πολλά μικρά αιτήματα σε διαφορετικά δεδομένα του ίδιου τύπου.

DataLoader — πρότυπο απαλοιφής διπλοτύπων για διακομιστές GraphQL. Λύνει αυτόματα το πρόβλημα N+1 και δεν απαιτεί μη αυτόματη ρύθμιση της κρυφής μνήμης. Συνιστάται για οποιονδήποτε διακομιστή με στρώμα GraphQL.

Cache HTTP με απαλοιφή διπλοτύπων — σε επίπεδο OkHttp (Android) ή URLSession (iOS) μπορεί να ρυθμιστεί η απαλοιφή διπλοτύπων μέσω Interceptor ή delegate. OkHttp CacheInterceptor — ένας προσαρμοσμένος αναχαιτιστής που ελέγχει αν ένα αίτημα με το ίδιο URL εκτελείται ήδη και τα συνδυάζει. Αυτή η μέθοδος λειτουργεί σε ένα επίπεδο κάτω από την επιχειρηματική λογική και καλύπτει όλα τα αιτήματα της εφαρμογής χωρίς να τροποποιεί τον κώδικα λειτουργικότητας.

Συχνές Ερωτήσεις

Τι διαφέρει η απαλοιφή διπλοτύπων από την αποθήκευση στην κρυφή μνήμη;

Η απαλοιφή διπλοτύπων αποτρέπει την εκτέλεση ενός διπλότυπου αιτήματος όσο το πρώτο εκτελείται ακόμα. Η αποθήκευση στην κρυφή μνήμη αποθηκεύει το αποτέλεσμα μετά την εκτέλεση. Συμπληρώνονται αμοιβαία: η απαλοιφή διπλοτύπων προστατεύει από επαναλαμβανόμενα αιτήματα κατά τη φόρτωση, η κρυφή μνήμη — από επαναλαμβανόμενα αιτήματα μετά.

Πότε μπορεί η απαλοιφή διπλοτύπων να βλάψει;

Αν το κλειδί απαλοιφής διπλοτύπων είναι λανθασμένο. Παράδειγμα, αν όλοι οι χρήστες χρησιμοποιούν ένα κλειδί, το πρώτο αίτημα θα αποκλείσει όλα τα υπόλοιπα. Το κλειδί πρέπει να είναι ειδικό: να περιλαμβάνει URL, παραμέτρους, ID χρήστη. Επίσης, η απαλοιφή διπλοτύπων μπορεί να κρύψει προβλήματα διακομιστή, κρύβοντας την πραγματική συχνότητα των αιτημάτων στις μετρικές.

Πώς να επιλέξω το χρονικό όριο παραθύρου για Request Merging;

Βέλτιστο παράθυρο — 20–50 ms για σενάρια χρήστη. Αυτό είναι αρκετό για τη συλλογή μιας ομάδας αιτημάτων, αλλά όχι αρκετό για να αντιληφθεί ο χρήστης την καθυστέρηση. Για λειτουργίες υποβάθρου (αρχεία καταγραφής, αναλυτικά), το παράθυρο μπορεί να αυξηθεί σε 200–500 ms. Εμπειρικός κανόνας: το παράθυρο δεν πρέπει να υπερβαίνει το 10% του χρόνου εκτέλεσης ενός αιτήματος.

Λειτουργεί η απαλοιφή διπλοτύπων με WebSocket;

Ναι, η αρχή είναι η ίδια: αν πολλά μέρη της εφαρμογής εγγράφονται στο ίδιο κανάλι WebSocket, ο απαλοιφέας διπλοτύπων ανοίγει μία σύνδεση και αποστέλλει μηνύματα σε όλους τους συνδρομητές. RxJava Share ή Kotlin SharedFlow — ιδανικά εργαλεία για την απαλοιφή διπλοτύπων μηνυμάτων WebSocket στον πελάτη.

Πώς να δοκιμάσω την απαλοιφή διπλοτύπων;

Χρησιμοποιήστε το MockWebServer (OkHttp) για Android ή τα OHHTTPStubs για iOS. Εκκινήστε 10 παράλληλα αιτήματα με τις ίδιες παραμέτρους και ελέγξτε ότι ο διακομιστής έλαβε ακριβώς μία κλήση. CountDownLatch ή coroutineScope θα βοηθήσουν στον συγχρονισμό παράλληλων κλήσεων στη δοκιμή.

Σύνοψη

  • Request Deduplication — συνδυασμός ταυτόσημων παράλληλων αιτημάτων σε ένα με αποστολή του αποτελέσματος σε όλους τους ενεργοποιητές.
  • Memoization — προσωρινή αποθήκευση του αποτελέσματος κατά την εκτέλεση· απλή και αποτελεσματική μέθοδος για μία διεργασία.
  • Request Merging — συλλογή ομάδας διαφορετικών αιτημάτων σε batch· απαιτεί υποστήριξη διακομιστή και χρονικό όριο παραθύρου.
  • DataLoader — πρότυπο απαλοιφής διπλοτύπων για GraphQL· λύνει το πρόβλημα N+1 σε επίπεδο διακομιστή.
  • Έως και 18% των αιτημάτων σε εφαρμογές κινητών είναι διπλότυπα· η απαλοιφή διπλοτύπων μειώνει το φορτίο διακομιστή και μπαταρίας.
  • Το κλειδί απαλοιφής διπλοτύπων πρέπει να είναι ειδικό: να περιλαμβάνει URL, παραμέτρους και περιβάλλον χρήστη.
  • Βέλτιστη πρακτική — συνδυασμός απαλοιφής διπλοτύπων στην πλευρά του πελάτη (OkHttp Interceptor / URLSession) και του διακομιστή (DataLoader).

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

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

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