繁体   English   中英

使用MemoryCache的数据存储库

[英]Data Repository using MemoryCache

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

一个显而易见的假设是,您需要同步加载/保存事件,就像在从数据源加载实体之前要保存该缓存的实体一样。

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

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

因此,一旦我意识到存在一种可能性,即我的数据实体不再存在于高速缓存中,但可能尚未保存到其数据源中,那么它可能会解释5000个错误订单中的3个。在策略在主数据实体上滑行20分钟后,可以轻松执行工作。 这意味着,如果它们恰好在到期的同一时间提交,则会在负载(通过请求上下文)和保存(通过缓存过期的回调)之间出现一个有趣的竞争条件。

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

为了完成同步,我需要一个命名的信号锁,因此我选择了EventWaitHandle。 将为每个用户创建一个小于5000的命名锁。这将允许Load等待来自过期事件的信号,该事件将保存该实体(该线程的内存在HttpContext之外的自身上下文中)。 因此,在保存中,很容易抓住现有的名称句柄,并在完成保存后发出信号通知加载继续。

我也有一个冗余,它超时并通过保存操作记录每个10秒的块。 就像我说的那样,默认值是从MemoryCache删除实体到意识到触发事件从而保存该实体之间的20秒。

谢谢所有跟随我漫无目的的人。 考虑到同步要求的性质,EventWaitHandle锁是最好的解决方案吗?

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

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

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

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

最后,我们将其与数据实体存储库中的常规锁(这是用户的会话)和通过它们的无间隙到期事件路由绑定在一起,以允许该锁覆盖加载和保存(到期)操作。


**这些事件很有趣,因为A)您不能同时订阅这两个事件,并且B)在从缓存中删除该项目之前调用UpdateCallback,但是在您明确删除该项目(即myCache.Remove(entity))时不会调用它。不会调用事件,但会调用UpdateCallback)。 我们决定是否将该项目从我们不在乎的缓存中强行删除。 当用户更换公司或清除其购物清单时,就会发生这种情况。 因此,这些情况不会触发该事件,因此该实体可能永远不会保存到DB的缓存表中。 尽管可能出于调试目的很好,但使用具有100%覆盖率的RemovedCallback处理实体存在的边缘状态并不值得。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM