概要
GrailsのCacheプラグインで、GSPの中で利用できる<cache:block>
というタグが有ります。
<cache:block></cache:block>
で囲まれた中の処理はキャッシュされ、2度目のアクセスからはキャッシュされた内容が返されるので、処理速度の向上が図れます。
また、key
属性を使うことで、ページごとに異なった内容をキャッシュさせることも出来ます。
<cache:block key="contentId-123>
みたいな感じですね。
問題点
Serviceのキャッシュなどと違い、何故かこの<cache:block>
には、key毎にキャッシュを削除するメソッドが存在しません。
なので、特定のページ(例えばcontentId-123の商品を表示しているページ)の<cache:block>
でキャッシュされたものをクリアする、と言うことが出来ません。
キャッシュをクリアするためにはgrailsCacheAdminService.clearBlocksCache()
というメソッドを使うしかありません。処理的には問題ありませんが、消す必要のないキャッシュまで全て消えてしまいます。
対策
自前で処理を追加します。
方法は色々有りますが、私は以下のようにしています。
キャッシュの作成
キャッシュの生成自体は上のサンプル通りです。
適当なGSP内で、キャッシュしたい内容を<cache:block>
で囲みます。
<cache:block key="contentId-${contentId}">
...ココに重い処理。この部分がキャッシュされる
</cache:block>
キャッシュの削除メソッド
どこかのServiceに以下のメソッドを追加。
@CacheEvict(value='grailsBlocksCache', key="#key")
void clearBlockCacheByKey(String key){}
value='grailsBlocksCache' というvalueは固定です。コレはCacheプラグインが自動で付与しているものです。
削除メソッドを呼ぶ
ココが味噌です。先ほどclearBlockCacheByKeyに削除したいキャッシュのkeyを渡すだけなのですが、実は<cache:block>
taglibを使うと、指定したkeyにGSPのクラス名が付与されています。
フォーマットは{GSPのクラス名}:{自分で指定したkey}
となっています。
余り効率的なコードではありませんが、私は以下のようにして、grailsBlocksCache
に紐付いているkey名を一気に取得して、その中から、自分が指定したkey名を含むものを探しだして、先ほどのclearBlocksCacheByKey
に渡すようにしています。
def deleteCacheBlock() {
String valueForCacheBlock = "grailsBlocksCache" // コレは固定
String deleteKey = "contentId-123" // この値を指定して、削除したいキャッシュのキーを切り替える
String targetCacheKey = grailsCacheManager.getCache(valueForCacheBlock).getAllKeys().find {String key ->
key.split(':')[-1] == deleteKey
}
if (targetCacheKey) {
myCacheService.clearBlockCacheByKey(targetCacheKey)
}
render "ok"
}
まとめ
特に難しい内容でもなく、理想的な対処方法ではありませんが、ちょっとハマったのでメモです。
いやいや、もっと効率的な方法あるよとか、そもそもそのためにXXXというメソッドがあるよ、など有りましたら教えていただけると嬉しいです。。。
参考