NSIncrementalStore子类中的乐观锁定支持

Bar*_*yzy 5 core-data optimistic-locking ipad ios nspersistentstore

我正在实现一个自定义NSIncrementalStore子类,它使用关系数据库进行持久存储.我仍在努力的一件事是支持乐观锁定.


(可以在下面的问题中跳过这个冗长的描述)

我分析了Core Data的SQLite增量存储如何通过检查它生成的SQL日志来解决这个问题,并得出以下结论:

  • 数据库中的每个实体表都有一个Z_OPT列,它指示从1(初始插入)开始修改此实体(行)的特定实例的次数.

  • 每次修改托管对象时,其相应数据库行中的Z_OPT值都会递增.

  • 存储维护NSIncrementalStoreNode实例的缓存(在Core Data docs中称为行缓存),每个实例的版本属性等于由托管对象的行上的先前SELECTUPDATE SQL查询返回的Z_OPT值.

  • 当从NSManagedObjectContext返回托管对象时(例如,通过对其执行NSFetchRequest),MOC会创建包含此版本号的此对象的快照.

  • 修改或删除对象时,Core Data通过比较缓存的行和对象快照的版本,确保它未在上下文之外被修改或删除.当-save:在对象所属的上下文上调用时,所有这些都会发生.如果版本不同,则基于集合并策略检测并处理合并冲突.

保存MOC时,为每个修改/删除的对象调用-newValuesForObjectWithID:withContext:error:方法,后者又返回带有版本号的NSIncrementalStoreNode.然后将此版本与快照的版本进行比较,如果它们不同,则保存将因适当的合并冲突而失败(至少使用默认合并策略).

这个简单的用例适用于我的商店,因为-newValuesForObjectWithID:withContext:error:首先检查行缓存,如果在其他上下文中使用相同的商店实例同时修改了该对象,那么这就足够了.如果是这种情况,则缓存包含具有更高版本号的更新行,该行足以检测冲突.

但是,我如何检测到底层数据库已经在我的商店外被修改,可能是由使用相同数据库文件的其他应用程序或其他商店实例?我知道这是一个不常见的边缘情况,但Core Data正确处理它,我宁愿做同样的事情.

Core Data的商店使用这些SQL查询来更新/删除对象的行:

UPDATE ZFOO SET Z_OPT=Y, (...) WHERE (...) AND Z_OPT=X
DELETE FROM ZFOO WHERE (...) AND Z_OPT=X
Run Code Online (Sandbox Code Playgroud)

其中:
X - 商店最后知道的版本号(来自缓存)
Y - 新版本号

如果此类查询失败(没有行受影响),则会在商店的缓存中更新该行,并将其版本与先前缓存的版本进行比较.


我的问题是:自定义NSIncrementalStore如何通知Core Data一些更新/删除/锁定的对象发生了乐观锁定失败?这是这是唯一能告诉大家,当它处理店里NSSaveChangesRequest传递给它的-executeRequest:withContext:错误:方法.

如果底层数据库在存储下没有更改,则检测到冲突,因为Core Data 在商店上执行保存更改请求之前,在每个已修改/已删除/已锁定的对象上调用-newValuesForObjectWithID:withContext:error:.我无法找到NSIncrementalStore的任何方法来通知Core Data 开始处理保存请求发生了乐观锁定失败.是否有一些无证的方法可以做到这一点?核心数据似乎在这种情况下抛出一些异常,然后神奇地转换为失败的保存请求,NSError列出了所有冲突.我只能通过从-executeRequest返回nil来模仿这一点:withContext:error:并由我自己创建错误消息.我认为在这种情况下必须有一种方法来使用标准的Core Data冲突处理机制.

Dan*_*lly 1

我意识到这不是您问题的答案,但我会尝试向您提供我对 CoreData 以及与数据库的相关性的看法:

(一级缓存)
NSPecientStoreCoordinator + NSPercientStore == 与数据库的单个连接

(二级缓存)
NSManagedObjectContext == 通过连接进行缓存以保存更改

因此,据我了解,您的问题是您与商店有多个连接,每个连接都进行更改,但您对记录没有集中版本控制。您的商店将收到一条-executeRequest:withContext:error:信息,NSSaveRequestType
然后您将负责验证记录版本是否匹配,如果您发现连接级别(级别 1)存在冲突,您将报告上下文(级别 2)和协调器之间的版本不匹配。
您需要报告您的连接(级别 1)和您的商店之间的版本不匹配。
为了能够做到这一点,您的商店必须报告其所有连接 (ConnectionManager) 上的更改,或者它可能提供对其执行的更改的挂钩。
我不是 SQLite 专家,但 SQLite API 确实在该领域提供了一些东西:
更新钩子
提交钩子
更改
总更改
(我没有设置此类钩子的经验,但如果 CoreData 使用它们,它将不会显示在调试日志)

您可以通过设置错误指针 (NSError**) 并将其内部数据设置为与 CoreData 协调器正在设置的数据相匹配来报告这些错误(创建合并冲突并根据需要设置其中的信息)

请注意,乐观锁定失败只会在-executeRequest:withContext:error: (除非您与存储有恶意连接,且管理器未跟踪该连接) 期间发生。
为了支持此行为,您的管理器可能需要在提交保存时验证每条记录[巨大]性能成本],或者使用一些挂钩到最近对记录所做的更改)

要处理到存储的多个连接,您可能需要有一个 NSIncrementalStoreNode 的共享缓存,由存储 url 键入:
static @{
url1 :actualCacheMapping1,
url2 :actualCacheMapping2,
...
}
保存到存储的每个连接都将再次验证存储url实际缓存。

希望这对您有意义。