Request Deduplication: mi ez, módszerek és működési mechanizmusok

Szerző: IT Sectr Megjelenés: 2026-06-13 Olvasási idő: 9 perc

Request Deduplication — az azonos párhuzamos kérések egyetlen kéréssé egyesítésének mechanizmusa, így az adatforrás tucatnyi hívás helyett csak egy hívást kap. Mobilalkalmazásokban a deduplikáció különösen fontos: több képernyő egyszerre kérheti ugyanazt a felhasználói profilt vagy terméklistát. A Square Engineering (2024) szerint a deduplikáció bevezetése 30%-kal csökkentette az API terhelését anélkül, hogy megváltoztatta volna a szerver logikáját.

Főbb pontok

  • Request Deduplication — technika, amelynél a duplikálódó kérések egyetlen kéréssé egyesülnek, és az eredményt minden kezdeményező megkapja.
  • Memoization — a kérés eredményének gyorsítótározása a végrehajtás idejére; az ismételt hívások kész objektumot kapnak.
  • Request Merging — több különböző adat kérésének egyesítése egyetlen batch kérésbe a szerver felé.
  • DataLoader — a GraphQL könyvtára, amely batched request deduplication-t valósít meg a szerver oldalán.
  • Ablak időkorlát — rövid késleltetés (10–50 ms) a duplikálódó kérések csoportjának összegyűjtésére a küldés előtt.

Mi az a kérések deduplikációja?

Request Deduplication — olyan technika, amely megakadályozza több azonos kérés végrehajtását ugyanahhoz az adatforráshoz egy időablakon belül. Ahelyett, hogy 10 azonos HTTP kérést küldene, a rendszer egyet küld, a többi 9 pedig vár az eredményére.

A duplikálódó kérések problémája különösen éles a mobilalkalmazásokban állapotalapú architektúrával (MVVM, MVI, Redux). Amikor több megfigyelő rövid időn belül ugyanazokra az adatokra fizet elő, mindegyik elindítja a saját kérését, többletterhelést okozva. Az Uber Engineering (2024) szerint az Uber mobil klienseiben az összes kérés akár 18%-a duplikálódik, és a kliens oldali deduplikáció négyede csökkentette a számukat.

A deduplikáció nem ugyanaz, mint a gyorsítótárazás. A gyorsítótár a kérés eredményét a végrehajtás után tárolja. A deduplikáció megakadályozza a többletkéréseket a végrehajtás előtt és alatt. A kérés befejezése után a gyorsítótár lép működésbe és tárolja az eredményt.

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()
}

Ez a Kotlin osztály garantálja, hogy minden kulcshoz csak egy korutin hajódik végre. Az összes párhuzamos hívás ugyanazzal a kulccsal egy Deferred-re vár. Befejezés után a kulcs törlődik, és a következő kérés normálisan hajódik végre.

Miért van szükség deduplikációra a mobilalkalmazásokban

Szerverterhelés csökkentése — az első és legnyilvánvalóbb ok. Minden duplikálódó kérés szervererőforrásokat (CPU, memória, adatbázis kapcsolatok) fogyaszt. Több millió eszköz skáláján még 10–15% duplikálódó kérés is jelentős terhelést okoz, ami további szervereket igényel.

Akkumulátor és adatforgalom megtakarítása — minden HTTP kérés egy mobileszközön energiát fogyaszt a rádiómodulból. A Google I/O (2025) szerint egy sikertelen vagy duplikálódó kérés akár egy hálózati munkamenet energiájának 15%-át is felemésztheti. A deduplikáció csökkenti a rádiómodul bekapcsolásainak számát, meghosszabbítva az eszköz akkumulátoros üzemidejét.

Adatütközések elkerülése — ha két duplikálódó kérés adatokat ír a helyi tárolóba, versenyhelyzet (race condition) alakulhat ki: a második kérés felülírhatja az első eredményét elavult adatokkal. A deduplikáció garantálja, hogy a helyi tárolóba írás egyszer történik, kiküszöbölve a versenyhelyzeteket.

UX javítása — a felhasználó nem lát több betöltő jelzőt ugyanazokhoz az adatokhoz. Az UI állapotot (loading / success / error) egyetlen igazságforrás kezeli, nem több versengő kérés.

Memoization — gyorsítótárazás memóriában

Memoization (memorizálás) — a függvény eredményének gyorsítótárazása a végrehajtás idejére. Ha a függvény már fut ugyanazokkal az argumentumokkal, az új hívás nem indít második folyamatot, hanem megkapja az első eredményét. Ez a deduplikáció legegyszerűbb formája a folyamaton belüli forgatókönyvekhez.

