避免缓存失败的操作

时间:2012-07-13 14:48:46

标签: java scala caching playframework-2.0

修改
开始chucking(抛出异常),然后就没有“坏”数据要缓存。通过捕获异常,甚至包含在Either [Why-Fail,Option [T]]中,我只为自己做了更多工作。例外情况(理想情况下)是例外的,因此记录,抛出和继续......

ORIGINAL
缓存很好,但缓存失败的操作很糟糕

Cache.orElse("directory.active") {
  Ok( dao.findAll(active = true) as json )
}

DAO数据库查找可能失败(在这种情况下,在数据库/查询失败时返回空List),这将导致缓存错误数据。

如何解决这个问题?我们只想运行一次查询,然后缓存以进一步请求。在Scala中你可以进行延迟初始化,但这会产生永久缓存,这也是不可取的(需要清除成员目录添加/编辑时的缓存)。

假设这适用于任何平台:基本上需要执行一次操作并将其缓存到成功的结果

2 个答案:

答案 0 :(得分:1)

我没有方便的Cache实现,所以无法测试这个,但是如何:

def getCache[A](key:String, result:()=>A)(invalidTest:A=>Boolean):Option[A] = {
  Cache.get(key).getOrElse {
    result match {
      case m:A if (!invalidTest(m)) => Cache.set(key,m);Some(m)
      case _ => None
    }
  }
}

getCache("directory.active", () => dao.findAll(active = true))(_.isEmpty)

正如你所说,你可以轻松地通过pimp Cache来添加它,并对你放入缓存的类型进行默认的有效性测试。

如果你想要缓存Action结果(如果你总是返回json,如果as json返回类型Json - 抱歉,不是Play人),也许:

def getCache[A](key:String, result:()=>A)(invalidTest:A=>Boolean):SimpleResult[Json] = {
  Cache.get(key).getOrElse {
    result match {
      case m:A if (!invalidTest(m)) => Cache.set(key,Ok(m as json));Ok(m as json)
      case x:A => Ok(x as json)
      case _ => Ok(errorjson as json)
    }
  }
}

getCache("directory.active", () => dao.findAll(active = true))(_.isEmpty)

答案 1 :(得分:0)

感谢@brandon我想出了一个蛋糕吃得太多的Play's Cache implementation

def orElse[A,B](key: String, expire: Int = 0)
  (canFail: => B)(failOn: B => Boolean)(body: B => A)
  (implicit m: ClassManifest[A]): A = {

  PlayCache.getAs[A](key).getOrElse {
    canFail match {
      case x if(!failOn(x)) => // all good, cache away
        val tmp = body(x); set(key, tmp, expire); tmp
      case x => body(x) // never cache a failure
    }
  }
}

然后像这样使用:

Cache.orElse("directory.active")(dao.findAll(active = true))(_.isEmpty) { 
  model=> Ok( directory.index(model) )
}

这给我们买的是能够有选择地缓存可能失败的操作,同时保留缓存注入了canFail操作的html包装器的能力。在这种特殊情况下,findAll查询返回@ 400个成员记录并在html层中生成一个jQuery DataTable(生成150KB),因此需要缓存整个操作。