Die Apollo Engine stellt eine einfache Möglichkeit zum Response Caching zur Verfügung. Dabei können ganze GraphQL Query Antworten oder auch nur einzelne Felder gecached werden. 

Warum Caching und was ist das Besondere dabei mit GraphQL? 

Caching ist bei GraphQL etwas schwieriger als bei REST-Schnittstellen, da nicht wie beim HTTP oder Netzwerk Caching, Daten zum Beispiel über die URL gecached werden können. Dennoch ist Caching bei Datenbankanwendung, wie in unserem Beispiel, das A&O um schnelle Antwortzeiten zu erlangen. Daher muss Caching bei GraphQL die Queries selber bewerten und Antworten im Cache speichern. Genau das liefert Apollo Engine mit. 

Wie aktiviere ich den Cache?

Die einzigen Schritte, die dazu gemacht werden müssen, sind das Caching in den Serveroptionen zu aktivieren und die Cache Hints zu setzen. 

  1. Caching in den Serveroptionen aktivieren:
    Sowohl tracing als auch cacheControl müssen auf true gesetzt werden.
    CacheControl kann optional auch ein Defaultobjekt übergeben werden, in dem das defaultMaxAge gesetzt wird.

    resolve({        schema,
    tracingtrue,
            cacheControl: {
                    defaultMaxAge: 10,
            },
    context
    }); 

  2.  Cache Hints setzen
    Man kann Cache Hints an zwei verschiedenen Stellen setzen. Entweder direkt im resolver oder im Schema. Im Schema können sowohl ganzen Typen, als auch einzelnen Feldern Cache Hints gegeben werden.
    Cache Hints bestehen aus zwei Parametern. Dem maxAge, welcher angibt, wie lange die Daten maximal gecached werden sollen. Und dem scope, dieser kann entweder private oder public sein. Private gecachete Daten sind dann nur in der entsprechenden Session eines bestimmten Clients verfügbar. Der default Wert ist public, die so gecacheten Daten können von allen Clients abgerufen werden.
    Kürzere maxAge Angaben überladen längere in anderen Ebenen (Typen/Felder). Genau dasselbe gilt für private und public beim scope.

    type User @cacheControl (maxAge: 120) {
            id: Int
            name: String
            phone: String
            conversations(limit: Int!, offset: Int): [Conversation] 
            messages(limit: Int!, offset: Int): [Message] 
            isViewer: Boolean @cacheControl (maxAge: 60, scopeprivate)
    }

    Würde jetzt hier eine Anfrage an die ID und den Namen der User gestellt werden würden die Daten 120 Sekunden im Public Scope gehalten. Im Gegensatz dazu würde eine Anfrage an das isViever Attribut und den Namen nur 60 Sekunden und auch im nur im Private Scope gehalten werden.
     

  3. Optional können noch die Cache Optionen geändert werden
    Um private cachen zu können, muss dies auch bei der Konfiguration des Servers beachtet werden. Hierfür reichen die default-Werte nicht mehr aus. 

    Hier für muss“¦ 

    1. „¦ stores mit einem extra Cache initialisiert werden 
    2. „¦ sessionAuth übergeben werden, wie die Session identifiziert wird (HTTP-Header oder Cookie) 
    3. … im queryCache dem privateFullQueryStore der zuvor extra angelegte Cache zugewiesen werden

const engine = new ApolloEngine({
stores: [{
                name: ‚privateResponseInMemoryCache
        }],
sessionAuth: {
                header: ‚authorization‚,
},
queryCache: {
privateFullQueryStore: ‚privateResponseInMemoryCache‚,
                 //By not mentioning publicFullQueryStore, we keep it enabled
//with the default empty-string-named in-memory store.
},
„¦
„¦
});

Wo werden die Daten gespeichert? 

Es werden zwei Speichermöglichkeiten für den Cache unterstützt 

  1. inMemory (default)
    Der Cache liegt innerhalb des Engine Proxys, also auf dem eigenen Server und nutzt die LRU (Least recently used) Verdrängungstechnik. Dadurch, dass der Cache innerhalb eines bestimmten Engine Proxys liegt, kann der Cache zwischen verschiedenen Instanzen nicht gemeinsam genutzt werden ist dafür aber sehr schnell.
    Die Default Cachegröße liegt bei 50mb, kann aber geändert werden. 
  2. Memcache
    Memcache nutzt externe Memcached„¯Server. Dadurch wird der Cache für mehrere Engine Proxies verfügbar ist aber langsamer als der inMemory Cache und liegt nicht mehr auf den eigenen Servern. 

Ergebnisse bei unserer Demo-Anwendung und Zusammenfassung: 

Das Response Caching bringt bei unserer Anwendung bei vielen identischen Anfragen einen Enormen Performance boost (je nach Anfrage: Faktor 20 bis zu 10³). Wichtig ist auch, die im Cache zu speichernde Datenmenge zu reduzieren. Hierbei, hat der Einsatz des Dataloaders auch positive Nebeneffekte gehabt.

Zusammenfassend lässt sagen, dass sich der Cache bei der Apollo Engine sehr leicht mit ein paar Zeilen Code aktivieren lässt und als Response-Cache auch einen deutlichen Performance Boost bei Identischen Anfragen bringt. 

Anhang „“ Testergebnisse

Wir haben mit unserem Server Loadtesting durchgeführt. Dazu haben wir mit dem Tool JMeter unterschiedlich viele Wiederholungen der selben Anfragen an unseren Server geschickt.
Folgende Aspekte haben wir dabei beachtet:

  • Query: Die Query, die an den Server gestellt wurde
  • Komplexität Die maximale Anzahl der Knoten, die zurückgegeben werden können
  • Threads: Die Anzahl der parallelen Anfragen an den Server
  • Wdhs: Die Anzahl, der Wiederholungen der parallelen Threads
  • Abgeschlossene Proben: Die Anzahl der Anfragen, die insgesamt während dem Test an den Server geschickt wurden
  • Durchsatz: Die Anzahl der bearbeiteten Anfragen pro Sekunde
  • Caching: Gibt an, ob Caching im Server aktiviert war
  • Dataloader: Gibt an, ob der Dataloader aktiv war
  • Production Mode: Gibt an, ob der Server im Production Mode gelaufen ist.

In der folgenden Tabelle haben wir unsere Testergebnisse dokumentiert:
LoadTest – QraphQL

Alle Beiträge von Manuel Styrsky

Schreibe einen Kommentar