キャッシュアサイドパターンを変更するために、データをフェッチして更新するときの次の手順を定義します。
アイテムの取得
アイテムの更新
これはほとんどすべてのケースで完全に機能しますが、1つの理論的なシナリオでは失敗するようです。
更新アイテムのステップ1と2がアイテムの取得のステップ2と3の間に発生した場合。つまり、最初のデータストアの値は「A」であり、キャッシュにはなかったと考えてください。したがって、アイテムをフェッチするときに、データストアから「A」を読み取りましたが、キャッシュに入れる前に、アイテムが別のスレッドで「B」に更新されました(「B」がデータストアに書き込まれ、キャッシュからエントリを削除しようとしました、当時はありませんでした)。これで、フェッチスレッドが読み取ったアイテム(つまり「A」)をキャッシュに入れます。したがって、 'A'はキャッシュされたままになり、アイテムが期限切れになるか再度更新されるまで、さらにフェッチすると古いデータが返されます。
ここに何か欠けているのは、パターンの理解が間違っていることです。または、シナリオは実際には不可能であり、心配する必要はありません。
また、この問題を回避するために、パターンに変更を加えることができるかどうかを知りたいです。
競合状態を正しく指摘しています。
here および here で説明されているように、キャッシュアサイドは不完全な抽象化であり、すべてのデータストレージの使用例に適しているわけではありません。
Consistency。Cache-Asideパターンを実装しても、データストアとキャッシュ間の一貫性は保証されません。データストア内のアイテムは、外部プロセスによっていつでも変更される可能性があり、この変更は、次にアイテムがキャッシュに読み込まれるまでキャッシュに反映されない場合があります。データストア間でデータを複製するシステムでは、同期が非常に頻繁に発生する場合、この問題は特に深刻になる可能性があります。
このテキストは、無効化の必要性をキャッシュに通知せずに誰かがデータストアを変更した場合の問題を示しています。 (パターンは、データエラーのライフタイムを制限することを目的とした、適切なエビクションポリシーの必要性を示します。)
ただし、指摘している競合状態は、すべてのクライアントがルールに従っている場合でも発生する可能性があります。競合状態が発生すると、古くなったデータがキャッシュに保存され、標準の(たとえば、時間ベースの)エビクションによって排除されるか、そのキーのデータが再度更新されて、今回はおそらく競合がなくなるまで、キャッシュに残ります。 。)
いくつかの古いデータを最新のデータと一緒に提供することは、少なくともある時点でまとめて少なくとも完全に正しくなった古い情報(スナップショット)を単に返すよりも、一貫性の侵害としては悪いものです。これは、読み取りまたは書き込みスキューの形式と呼ばれることもあります。
また、この問題を回避するために、パターンに変更を加えることができるかどうかを知りたいです。
このパターンの問題は、単一の責任(データストレージ、保持状態)が複数のコンポーネントに分散することです。したがって、指摘している競合状態を修正する方法は、モデルを変更して、キャッシュがデータの読み取りと書き込みの両方に完全な責任を持つファーストクラスのエンティティになるようにすることです。クライアントはキャッシュにデータを要求し、必要に応じて、キャッシュはデータストアからデータをフェッチしてクライアントに返します。クライアントは更新をキャッシュに通知し、キャッシュはデータストアを更新します。キャッシュは、適切な同期を提供できる位置にあるため、競合状態を回避できます。
最終的に、このパターンは、頻繁に変更されないデータや、複数のキーのセットの一貫性やトランザクションに依存しないデータストレージに役立ちます。たとえば、音楽ライブラリなど、特定の種類のドキュメントストアで機能する場合があります。特に、ライブラリがほとんど追加されている場合、キーは更新されず、ドキュメントが削除されることもありますが、(ビジネスの観点から)続行しても問題ありません。削除された後、エビクション期間中それらを提供します。
アイテムの更新のステップ1と2がアイテムのフェッチのステップ2と3の間に発生した場合
この問題は、アイテムのフェッチとアイテムの更新を相互にアトミック操作として処理できる場合に回避されます。次に、2つのプロセスはsynchronizedと呼ばれます。
これを(効率的かつ慣用的な方法で)実現する方法は、実装言語によって異なります。