Tipikus megvalósítás mobilalkalmazásokban — kulcsok HashMap-je Deferred-ben vagy Promise-ban. A kulcs általában a kérés URL stringje vagy a paraméterek összefűzése. A rekord élettartama — az első kéréstől a válasz befejeződéséig. A Dropbox Engineering (2024) szerint a memorizálás a Dropbox mobil kliensében 40%-kal csökkentette az API duplikálódó kéréseinek számát.

Flawed deduplication — veszélyes hiba: ha nem törli a kulcsot hiba után, az összes későbbi kérés örökre ugyanazt a hibát adja vissza. A helyes megvalósításnak kezelnie kell a hibákat és meghibásodásokat, törölnie kell a gyorsítótárat és engedélyeznie kell az újrapróbálkozást.

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()
}

A MemoizedLoader a Result<T> használja a helyes hibakezeléshez: siker esetén gyorsítótáraz, hiba esetén lehetővé teszi az újrapróbálkozást. Ez a megközelítés garantálja, hogy az átmeneti hálózati hiba nem blokkolja a későbbi kéréseket.

Request Merging — egyesítés batch-ben

Request Merging (kérések összevonása) — technika, amelynél több különböző kérés ugyanahhoz a forráshoz egy csoportba gyűlik és egyetlen batch kérésként kerül elküldésre. A deduplikációval ellentétben a kérések nem azonosak — paramétereikben különböznek, de ugyanarra az erőforrásra hivatkoznak.

Tipikus forgatókönyv: az alkalmazás 5 képernyője különböző felhasználók profiljait kéri. 5 egyedi kérés helyett a /api/users/1, /api/users/2 stb. címekre, a rendszer 20 ms-ot vár, összegyűjti az összes ID-t és elküld egy kérést: /api/users?ids=1,2,3,4,5. Ablak időkorlát — kulcsfontosságú paraméter: a túl hosszú ablak rontja az UX-t, a túl rövid ablak nem teszi lehetővé a megfelelő számú kérés összegyűjtését.

A Netflix Engineering (2023) szerint a GraphQL BFF (Backend for Frontend) aggregátorban a kérések összevonása 65%-kal csökkentette a HTTP hívások számát a rétegek között és 120 ms-mal az átlagos válaszidőt a felesleges RTT-k kiküszöbölésével. Aszinkron ablak (debounce) — szabványos megvalósítás korutinokon vagy RxJava-n keresztül.

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()
    }
}

Ez a mixin a suspendCoroutine-t használja az egyes kérések felfüggesztésére és egy 30 ms-os ablakot a csoport összegyűjtésére. Az időzítő lejárta után az összes összegyűjtött ID elküldésre kerül egyetlen batch kéréssel, és minden korutin megkapja a saját eredményét.

Szerveroldali deduplikáció DataLoader segítségével

DataLoader — egy könyvtár (eredetileg JavaScript/GraphQL számára), amely batching-et és memorizálást valósít meg a szerver oldalán. Az event loop egy tickje alatt csoportosítja az összes kérést ugyanahhoz az adatforráshoz és egyetlen hívással hajtja végre őket. A DataLoader széles körben használják a GraphQL-lel, de bármely REST alkalmazásban alkalmazható.

Működési elv: az összes loader.load(id) hívás egy mikrotárgyon belül ID-k tömbjébe gyűlik és továbbítódik a batch függvénybe. Az eredmények beérkezése után minden ID megkapja a saját tömbelemét. A gyorsítótárazás a DataLoader-ben csak egy HTTP kérésen belül működik — a következő kérésnél a gyorsítótár törlődik, garantálva az adatok naprakészségét.

A Meta Engineering (2024) szerint a DataLoader bevezetése a Facebook GraphQL rétegében kiküszöbölte az N+1 problémát, 200-ról 10-re csökkentve az adatbázis lekérdezések számát egy tipikus oldalon. Batch scheduling — a DataLoader fő innovációja — a process.nextTick (Node.js) vagy DispatchQueue.main (iOS) használja a csoportosítás optimalizálására.

Melyik deduplikációs stratégiát válasszuk

Memoization — optimális egyetlen folyamathoz (mobilalkalmazás, mikroszolgáltatás). Egyszerűen megvalósítható és hatékony az azonos párhuzamos hívásokhoz. Hátránya — nem működik folyamatok vagy eszközök között.

