Is calling the Future Method "By Name" necessary?

I have something like the following for caching JSON responses in Redis for a Scala application using http4s, Argonaut and Slick and want to confirm that it works as I expect. The idea is that Redis was key

not found, then call the given method fallback

to get data from the original source (MySQL) and cache it for future queries, otherwise skip 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)))

      

This works, however the above code will always print "getThingsFromDatabase" regardless of whether there is data in Redis, because it getThingsFromDatabase(12)

is executed when called as a parameter. The original database doesn't seem to hit (no error if turned off) with the data in Redis as intended. I think this is due to the fact that fallback

Future is not used in this scenario, so it does not terminate even though this method was executed.

If fallback: Future[Seq[Thing]]

changed to call by name (ie fallback: => Future[Seq[Thing]]

), "getThingsFromDatabase" is only displayed the first time the cache is empty, as expected, since it fallback

is only called in None()

and is not executed as a parameter.

While the latter is the intended functionality, would there be a difference between the original and the call by name version if getThingsFromDatabase

there was no method in the method println

? Both of them seem to need to not go to MySQL if Redis has the data they want, neither of which terminate the future, even if the former does that method.

+3


source to share


1 answer


There would be a significant difference. As written, will be called db.run()

and the database will execute the query; the results can be discarded, but usually the server does all the work.



If it things

is a large, non-indexed table, or if this code is called frequently, then yes, you can see significant performance degradation from unnecessary calls. This example is a poster child for the utility of calling by name.

+5


source







All Articles