Clojure 在并发环境中拍摄复杂可变结构的快照

Clojure 在并发环境中拍摄复杂可变结构的快照,clojure,Clojure,给定:各种嵌套集合的复杂结构,引用分散在不同级别 需要:一种拍摄此类结构快照的方法,同时允许在其他线程中继续执行写入操作 因此,“读取器”线程需要在单个长事务中读取整个复杂状态。“writer”线程同时在多个短事务中进行修改。据我所知,在这种情况下,STM引擎使用refs历史 这里我们有一些有趣的结果。例如,读卡器在事务开始后10秒内到达某个ref。编写器每1秒修改一次此引用。它产生10个ref历史值。如果超过ref的:max history限制,则读卡器事务将永远运行。如果超过:min his

给定:各种嵌套集合的复杂结构,引用分散在不同级别

需要:一种拍摄此类结构快照的方法,同时允许在其他线程中继续执行写入操作

因此,“读取器”线程需要在单个长事务中读取整个复杂状态。“writer”线程同时在多个短事务中进行修改。据我所知,在这种情况下,STM引擎使用refs历史

这里我们有一些有趣的结果。例如,读卡器在事务开始后10秒内到达某个ref。编写器每1秒修改一次此引用。它产生10个ref历史值。如果超过ref的
:max history
限制,则读卡器事务将永远运行。如果超过
:min history
,事务可能会重新运行多次

但实际上,读者只需要一个ref值(第一个值),而作者只需要一个最近的值。历史记录列表中的所有中间值都是无用的。有没有办法避免这种历史的过度使用


谢谢。

您的问题的一般答案是您需要两件事:

  • 指示系统处于“快照写入”模式的标志
  • 保存系统处于快照模式时发生的所有事务的队列
  • 如果由于快照进程不够快而导致队列溢出,那么,除了优化该进程或增加队列大小之外,您没有什么可以做的,这将是一个平衡,您必须根据应用程序的需要来实现。这是一个微妙的平衡,需要进行一些相当广泛的测试,这取决于您的系统有多复杂

    但你走对了方向。如果您基本上将系统置于“快照写入模式”,那么您的读写器方法应该自动更改它们的读写位置,以便进行更改的线程获取所有“当前值”,而读取快照状态的线程读取所有“快照值”。您可以将这些方法拆分为单独的方法—快照读取器将使用“快照值”方法,而所有其他线程将读取“当前值”方法

    快照读取器完成工作后,需要清除快照状态

    如果线程在当前未设置“快照状态”的情况下尝试读取“快照值”,则它们只需响应“当前值”。没什么大不了的

    允许为备份目的拍摄文件系统快照的系统,在不阻止写入新数据的情况下,也遵循类似的方案

    最后,除非您需要保留系统所有更改的记录(即审计跟踪),否则事务队列实际上不需要是要应用的更改队列-它只需要存储您在系统中更改的任何内容的最新值。当“快照状态”被清除时,您只需将所有未提交的值写入系统,并调用done。你可能要考虑的事情是记录下这些变化,如果你需要从崩溃中恢复过来,并且仍然应用这些变化。日志文件将为您提供所发生事件的记录,并允许您执行此恢复。这是对恢复过程的过度简化,但这并不是你的问题所在,所以我就到此为止。

    对于我来说,拥有一个包含大量嵌套引用的大型结构有点“设计味道”。您正在有效地模拟可变对象图

    尝试一些不同的想法:

    • 在Clojure中解决这个问题的惯用方法是将状态放在一个顶级ref中,其中的所有内容都是不可变的。然后,读者可以免费拍摄整个并发状态的快照(甚至不需要事务)。从您当前的位置进行重构可能很困难,但我认为这是最佳实践
    • 如果您只希望读取器获取顶级ref的快照,那么您可以直接在事务外部对其进行解引用。请注意,内部的引用可能会继续发生变异,因此这是否有用取决于您对读者的一致性要求
    • 您可以在(dosync…)事务中做任何事情,这对于读卡器和写卡器来说都是正常的。您可能会获得争用和事务重试,但这可能不是问题
    • 您可以创建一个“快照”函数,该函数可以快速遍历图形并取消引用事务中的所有引用,返回结果时去掉引用(或替换为新的克隆引用)。读取器调用快照一次,然后在快照完成后继续执行其余的工作
    • 您可以在编写器每次完成后立即拍摄快照,并将其单独存储在atom中。读者可以直接使用它(即,只有writer线程可以直接访问实时数据图)

    您追求的是高性能并发方面的最新技术。你应该看看Nathan Bronson的工作,以及他的实验室与Aleksandar Prokopec、Phil Bagwell和Scala团队的合作

    二叉树: https://github.com/nbronson/snaptree/

    基于数组树的哈希映射


    然而,快速查看上面的实现应该会让您确信这不是“滚自己的”领域。如果可能的话,我会尝试根据您的需要调整现成的并发数据结构。我链接到的所有内容都可以在JVM上免费获得,但它本身并不是本地Clojure。

    “奇怪的时间延迟”不是一个有用的问题标题。你们可以把它编辑成一些有用的问题标题。@Lion,我最好重写整个问题,因为有些事情在我重写部分后变得更清楚了