Request Merging — alkalmas BFF réteg vagy aggregátor szolgáltatás számára. A szerver oldalán batch végpontok támogatását igényli. A legjobb választás, amikor a frontend sok kis kérést küld különböző, de azonos típusú adatokhoz.

DataLoader — deduplikációs szabvány a GraphQL szerverek számára. Automatikusan megoldja az N+1 problémát és nem igényli a gyorsítótár kézi beállítását. Ajánlott minden GraphQL réteggel rendelkező szerver számára.

HTTP gyorsítótár deduplikációval — OkHttp (Android) vagy URLSession (iOS) szintjén konfigurálható deduplikáció Interceptor vagy delegate segítségével. OkHttp CacheInterceptor — egyéni elfogó, amely ellenőrzi, hogy egy kérés azonos URL-lel már fut-e és összevonja őket. Ez a módszer az üzleti logika alatti szinten működik és lefedi az alkalmazás összes kérését a funkciók kódjának megváltoztatása nélkül.

Gyakran Ismételt Kérdések

Miben különbözik a deduplikáció a gyorsítótárazástól?

A deduplikáció megakadályozza a duplikálódó kérés végrehajtását, míg az első még fut. A gyorsítótárazás az eredményt a végrehajtás után tárolja. Kiegészítik egymást: a deduplikáció véd az ismételt kérések ellen betöltés közben, a gyorsítótár pedig az ismételt kérések ellen utána.

Mikor lehet ártalmas a deduplikáció?

Ha a deduplikációs kulcs helytelenül van megválasztva. Például, ha minden felhasználó ugyanazt a kulcsot használja, az első kérés blokkolja az összes többit. A kulcsnak specifikusnak kell lennie: tartalmaznia kell az URL-t, paramétereket, felhasználó ID-t. A deduplikáció emellett elfedheti a szerverproblémákat, elrejtve a kérések valós gyakoriságát a metrikákban.

Hogyan válasszuk ki az ablak időkorlátot a Request Merginghez?

Optimális ablak — 20–50 ms a felhasználói forgatókönyvekhez. Ez elegendő a kérések csoportjának összegyűjtéséhez, de nem elég ahhoz, hogy a felhasználó észrevegye a késleltetést. Háttérműveletekhez (naplók, analitika) az ablak 200–500 ms-ra növelhető. Empirikus szabály: az ablak nem haladhatja meg egyetlen kérés végrehajtási idejének 10%-át.

Működik a deduplikáció WebSocket-tel?

Igen, az elv ugyanaz: ha az alkalmazás több része ugyanarra a WebSocket csatornára fizet elő, a deduplikátor egy kapcsolatot nyit és szétküldi az üzeneteket az összes előfizetőnek. RxJava Share vagy Kotlin SharedFlow — ideális eszközök a WebSocket üzenetek deduplikációjára a kliens oldalán.

Hogyan teszteljük a deduplikációt?

Használja a MockWebServer (OkHttp) az Androidhoz vagy OHHTTPStubs az iOS-hez. Indítson el 10 párhuzamos kérést ugyanazokkal a paraméterekkel és ellenőrizze, hogy a szerver pontosan egy hívást kapott. CountDownLatch vagy coroutineScope segít a párhuzamos hívások szinkronizálásában a tesztben.

Összefoglaló

  • Request Deduplication — azonos párhuzamos kérések egyesítése egyetlen kéréssé az eredmény elküldésével minden kezdeményezőnek.
  • Memoization — az eredmény gyorsítótárazása a végrehajtás idejére; egyszerű és hatékony módszer egyetlen folyamathoz.
  • Request Merging — különböző kérések csoportjának összegyűjtése batch-be; szervertámogatást és ablak időkorlátot igényel.
  • DataLoader — deduplikációs szabvány a GraphQL számára; szerver szinten oldja meg az N+1 problémát.
  • A kérések akár 18%-a mobilalkalmazásokban duplikálódik; a deduplikáció csökkenti a szerver és akkumulátor terhelését.
  • A deduplikációs kulcsnak specifikusnak kell lennie: URL-t, paramétereket és felhasználói kontextust kell tartalmaznia.
  • Legjobb gyakorlat — a kliens (OkHttp Interceptor / URLSession) és szerver (DataLoader) oldali deduplikáció kombinációja.

Kulcsrakész mobilalkalmazást fejlesztünk

Az IT Sectr 2017 óta készít iOS és Android alkalmazásokat induló vállalkozásoknak és vállalkozásoknak. Tanácsot adunk, és a legjobb megoldást javasoljuk.

Projekt megbeszélése

Olvassa el is