Multithreading 使用MemoryCache的数据存储库

Multithreading 使用MemoryCache的数据存储库,multithreading,repository,signals,memorycache,event-wait-handle,Multithreading,Repository,Signals,Memorycache,Event Wait Handle,我构建了一个自制数据实体存储库,其中包含一个工厂,该工厂按类型定义保留策略(例如绝对或滑动过期)。该策略还将缓存类型指定为httpcontext请求、会话或应用程序。MemoryCache由所有3种缓存类型的缓存代理维护。无论如何,我有一个数据实体服务绑定到存储库,它为我们的主数据实体进行加载和保存。其思想是使用实体存储库,而不需要关心实体是缓存的还是从其数据源(在本例中为db)检索的 一个明显的假设是,您需要同步加载/保存事件,因为您需要在从实体的数据源加载实体之前保存缓存的实体 所以我今天在

我构建了一个自制数据实体存储库,其中包含一个工厂,该工厂按类型定义保留策略(例如绝对或滑动过期)。该策略还将缓存类型指定为httpcontext请求、会话或应用程序。MemoryCache由所有3种缓存类型的缓存代理维护。无论如何,我有一个数据实体服务绑定到存储库,它为我们的主数据实体进行加载和保存。其思想是使用实体存储库,而不需要关心实体是缓存的还是从其数据源(在本例中为db)检索的

一个明显的假设是,您需要同步加载/保存事件,因为您需要在从实体的数据源加载实体之前保存缓存的实体

所以我今天在调查生产中的数据完整性问题…:)

今天我读到,从MemoryCache中删除的实体和CacheItemRemovedCallback事件触发之间可能存在很长的间隔(默认为20秒)。我对加载和保存数据操作的简单锁定是不够的。此外,CacheItemRemovedCallback位于HttpContext之外的自己的上下文中,这使事情变得有趣。这意味着我需要使回调函数成为静态的,因为我可能会为事件分配一个已处置的实例

因此,一旦我意识到可能存在缺口,即我的数据实体不再存在于缓存中,但可能尚未保存到其数据源,这可能解释了5000份订单中的3份损坏订单。在填写长表单的同时,在主数据实体上执行策略20分钟滑动到期之后的工作将很容易。这意味着,如果它们恰好在过期的同一时刻提交,则会出现加载(通过请求上下文)和保存(通过缓存过期回调)之间有趣的竞争条件

用一个简单的锁就可以掷骰子了,是保存还是加载赢了?显然,我们需要在从数据源(db)进行下一次加载之前进行保存。理想情况下,当项目从缓存中过期时,它会自动写入其数据源。当实体从缓存中消失但过期的回调尚未启动时,加载操作可能会滑入。在这种情况下,缓存中找不到实体,因此默认情况下从数据源加载。但是,由于保存操作可能尚未开始,导致数据完整性损坏,并且可能会破坏您现在保存的缓存数据

为了完成同步,我需要一个命名的信令锁,所以我决定使用EventWaitHandle。每个用户创建一个<5000的命名锁。这允许加载等待来自保存实体(其线程存在于HttpContext之外的自身上下文中)的过期事件的信号。因此,在save中,很容易抓取现有的名称句柄,并在保存完成后发出继续加载的信号

我还有一个冗余,它会超时,并通过保存操作记录每10秒的数据块。正如我所说的,默认值是从MemoryCache中删除实体和它意识到触发事件之间的20秒,而触发事件又会保存实体


谢谢所有关注我闲聊的人。鉴于同步要求的性质,EventWaitHandle锁是最佳解决方案吗?

为了完整性,我想发布我为解决该问题所做的工作。我对设计进行了多次更改,以创建一个更整洁的解决方案,该解决方案不需要命名的同步对象,并允许我使用一个简单的锁

首先,数据实体存储库是一个存储在请求缓存中的单例。存储库的前端与缓存本身分离。我将它改为驻留在会话缓存中,这在下面变得很重要

其次,我将过期实体的事件更改为通过上面的数据实体存储库进行路由

第三,我将MemoryCache事件从RemovedCallback更改为UpdateCallback**

最后,我们将其与数据实体存储库中的一个常规锁(即用户的会话)结合在一起,并通过该锁路由无间隙过期事件,从而允许该锁覆盖加载和保存(过期)操作



**这些事件很有趣,因为A)您不能同时订阅,B)在从缓存中删除项之前调用UpdateCallback,但在显式删除项时不会调用它(也称为myCache.remove(entity)不会调用事件,但UpdateCallback会调用)。我们决定是否强制从缓存中删除我们不关心的项。当用户改变公司或清除他们的购物清单时,就会发生这种情况。因此,这些场景不会触发事件,因此实体可能永远不会保存到DB的缓存表中。尽管出于调试的目的,它可能很好,但使用覆盖率为100%的RemovedCallback来处理实体存在的不稳定状态并不值得。

也许使用MemoryCache这种方式不是一种很好的设计。在一定的复杂度之后,总是很容易将某个东西称为糟糕的设计。然而,我认为跨线程处理缓存并不是不合理的,也不需要同步所述线程。关于MemoryCache,我怀疑是否值得尝试发明一款更好的。我很想听到一个相反的论点,或者替代方案,或者关于更好的同步机制。也许可以帮助你。