论文地址:无边界一致性——Alvaro等人。2013年

    我们研究了应用程序开发人员的需求和数据库社区提供的内容之间的差距,从而得出结论,这导致了激进的并发控制的广泛采用。两年前撰写的今天的论文预测了这个问题,并讨论了应用程序和底层数据存储之间的层中可能的解决方案空间。
    在过去几十年中,分布式系统的开发人员传统上依赖于I/O级技术……这些技术将分布式编程的许多复杂性封装在存储层或消息层中,从而简化了应用程序开发。
    然而,这种方法有两个缺点——随着系统规模的扩大,一致性保证的I/O级方法变得越来越成问题,程序员很难解释:
    也许最重要的是,I/O级接口将操作与其应用程序的语义上下文分离:程序员根据应用程序级的正确性属性进行推理,而存储系统则为低级操作(如读取和写入不透明寄存器)提供保证。这迫使开发人员在应用程序级概念与低级存储和消息传递操作之间进行手动转换,这是一项容易出错的任务,需要对底层系统有广泛的了解。反过来,存储或消息传递基础架构无法利用应用程序语义,从而导致保守的协议具有不必要的高延迟和降低的可用性。因此,许多实践者选择尽可能避免I/O级一致性机制,而不是依赖非正式的应用程序级设计模式来实现正确的行为。这些模式很有见地,但对于在每个特定的应用程序场景中正确地实现、测试和维护来说是一个挑战。

    随后关于协调避免和激进并发控制的论文的结合支持了这些观点。
    I/O保证和自定义应用程序逻辑可以被视为两个极端,导致解决方案要么不适合,要么过度适合它们所保护的不变量的应用程序。那该怎么办?
    我们认为,技术界有必要通过在这两个极端之间的设计空间提供一致性解决方案来重新构建这一讨论框架。我们设想了一系列跨堆栈的一致性技术,包括在对象、数据流和语言级别,在效率、通用性和工程复杂性之间进行了各种权衡。

    救援对象?
    从目标层开始,也许CRDTs可以拯救我们?
    编码额外的语义知识显然是有用的:这些工作带来的显著延迟、吞吐量和可用性改进支持了我们的论点,即不透明的I/O级一致性不是一个完整的解决方案。基于对象的方法还解决了我们对应用程序级一致性的可重用性差的一些担忧,因为常见的数据类型(如计数器、列表和图)可以实现一次,然后由多个应用程序共享。
    这里的缺点在于构成CRDTs的挑战。
    虽然对象级方法可以编码有关单个对象或值的语义知识,但不能表示有关对象组成的系统范围语义。对象级一致性通常侧重于实现存储级属性,如副本聚合,让开发人员负责从高级应用程序属性映射到单个对象上的不变量。
    将整个应用程序表示为一个整体收敛对象的极端做法导致了不自然的设计,并使我们回到了特定于应用程序的一致性逻辑的极端。

    数据流来拯救?
    因此,如果我们想考虑单个对象之外的一致性,“下一步自然要考虑数据在应用程序模块、流程边界和服务之间传输时的语义。”
    对由各种服务组成的应用程序的一致性属性进行推理需要对组件的语义属性以及如何在与其他组件的组合中保留这些属性进行推理。因此,它需要一个同时捕获组件语义和交互组件之间依赖关系的模型。一种方法是将分布式系统视为异步数据流,其中输入流通过过滤、转换并将其组合成输出流的组件图。
    合流组件对消息传递顺序不敏感,为所有顺序生成唯一的输出集,并对其输入进行批处理。
    比较融合与CRDT级副本融合的目的,具有一定的指导意义。聚合适用于单个对象,而聚合是数据流组件的一个属性,由更大的数据流图组成。合流组件的组合简化了有关更高级别应用程序级正确性属性的推理,允许开发人员忽略异步网络行为和跨潜在复杂服务的并发性。
    Blazes使用程序员提供的注释来确定在没有任何协调的情况下是否保证一致的结果。当组件不汇合时,Blazes会合成额外的同步逻辑,以确保输出的唯一性

    为什么该方案得不到关注?
    数据流方法的主要缺点是需要手动组件注释:注释模块可能会很繁重且容易出错,特别是对于复杂组件。不正确的注释会损坏分析,并可能导致不安全的优化。对于可重用模块(如第4节中讨论的crdt),可能有专家提供注释。这将分摊注释的成本并降低出错的风险,但仅适用于常用组件。撇开这一缺点不谈,一致性的流级方法占据了一个有趣的中间位置:它们比语言或应用程序级方法更广泛地应用,比不能捕获跨服务组合的对象级方法更强大。

    新的语言来拯救?
    所以,如果我们用一种高级语言编写应用程序,直接对依赖项和适当的语义属性进行编码,那么我们就不需要依赖用户提供的注释,而可以让编译器来理解它…
    首先,我们需要所有系统状态的统一表示,包括进程本地知识、系统事件(如计时器和中断)和网络消息。其次,我们需要一个依赖关系的概念,它同时考虑同步的、过程本地依赖关系(本地计算)和异步的、跨过程依赖关系(通信)。我们称这些思想的结合为以数据为中心的编程:所有系统状态都以统一的方式(作为关系)表示,这使得系统逻辑可以作为对该状态的声明性查询来编写。允许异步查询的扩展语言可以捕获同一声明性框架内的通信。我们团队最近设计的以数据为中心的语言叫做Bloom。
    单调性被证明是一个重要的语义属性——如果一个程序可以完全用单调逻辑来表达,那么它一定是合流的。
    因此,单调操作形成了分布式编程的“安全”词汇表:因为程序的输出是其输入的确定函数,所以更容易检查是否保留了正确性不变量。

    反思
    可以观察到一个总的趋势:随着一致性机制向应用程序靠拢,它们失去了通用性,但获得了效率。

    特定于域的工具和有限的注释是否有帮助?
    帮助程序员实施一致性不变量的一种方法是为他们提供用于分布式程序分析的特定于域的工具。新语言是我们探索过的一个例子;有限的注释是另一个例子。在这方面还有很多工作要做,其他方法也值得探索。例如,理想情况下,人们可以想象新的编程语言工具从遗留程序和命令式语言中提取诸如单调性保证之类的属性。
    我们能允许一些受控的非决定论回到我们的程序中吗?当我们超越单调性时,存在什么机制?
    对于不确定但定义良好的正确性标准,什么是最好的推理方法?一种策略是简单地将可接受结果的空间编码为分离(例如,“购买X成功,购买Y失败,或购买X失败,购买Y成功”)。满足这种分离的合流系统确保始终产生可接受的结果。然而,随着应用程序复杂性的增加,枚举可接受结果的空间的伸缩性很差。有没有一个比列举的结果选择更自然的模型,如果有,我们可以构建程序分析工具来支持它?更根本的是,除了单调性之外,是否有设计模式有助于实现这种“受控非决定论”,并且这些模式可以被编入定理、分析技术和语言结构中?

    底线是:
    可靠的分布式应用程序的开发依赖于程序员对一致性进行推理的能力。通过狭义地关注I/O级一致性,这一领域的传统研究有可能变得越来越无关紧要:随着传统一致性协议的延迟和可用性成本在规模上变得令人望而却步,开发人员已经开始完全避免一致性机制,而不是依赖于临时的,冲突解决与和解的应用程序特定规则。我们认为,解决方案是在自己的地盘上与应用程序开发人员会面:探索在软件堆栈的不同层上操作的各种一致性机制、分析工具和编程结构。目标应该是帮助程序员明智地使用适当强度的一致性,并在最自然的地方对一致性进行推理。