0

我構建了一個自定義數據實體存儲庫,其工廠根據類型(例如絕對或滑動過期)定義了保留策略。該策略還將緩存類型指定爲httpcontext請求,會話或應用程序。 MemoryCache由所有3種緩存類型中的緩存代理維護。無論如何,我有一個數據實體服務綁定到存儲庫,它負責爲我們的主數據實體進行加載和保存。這個想法是你使用實體庫,並不需要關心實體是否緩存或從它的數據源(在這種情況下,數據庫)中檢索。使用MemoryCache的數據存儲庫

一個明顯的假設是您需要同步加載/保存事件,因爲您需要在從其數據源加載實體之前保存緩存實體。

所以我在今天生產的調查數據完整性問題... :)

我今天看了就不可能有實體之間的良好的長期差距從的MemoryCache和CacheItemRemovedCallback事件觸發(默認20被移除秒)。我在加載和保存數據操作時遇到的簡單鎖定不足。此外,CacheItemRemovedCallback在HttpContext之外的自己的上下文中使事情變得有趣。這意味着我需要使回調函數成爲靜態的,因爲我可能會將事件置於實例中。

因此,一旦我意識到存在差距的可能性,即我的數據實體不再存在於緩存中,但可能未保存到其數據源中,可能會解釋5000中的3個損壞訂單。從主要數據實體的政策20分鐘滑動到期之後執行工作很容易。這意味着,如果它們碰巧在相同的時刻提交,則會出現加載(通過請求上下文)和保存(通過緩存過期回調)之間的有趣競爭條件。

簡單的鎖定是擲骰子,會保存或加載勝利?顯然,我們需要在數據源(db)的下一次加載之前進行保存。理想情況下,當一個項目從緩存到期時,它會原子地寫入其數據源。當實體從緩存中消失但過期回調尚未觸發時,加載操作可能會滑入。在這種情況下,實體將無法在緩存中找到,因此將默認從數據源加載。但是,由於保存操作可能尚未開始,導致數據完整性損壞,並可能會破壞您現在保存的緩存數據。

爲了實現同步,我需要一個命名的信號鎖,因此我決定使用EventWaitHandle。爲每個用戶創建一個命名鎖,它是< 5000.這允許Load等待來自過期事件的信號,該事件保存實體(其線程存在於其自己的上下文之外的HttpContext中)。因此,在保存完成後,很容易獲取現有的名稱句柄併發信號通知Load以繼續。

我也有一個冗餘,它超時並通過保存操作記錄每個10秒的塊。正如我所說的,默認的意思是在從MemoryCache被移除的實體和它意識到它觸發事件並進而保存實體之間的20秒之間。

謝謝任何​​通過我所有的追隨我的漫步者。鑑於同步要求的性質是EventWaitHandle鎖定最佳解決方案?

+0

也許使用MemoryCache這種方式不是一個很好的設計。 –

+0

把一些糟糕的設計稱爲超過一定的複雜程度總是很誘人。但是,我會爭辯說,跨線程處理緩存並非不合理,也不需要同步所述線程是不合理的。關於MemoryCache,我懷疑它是值得嘗試創造一個更好的。我很樂意聽到相反的論點,或者選擇,或者更好的同步機制。 – codinglifestyle

+0

也許[redis](https://redis.io/topics/introduction)可以幫助你。 –

回答

0

爲了完整起見,我想發佈我所做的解決問題的方法。我對設計進行了多處更改,以創建一個不需要命名同步對象的更整潔的解決方案,並允許我使用簡單的鎖定。

首先,數據實體庫是存儲在請求緩存中的單例。存儲庫的這個前端與緩存本身分離。我將它改爲駐留在會話緩存中,而這在下面變得很重要。

其次,我更改了過期實體的事件,以便通過上面的數據實體存儲庫進行路由。

第三我將MemoryCache事件從RemovedCallback更改爲UpdateCallback **。最後,我們將所有數據綁定在數據實體存儲庫中,這是用戶的會話和通過相同的允許鎖來覆蓋加載和保存(到期)操作的無間隙過期事件路由。


**這些事件是在一個有趣的),你可以不同意這兩個和B)該項目是從緩存中移除之前UpdateCallback被稱爲但是當你明確地刪除該項目(它不叫又名myCache.Remove(實體)不會調用事件,但UpdateCallback將會)。我們做出決定,如果該項目被強制從緩存中刪除,我們不在乎。當用戶更改公司或清除購物清單時會發生這種情況。所以這些場景不會觸發事件,所以實體可能永遠不會被保存到數據庫的緩存表中。雖然它對於調試目的可能很好,但使用具有100%覆蓋率的RemovedCallback來處理實體存在的限制狀態並不值得。