是否需要调用 Future 方法参数 "by-name"?
Is calling a Future method parameter "by-name" necessary?
对于使用 http4s、Argonaut 和 Slick 的 Scala 应用程序,我有类似以下内容用于在 Redis 中缓存 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()
条件下调用,不作为参数执行。
虽然后者是预期的功能,但如果 getThingsFromDatabase
方法中没有 println
,原始版本和按名称调用版本之间会有区别吗?如果 Redis 有所需的数据,两者似乎都满足了不去 MySQL 的需要,即使前者执行了方法,也没有实际完成 Future。
会有显着的差异。正如所写,db.run()
将被调用,数据库将执行查询;结果可能会被丢弃,但通常服务器会完成所有工作。
如果 things
是一个大的、未索引的 table 或者如果此代码被频繁调用,那么是的,您会发现不必要的调用会导致性能显着下降。这个例子是 call-by-name 有用性的典型代表。
对于使用 http4s、Argonaut 和 Slick 的 Scala 应用程序,我有类似以下内容用于在 Redis 中缓存 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()
条件下调用,不作为参数执行。
虽然后者是预期的功能,但如果 getThingsFromDatabase
方法中没有 println
,原始版本和按名称调用版本之间会有区别吗?如果 Redis 有所需的数据,两者似乎都满足了不去 MySQL 的需要,即使前者执行了方法,也没有实际完成 Future。
会有显着的差异。正如所写,db.run()
将被调用,数据库将执行查询;结果可能会被丢弃,但通常服务器会完成所有工作。
如果 things
是一个大的、未索引的 table 或者如果此代码被频繁调用,那么是的,您会发现不必要的调用会导致性能显着下降。这个例子是 call-by-name 有用性的典型代表。