草庐IT

ios - NSIncrementalStore 子类中的乐观锁定支持

coder 2023-09-25 原文

我正在实现一个自定义 NSIncrementalStore 子类,它使用关系数据库进行持久存储。我仍然挣扎的事情之一是对乐观锁定的支持。

(请随意跳过下面对我的问题的冗长描述)

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

  • 数据库中的每个实体表都有一个 Z_OPT 指示此实体(行)的特定实例已被修改的次数的列,从 1(初始插入)开始。
  • 每次修改托管对象时, Z_OPT 其相应数据库行中的值递增。
  • 存储维护 NSIncrementalStoreNode 实例的缓存(在 Core Data 文档中称为行缓存),每个实例都有一个 版本属性等于 Z_OPT 前一个返回的值 选择 更新 对托管对象行的 SQL 查询。
  • 当一个管理对象从 NSManagedObjectContext 返回时(例如通过在它上面执行 NSFetchRequest),MOC 创建这个对象的快照,其中包含这个 版本数字。
  • 当对象被修改或删除时,Core Data 通过比较缓存行和对象快照的版本来确保它没有在上下文之外被修改或删除。所有这一切都发生在 -保存:在对象所属的上下文上调用。如果版本不同,则根据设置的合并策略检测和处理合并冲突。

  • 保存 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
    

    在哪里:
    X - 商店最后知道的版本号(来自缓存)
    - 新版本号

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

    我的问题是 :自定义 NSIncrementalStore 如何通知 Core Data 某些更新/删除/锁定的对象发生了乐观锁定失败?只有 store 才能知道当它处理传递给它的 NSSaveChangesRequest 时,它的 -executeRequest:withContext:error: 方法。

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

    最佳答案

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

    (一级缓存)
    NSPesistentStoreCoordinator + NSPersistentStore == 一个数据库连接

    (二级缓存)
    NSManagedObjectContext == 缓存在保存更改的连接上

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

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

    注意乐观锁失败只会在-executeRequest:withContext:error:期间发生
    (除非您与商店有流氓连接,经理未跟踪的连接。
    为了支持这种行为,您的经理可能需要验证每条记录,因为它被提交以节省 [巨大的性能成本],或者使用一些 Hook 到最近对记录所做的更改
    )

    要处理到您的商店的多个连接,您可能需要拥有 NSIncrementalStoreNode 的共享缓存,由商店 url 键控:
    静止的 @{
    url1 : 实际缓存映射 1,
    url2 : 实际缓存映射2,
    ...
    }
    保存到商店的每个连接将再次验证商店 url 实际缓存。

    希望这对你有意义。

    关于ios - NSIncrementalStore 子类中的乐观锁定支持,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15821652/

    有关ios - NSIncrementalStore 子类中的乐观锁定支持的更多相关文章

    1. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

      总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

    2. ruby - 其他文件中的 Rake 任务 - 2

      我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

    3. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

      作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

    4. ruby-on-rails - Rails 3 中的多个路由文件 - 2

      Rails2.3可以选择随时使用RouteSet#add_configuration_file添加更多路由。是否可以在Rails3项目中做同样的事情? 最佳答案 在config/application.rb中:config.paths.config.routes在Rails3.2(也可能是Rails3.1)中,使用:config.paths["config/routes"] 关于ruby-on-rails-Rails3中的多个路由文件,我们在StackOverflow上找到一个类似的问题

    5. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

      我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

    6. ruby-on-rails - Rails - 一个 View 中的多个模型 - 2

      我需要从一个View访问多个模型。以前,我的links_controller仅用于提供以不同方式排序的链接资源。现在我想包括一个部分(我假设)显示按分数排序的顶级用户(@users=User.all.sort_by(&:score))我知道我可以将此代码插入每个链接操作并从View访问它,但这似乎不是“ruby方式”,我将需要在不久的将来访问更多模型。这可能会变得很脏,是否有针对这种情况的任何技术?注意事项:我认为我的应用程序正朝着单一格式和动态页面内容的方向发展,本质上是一个典型的网络应用程序。我知道before_filter但考虑到我希望应用程序进入的方向,这似乎很麻烦。最终从任何

    7. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

      我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

    8. ruby-on-rails - Rails 应用程序中的 Rails : How are you using application_controller. rb 是新手吗? - 2

      刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr

    9. ruby-on-rails - form_for 中不在模型中的自定义字段 - 2

      我想向我的Controller传递一个参数,它是一个简单的复选框,但我不知道如何在模型的form_for中引入它,这是我的观点:{:id=>'go_finance'}do|f|%>Transferirde:para:Entrada:"input",:placeholder=>"Quantofoiganho?"%>Saída:"output",:placeholder=>"Quantofoigasto?"%>Nota:我想做一个额外的复选框,但我该怎么做,模型中没有一个对象,而是一个要检查的对象,以便在Controller中创建一个ifelse,如果没有检查,请帮助我,非常感谢,谢谢

    10. ruby - rspec 需要 .rspec 文件中的 spec_helper - 2

      我注意到像bundler这样的项目在每个specfile中执行requirespec_helper我还注意到rspec使用选项--require,它允许您在引导rspec时要求一个文件。您还可以将其添加到.rspec文件中,因此只要您运行不带参数的rspec就会添加它。使用上述方法有什么缺点可以解释为什么像bundler这样的项目选择在每个规范文件中都需要spec_helper吗? 最佳答案 我不在Bundler上工作,所以我不能直接谈论他们的做法。并非所有项目都checkin.rspec文件。原因是这个文件,通常按照当前的惯例,只

    随机推荐