2017-03-21 2 views
3

Ich habe so etwas wie das Folgende für das Zwischenspeichern von JSON-Antworten in Redis für eine Scala-Anwendung mit http4s, Argonaut und Slick, und möchte bestätigen, dass es funktioniert, wie ich es erwarte. Die Idee ist, wenn ein Redis key nicht gefunden wurde, dann rufen Sie die gegebenen fallback Methode die Daten von der ursprünglichen Quelle (MySQL) und für zukünftige Anfragen cachen zu bekommen, sonst überspringen MySQL:ruft eine Zukunft Methodenparameter „by-name“ notwendig?

/** Get from MySQL */ 
def getThingsFromDatabase(matchingId: Int): Future[Seq[Thing]] = { 
    println("getThingsFromDatabase") 
    val query = things.filter(_.fieldId === matchingId) 
    db.run(query.result) 
} 

/** Get from Redis, else MySQL via `fallback` */ 
def getThingsFromRedisOrDatabase(key: String, 
           fallback: Future[Seq[Thing]]): 
           Future[argonaut.Json] = { 
    val stored = redis.get(key) 
    stored match { 
    // Data exists, return from redis 
    case Some(s) => { 
     Parse.parse(s) match {   // convert string to Json 
     case Right(r) => Future { r } // Json => Future[argonaut.Json] 
     case Left(l) => println(l) // error 
     } 
    } 
    // Data does not exist, get from database and store 
    case None() => { 
     val data = fallback.map(_.toList.asJson) 
     data map { redis.set(key, _) } 
     data // Future[argonaut.Json] 
    } 
    } 
} 

// GET /things/12 
Ok(getThingsFromRedisOrDatabase("things:12", getThingsFromDatabase(12))) 

Dies funktioniert jedoch der obige Code druckt „getThingsFromDatabase“ immer unabhängig davon, ob oder nicht es gibt Daten in Redis weil getThingsFromDatabase(12), wenn sie als Parameter aufgerufen ausführt. Die ursprüngliche Datenbank scheint nicht wie beabsichtigt mit Daten in Redis (ohne Fehler beim Herunterfahren) betroffen zu sein. Ich denke, das liegt daran, dass die fallback Zukunft ist in diesem Szenario nicht verwendet wird, so dass es auch dann nicht abschließt, obwohl die Methode ausgeführt wurde.

Wenn fallback: Future[Seq[Thing]] geändert Call-by-Namen (dh fallback: => Future[Seq[Thing]]), „getThingsFromDatabase“ nur beim ersten Mal gedruckt, wenn der Cache leer ist, wie erwartet, da fallback heißt nur in dem None() Zustand und nicht ausgeführt als ein Parameter.

Während letzteres die beabsichtigte Funktionalität ist, würde es einen Unterschied zwischen der ursprünglichen und der Call-by-Name-Version geben, wenn es keine println in der getThingsFromDatabase Methode gibt? Beide scheinen die Notwendigkeit nicht gehen zu MySQL zu treffen, wenn Redis die gewünschten Daten hat, weder tatsächlich die Zukunft abgeschlossen, obwohl das erstere das Verfahren ausführt.

Antwort

5

Es wäre erhebliche Unterschied. Wie schon geschrieben, wird die db.run() aufgerufen, und die Datenbank wird die Abfrage auszuführen; Die Ergebnisse können verworfen werden, aber normalerweise erledigt der Server die gesamte Arbeit.

Wenn things eine große, nicht indizierte Tabelle ist oder wenn dieser Code häufig aufgerufen wird, dann könnte es zu einer erheblichen Leistungsverschlechterung durch unnötige Aufrufe kommen. Dieses Beispiel ist ein Poster-Kind für die Nützlichkeit von Call-by-Name.