Request Deduplication ist ein Mechanismus, der identische parallele Anfragen zu einer zusammenfasst, sodass die Datenquelle nur einen Aufruf statt Dutzende erhält. In mobilen Anwendungen ist die Deduplizierung besonders wichtig: Mehrere Bildschirme können gleichzeitig dasselbe Benutzerprofil oder dieselbe Produktliste anfordern. Laut Square Engineering (2024) reduzierte die Einführung der Deduplizierung die API-Last um 30%, ohne die Serverlogik zu ändern.
Wichtige Erkenntnisse
Request Deduplication ist eine Technik, die die Ausführung mehrerer identischer Anfragen an dieselbe Datenquelle innerhalb desselben Zeitfensters verhindert. Anstatt 10 identische HTTP-Anfragen zu senden, sendet das System eine, während die anderen 9 auf das Ergebnis warten.
Das Problem doppelter Anfragen ist besonders in mobilen Anwendungen mit zustandsbasierter Architektur (MVVM, MVI, Redux) akut. Wenn mehrere Beobachter innerhalb kurzer Zeit dieselben Daten abonnieren, löst jeder seine eigene Anfrage aus, was eine redundante Last erzeugt. Laut Uber Engineering (2024) sind bis zu 18% aller Anfragen in Uber-Mobilclients Duplikate, und die clientseitige Deduplizierung reduzierte ihre Anzahl um das Vierfache.
Deduplizierung ist nicht dasselbe wie Caching. Der Cache speichert das Anforderungsergebnis nach der Ausführung. Die Deduplizierung verhindert redundante Anfragen vor und während ihrer Ausführung. Nach Abschluss der Anfrage greift das Caching.
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()
}
Diese Kotlin-Klasse garantiert, dass pro Schlüssel nur eine Coroutine ausgeführt wird. Alle gleichzeitigen Aufrufe mit demselben Schlüssel warten auf ein einzelnes Deferred. Nach Abschluss wird der Schlüssel entfernt und die nächste Anfrage normal ausgeführt.
Reduzierte Serverlast ist der erste und offensichtlichste Grund. Jede doppelte Anfrage verbraucht Serverressourcen: CPU, Arbeitsspeicher, Datenbankverbindungen. Bei Millionen von Geräten erzeugen selbst 10–15% doppelte Anfragen eine erhebliche Last, die zusätzliche Server erfordert.
Reduzierter Batterie- und Datenverbrauch — jede HTTP-Anfrage auf einem mobilen Gerät verbraucht Energie des Funkmoduls. Laut Google I/O (2025) kann eine einzelne fehlgeschlagene oder doppelte Anfrage bis zu 15% der Energie einer Netzwerksitzung verbrauchen. Deduplizierung reduziert die Anzahl der Aktivierungen des Funkmoduls und verlängert so die Akkulaufzeit des Geräts.
Vermeidung von Datenkonflikten — wenn zwei doppelte Anfragen Daten in den lokalen Speicher schreiben, können Race Conditions auftreten: Die zweite Anfrage könnte das Ergebnis der ersten mit veralteten Daten überschreiben. Deduplizierung garantiert, dass das Schreiben in den lokalen Speicher nur einmal erfolgt, was Race Conditions ausschließt.
Verbesserte UX — der Benutzer sieht keine mehrfachen Ladeanzeigen für dieselben Daten. Der UI-Zustand (Laden / Erfolg / Fehler) wird von einer einzigen Quelle der Wahrheit verwaltet, nicht von mehreren konkurrierenden Anfragen.
Memoization ist das Zwischenspeichern des Ergebnisses einer Funktion während ihrer Ausführung. Wenn eine Funktion bereits mit denselben Argumenten ausgeführt wird, startet ein neuer Aufruf keinen zweiten Prozess, sondern erhält das Ergebnis des ersten. Dies ist die einfachste Form der Deduplizierung für In-Prozess-Szenarien.
Eine typische Implementierung in mobilen Anwendungen ist eine HashMap von Schlüsseln zu Deferred oder Promise. Der Schlüssel ist normalerweise die URL-Zeichenfolge der Anfrage oder eine Verkettung von Parametern. Die Lebensdauer des Eintrags reicht von der ersten Anfrage bis zum Abschluss der Antwort. Laut Dropbox Engineering (2024) reduzierte die Memoization im Dropbox-Mobilclient doppelte API-Anfragen um 40%.
Fehlerhafte Deduplizierung — ein gefährlicher Fehler: Wenn der Schlüssel nach einem Fehler nicht entfernt wird, geben alle nachfolgenden Anfragen für immer denselben Fehler zurück. Eine korrekte Implementierung muss Error und Failure behandeln, den Cache leeren und einen erneuten Versuch ermöglichen.
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 verwendet Result<T> für eine korrekte Fehlerbehandlung: bei Erfolg — zwischenspeichern, bei Fehler — erneuten Versuch ermöglichen. Dieser Ansatz stellt sicher, dass ein vorübergehender Netzwerkfehler nachfolgende Anfragen nicht blockiert.
Request Merging ist eine Technik, bei der mehrere unterschiedliche Anfragen an dieselbe Quelle in einer Gruppe gesammelt und als eine Batch-Anfrage gesendet werden. Im Gegensatz zur Deduplizierung sind die Anfragen hier nicht identisch — sie unterscheiden sich in den Parametern, adressieren aber dieselbe Ressource.
Ein typisches Szenario: 5 Anwendungsbildschirme fordern Profile verschiedener Benutzer an. Anstatt 5 einzelne Anfragen an /api/users/1, /api/users/2 usw. zu senden, wartet das System 20 ms, sammelt alle IDs und sendet eine Anfrage /api/users?ids=1,2,3,4,5. Das Fenster-Timeout ist der Schlüsselparameter: ein zu langes Fenster beeinträchtigt die UX, ein zu kurzes — sammelt nicht genügend Anfragen.
Laut Netflix Engineering (2023) reduzierte das Request Merging im GraphQL-Aggregator BFF (Backend for Frontend) die Anzahl der HTTP-Aufrufe zwischen den Schichten um 65% und die durchschnittliche Antwortzeit um 120 ms durch die Eliminierung zusätzlicher RTTs. Das asynchrone Fenster (Debounce) ist die Standardimplementierung über Coroutinen oder RxJava.
class BatchMergerT {
private val pending = ConcurrentLinkedQueue<Pair<String, CompletableDeferred<T>>>()
suspend fun get(id: String): T = suspendCoroutine { cont ->
pending.add(Pair(id, cont))
scheduleFlush()
}
}
Dieses Mixin verwendet suspendCoroutine, um jede Anfrage auszusetzen, und ein 30-ms-Fenster, um die Gruppe zu sammeln. Nach Ablauf des Timers werden alle gesammelten IDs in einer Batch-Anfrage gesendet, und jede Coroutine erhält ihr Ergebnis.
DataLoader ist eine Bibliothek (ursprünglich für JavaScript/GraphQL), die Batching und Memoization auf der Serverseite implementiert. Sie gruppiert alle Anfragen an dieselbe Datenquelle innerhalb eines einzelnen Event-Loop-Ticks und führt sie mit einem Aufruf aus. DataLoader wird häufig mit GraphQL verwendet, kann aber in jeder REST-Anwendung eingesetzt werden.
So funktioniert es: Alle loader.load(id)-Aufrufe innerhalb einer einzelnen Mikroaufgabe werden in einem Array von IDs gesammelt und an die Batch-Funktion übergeben. Nach Erhalt der Ergebnisse erhält jede ID ihr Array-Element. Das Caching in DataLoader funktioniert nur innerhalb einer einzelnen HTTP-Anfrage — bei der nächsten Anfrage wird der Cache gelöscht, was die Aktualität der Daten gewährleistet.
Laut Meta Engineering (2024) beseitigte die Einführung von DataLoader in der GraphQL-Schicht von Facebook das N+1-Problem und reduzierte die Datenbankabfragen von 200 auf 10 pro typischer Seite. Batch-Scheduling — die wichtigste Innovation von DataLoader — verwendet process.nextTick (Node.js) oder DispatchQueue.main (iOS), um die Gruppierung zu optimieren.
Memoization ist optimal für einen einzelnen Prozess (mobile App, Mikroservice). Einfach zu implementieren und effektiv für identische parallele Aufrufe. Der Nachteil ist, dass es nicht prozess- oder geräteübergreifend funktioniert.
Request Merging eignet sich für die BFF-Schicht oder einen Aggregator-Dienst. Erfordert Batch-Endpunkt-Unterstützung auf dem Server. Die beste Wahl, wenn das Frontend viele kleine Anfragen für verschiedene Daten desselben Typs stellt.
DataLoader ist der Standard für GraphQL-Server. Es löst automatisch das N+1-Problem und erfordert keine manuelle Cache-Konfiguration. Empfohlen für jeden Server mit einer GraphQL-Schicht.
HTTP-Cache mit Deduplizierung — auf OkHttp-Ebene (Android) oder URLSession-Ebene (iOS) kann die Deduplizierung über Interceptor oder Delegate konfiguriert werden. OkHttp CacheInterceptor ist ein benutzerdefinierter Interceptor, der prüft, ob eine Anfrage mit derselben URL bereits ausgeführt wird, und diese zusammenführt. Diese Methode arbeitet unterhalb der Geschäftslogik-Ebene und deckt alle Anwendungsanfragen ab, ohne den Feature-Code zu ändern.
Häufig gestellte Fragen
Deduplizierung verhindert die Ausführung einer doppelten Anfrage, während die erste noch läuft. Caching speichert das Ergebnis nach der Ausführung. Sie ergänzen sich: Deduplizierung schützt vor wiederholten Anfragen während des Ladens, der Cache vor wiederholten Anfragen danach.
Wenn der Deduplizierungsschlüssel falsch gewählt wird. Wenn beispielsweise alle Benutzer denselben Schlüssel verwenden, blockiert die erste Anfrage alle anderen. Der Schlüssel muss spezifisch sein: URL, Parameter und Benutzer-ID einbeziehen. Deduplizierung kann auch Serverprobleme maskieren, indem sie die tatsächliche Anforderungshäufigkeit in Metriken verbirgt.
Das optimale Fenster beträgt 20–50 ms für benutzerorientierte Szenarien. Dies reicht aus, um eine Gruppe von Anfragen zu sammeln, aber nicht, damit der Benutzer eine Verzögerung bemerkt. Für Hintergrundoperationen (Protokolle, Analysen) kann das Fenster auf 200–500 ms erhöht werden. Faustregel: Das Fenster sollte 10% der Ausführungszeit einer einzelnen Anfrage nicht überschreiten.
Ja, dasselbe Prinzip gilt: Wenn mehrere Teile der App denselben WebSocket-Kanal abonnieren, öffnet der Deduplizierer eine einzige Verbindung und verteilt Nachrichten an alle Abonnenten. RxJava Share oder Kotlin SharedFlow sind ideale Werkzeuge zur Deduplizierung von WebSocket-Nachrichten auf dem Client.
Verwenden Sie MockWebServer (OkHttp) für Android oder OHHTTPStubs für iOS. Führen Sie 10 parallele Anfragen mit identischen Parametern aus und überprüfen Sie, ob der Server genau einen Aufruf erhalten hat. CountDownLatch oder coroutineScope helfen, parallele Aufrufe im Test zu synchronisieren.
Zusammenfassung
Wir entwickeln eine mobile Applikation schlüsselfertig
IT Sectr entwickelt seit 2017 iOS- und Android-Apps für Startups und Unternehmen. Wir beraten Sie und schlagen die beste Lösung vor.
Lesen Sie auch