2025/08/24 11:24 Show HN: CasCache – multi-generational cache with optimistic concurrency control

やっほー、ロボ子!今日のITニュースはcascacheっていう、プロバイダーに依存しないCASキャッシュについてなのじゃ。

CASキャッシュですか、博士。それは具体的にどのようなものなのでしょう?

CASっていうのはCompare-and-Swapの略で、要は「もし値が変わってなかったら書き換える」っていう処理のことなのじゃ。cascacheはそれができるキャッシュってこと!

なるほど。プロバイダーに依存しない、という点はどういう意味ですか?

そこがミソなのじゃ!RistrettoとかBigCache、Redisみたいな色々なキャッシュの仕組みを、プラグインみたいに組み替えられるってこと!

それは便利ですね!記事には「安全なシングルキーの読み取り」や「バルクキャッシュ」もサポートするとありますが、具体的にどういう状況で役立つのでしょうか?

例えば、データベースからデータを読み込む時に、古い値を読み込んじゃうのを防げるのじゃ。バルクキャッシュは、複数のデータをまとめてキャッシュできるから、パフォーマンスが上がるぞ!

世代ストアというのも気になります。これは何をするものですか?

WriterがDB読み取り前にキーごとの世代をスナップショットするのじゃ。キャッシュの書き込みは、世代が変更されていない場合にのみコミットされるから、データの整合性が保たれるってわけ!

なるほど!世代が変わっていたら書き込まない、と。

そういうこと!それに、古い値を返さないし、壊れたデータは自己修復するらしいぞ。かしこい!

セット形状の結果をキャッシュし、読み込み時にすべてのメンバーの世代を検証する、というのも興味深いです。これはどういう仕組みですか?

これは、例えばウェブサイトのトップページに表示する記事のリストをキャッシュする場合を考えてみてほしいのじゃ。リストの中のどれか一つの記事が更新されたら、キャッシュ全体を無効にする、みたいなことができるのじゃ!

なるほど、一部のデータが古くなったら、まとめてキャッシュを更新するんですね。

そういうこと!記事にも書いてあるけど、キーの形式はシングルエントリだと`single:<ns>:<key>`、バルクエントリだと`bulk:<ns>:<first16(sha256(sorted(keys)))>`になるらしいぞ。

キーの命名規則も決まっているんですね。Ristretto、BigCache、Redisの使い分けはどうすれば良いのでしょう?

Ristrettoはインプロセスで、エントリごとのTTLとコストベースの削除ができるのじゃ。BigCacheもインプロセスだけど、グローバルなライフウィンドウしかないから、エントリごとのTTLは無視される。Redisは分散型で、エントリごとのTTLが使えるぞ。

用途によって使い分ける必要があるんですね。分散GenStoreを使わない場合は、バルク処理を無効にするか、短いBulkTTLを使うべき、と。

そう!ローカル世代だと、レプリカ間で古くなる可能性があるからなのじゃ。共有GenStoreを使えば、その心配もなくなるぞ。

処理速度はどうですか?

シングルの読み取りはO(1)、バルクはメンバー数nに対してO(n)の時間計算量なのじゃ。ワイヤデコードはゼロコピーで、バルクアイテムごとに1つのstring割り当てが発生するらしい。

負荷が高い場合はどうなりますか?

Ristrettoは最初にバルクを削除するらしいぞ。デフォルトTTLとBulkTTLが適用されるけど、BigCacheの場合はグローバルなライフウィンドウが優先されるから注意なのじゃ。

クリーンアップについても書かれていますね。

最後に世代が更新された時間に基づいて定期的に行われるのじゃ。デフォルトの保持期間は30日らしいぞ。

cascache、奥が深いですね。色々な場面で活用できそうです。

そうじゃろ?ところでロボ子、キャッシュって、お金持ちの隠し財産みたいじゃな。見つからないように、でも必要な時にはすぐ取り出せるように…って、ちょっと違うか!
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。