如何使用yesod per-request缓存?

时间:2015-11-11 16:51:26

标签: haskell caching yesod

我正在尝试使用cached函数来阻止不同小部件和处理程序中的多个数据库查询:

newtype CachedBobId key
    = CachedBobId { unCachedBobId :: key }
    deriving Typeable

getBob' :: Handler BobId
getBob' = do
    uncle <- runInputGet $ ireq textField "bobsuncle"
    (Entity bob _) <- runDB $ getBy404 $ UniqueBob uncle
    return bob

getBob :: Handler BobId
getBob = do
    a <- getBob'
    let b = return $ CachedBobId a
    c <- cached b
    return $ unCachedBobId c

在某个小部件中:

renderDerp :: Widget
renderDerp = do
    --these are used in the shakespeare files
    lolBob <- handlerToWidget $ getBob
    nutherBob <- handlerToWidget $ getBob
    $(widgetFile "test")

这会编译,但获取ID的查询仍会多次运行。

我做错了什么?或者,有没有更好的方法只能获得bob一次并在每个处理程序和小部件中使用它?

1 个答案:

答案 0 :(得分:3)

我对Yesod很新,但我认为你只需要调整getBob

getBob :: Handler BobId
getBob = unCachedBobId <$> cached (CachedBobId <$> getBob')

问题是,您当前的getBob函数会使用do启动其a <- getBob'块。请记住,do块会对monadic操作进行排序,因此每次调用getBob'时,您实际上最终都会调用getBob。具有讽刺意味的是,在您完成此操作之后,您将创建一个处理程序的缓存版本,它返回您从getBob'获得的内容,但最终只查询缓存版本一次(之后使用{{ 1}}),然后它就会超出范围,垃圾收集器就会得到它。

在我上面提到的解决方案中,您可以在c <- cached b中包含getBob'给您的任何内容。然后,您将该处理程序CachedBobId传递给CachedBobId <$> getBob' :: Handler (CachedBobId BobId),它会返回另一个相同类型的处理程序cached,但会使用缓存。最后,提取缓存处理程序为您提供的任何内容都可以返回cached (CachedBobId <$> getBob')