我使用http4s,Argonaut和Slick在Redis中为Scala应用程序缓存JSON响应,并且想要确认它是否正常工作。想法是,如果找不到Redis key
,则调用给定的fallback
方法从原始源(MySQL)获取数据并将其缓存以备将来请求,否则跳过MySQL:
/** 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)))
这样可行,但上面的代码将始终打印" getThingsFromDatabase"无论Redis中是否有数据,因为getThingsFromDatabase(12)
在作为参数调用时执行。原始数据库似乎没有按照预期使用Redis中的数据命中(如果关闭则没有错误)。我认为这是因为fallback
Future未在此场景中使用,因此即使该方法已执行也无法完成。
如果fallback: Future[Seq[Thing]]
更改为按名称呼叫(即fallback: => Future[Seq[Thing]]
)," getThingsFromDatabase"仅在缓存为空时第一次打印,如预期的那样,因为fallback
仅在None()
条件下被调用而不作为参数执行。
虽然后者是预期的功能,但如果println
方法中没有getThingsFromDatabase
,那么原始版本和按名称调用版本会有区别吗?如果Redis具有所需的数据,两者似乎都满足不去MySQL的需要,即使前者执行该方法,也没有实际完成Future。
答案 0 :(得分:5)
会有显着的差异。如上所述,将调用db.run()
,数据库将执行查询;结果可能会被丢弃,但通常服务器会完成所有工作。
如果things
是一个很大的未编制索引的表,或者经常调用此代码,那么是的,您可以看到不必要的调用会显着降低性能。这个例子是一个海报孩子,用于按名称调用。