0

私は、タイプ別に保持ポリシーを定義するファクトリ(例:絶対有効期限または有効期限有効期限)を持つ自作データエンティティリポジトリを構築しました。このポリシーでは、キャッシュタイプをhttpcontext要求、セッション、またはアプリケーションとして指定します。 MemoryCacheは、3つのキャッシュタイプすべてのキャッシュプロキシによって維持されます。とにかく、私はロードして私たちのプライマリデータエンティティのために保存するリポジトリに関連付けられているデータエンティティサービスがあります。アイデアは、エンティティリポジトリを使用し、エンティティがデータソース(この場合はdb)からキャッシュまたは取得されているかどうかを気にする必要はありません。MemoryCacheを使用したデータリポジトリ

データソースからエンティティをロードする前に、キャッシュされたエンティティを保存する必要があるため、ロード/セーブイベントを同期させる必要があることは明白です。

は、だから私は、私が読ん:)

今日はMemoryCacheと発射CacheItemRemovedCallbackイベント(デフォルト20から削除されたエンティティとの間に良好な長いギャップが存在することができます...今日の生産にデータの整合性の問題を調査していました秒)。ロードの周りにあった単純なロックとデータ操作の保存は不十分でした。さらに、CacheItemRemovedCallbackはHttpContextの外部にあるため、興味深いものになっていました。つまり、イベントに配置されたインスタンスを割り当てる可能性があるため、コールバック関数を静的にする必要がありました。

私は一度、データエンティティがキャッシュに存在しなくなったが、データソースに保存されていない可能性があるため、5000のうち3つの破損した注文を説明する可能性があることを認識しました。プライマリデータエンティティ上のポリシーの20分のスライディング有効期限を超えて作業を実行するのは簡単です。つまり、期限切れの同じ瞬間に、ロード(要求コンテキスト経由)と保存(キャッシュ期限切れのコールバックによる)との間の興味深い競争状態が発生した場合に起こります。

シンプルなロックでは、サイコロのロールだったので、勝利を救いますか?明らかに、データソース(db)からの次のロードの前に保存が必要です。理想的には、アイテムがキャッシュから期限切れになると、データソースにアトミックに書き込まれます。エンティティがキャッシュから削除されたが、まだ呼び出されていないコールバックが期限切れになっていると、ロード操作が実行される可能性があります。この場合、エンティティはキャッシュ内に検出されないため、デフォルトでデータソースからロードされます。ただし、保存操作が開始されていない可能性があり、データの整合性が損なわれ、現在保存されているキャッシュデータが破損する可能性があります。

私はEventWaitHandleを解決するために、名前付きのシグナリングロックが必要です。ユーザーごとに名前付きロックが作成されます(< 5000)。これにより、エンティティ(スレッドがHttpContext外の独自のコンテキストに存在する)を保存する期限切れイベントからの信号を待機することができます。したがって、保存では、既存の名前ハンドルをつかんで、保存が完了したらロードを続行するように指示するのは簡単です。

私はまた、タイムアウトしてセーブ操作によって各10秒ブロックを記録する冗長性も持っています。私が言ったように、デフォルトは、フォームから削除されたエンティティと、エンティティをセーブするイベントを発生させることを意識しているエンティティとの間の20秒を意味します。

すべてのことを通して私の話題に従った誰にもありがとう。同期要件の性質を考えれば、EventWaitHandleは最良のソリューションをロックしていましたか?

+0

おそらくMemoryCacheを使用していますが、この方法はあまり良いデザインではありません。 –

+0

ある種の複雑さを克服して、何か悪いデザインと呼ぶのは常に魅力的です。しかし、私はスレッド間のキャッシュを扱うことは不合理ではなく、上記のスレッドを同期させる必要があることも不合理であると主張します。 MemoryCacheに関して、私はそれをより良いものを発明しようとする価値があるとは思っていません。私は、その逆、あるいは代替案、またはより良い同期メカニズムについての議論を聞きたいです。 – codinglifestyle

+0

おそらく[redis](https://redis.io/topics/introduction)がお手伝いします。 –

答えて

0

完全性のために、私が問題に対処するために行ったことを投稿したかったのです。私は、名前付きの同期オブジェクトを必要とせず、代わりに単純なロックを使用できる、より洗練されたソリューションを作成するために、デザインに複数の変更を加えました。

最初に、データエンティティリポジトリは、要求キャッシュに格納されたシングルトンです。このリポジトリのフロントエンドは、キャッシュ自体から切り離されています。代わりにセッションキャッシュに存在するように変更しました。これは以下で重要になります。

第二に、私は、上記のデータエンティティリポジトリ経由ルートに期限切れのエンティティのためのイベントを変更しました。

第三に、私は** RemovedCallbackからUpdateCallbackにMemoryCacheイベントを変更しました。

最後に、データエンティティリポジトリの通常のロック(ユーザーのセッション)とギャップレスの有効期限切れイベント(ロックを有効にしてロードと保存(期限切れ)操作をカバーする)を一緒にします。


**これらのイベントは、そのAで面白いです)あなたは両方とBに加入することはできません)アイテムがキャッシュから削除される前に、UpdateCallbackが呼ばれていますが、明示的に項目を削除すると、それが(と呼ばれていません別名myCache.Remove(エンティティ)はイベントを呼び出さずにUpdateCallbackを呼び出します。アイテムがキャッシュから強制的に削除された場合は、私たちが気にしなかったものを決定しました。これは、ユーザーが会社を変更したり、ショッピングリストをクリアしたりするときに発生します。したがって、これらのシナリオはイベントを起動しないため、エンティティは決してDBのキャッシュテーブルに保存されません。デバッグ目的にはいいかもしれませんが、100%カバレッジのRemovedCallbackを使用するには、エンティティの存在のlimbo状態を処理する価値はありませんでした。

関連する問題