我构建了一个自制数据实体存储库,其中包含一个按类型定义保留策略(例如绝对或滑动到期)的工厂。该策略还将缓存类型指定为 httpcontext 请求、会话或应用程序。MemoryCache 由所有 3 种缓存类型中的缓存代理维护。无论如何,我有一个与存储库相关的数据实体服务,它为我们的主要数据实体加载和保存。这个想法是您使用实体存储库,并且不需要关心实体是否被缓存或从它的数据源(在本例中为 db)检索。
一个明显的假设是您需要同步加载/保存事件,因为您需要在从数据源加载实体之前保存缓存的实体。
所以我今天正在调查生产中的数据完整性问题...... :)
今天我读到从 MemoryCache 中删除的实体和 CacheItemRemovedCallback 事件触发之间可能存在很长的差距(默认为 20 秒)。我对加载和保存数据操作的简单锁定是不够的。此外,CacheItemRemovedCallback 位于 HttpContext 之外的自己的上下文中,这让事情变得有趣。这意味着我需要将回调函数设为静态,因为我可能会将已处置的实例分配给事件。
因此,一旦我意识到可能存在差距,即我的数据实体不再存在于缓存中,但可能没有保存到它的数据源中,这可能会解释 5000 个中的 3 个损坏订单。在填写长表格时,它将是易于在主数据实体上执行超出策略 20 分钟滑动到期的工作。这意味着如果他们碰巧在到期的同一时刻提交了加载(通过请求上下文)和保存(通过缓存过期回调)之间的有趣竞争条件。
用一个简单的锁它就是掷骰子,保存或加载会赢吗?显然,我们需要在下一次从数据源 (db) 加载之前进行保存。理想情况下,当一个项目从缓存中过期时,它会自动写入它的数据源。当实体从缓存中消失但过期回调尚未触发时,加载操作可能会进入。在这种情况下,将无法在缓存中找到实体,因此默认从数据源加载。但是,由于保存操作可能尚未开始,从而导致数据完整性损坏,并且可能会破坏您现在保存的缓存数据。
为了完成同步,我需要一个命名的信号锁,所以我选择了 EventWaitHandle。每个用户创建一个 < 5000 的命名锁。这允许 Load 等待来自保存实体的过期事件的信号(其线程存在于 HttpContext 之外的自己的上下文中)。所以在保存中很容易抓住现有的名称句柄并在保存完成后向加载发出信号以继续。
我还有一个冗余,它超时并通过保存操作记录每 10 秒块。正如我所说,默认值意味着从 MemoryCache 中删除一个实体到它意识到它会触发该事件进而保存实体之间的 20 秒。
感谢所有跟随我的漫谈的人。鉴于同步要求的性质,EventWaitHandle 锁是最佳解决方案吗?