ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Unison 分布式编程 RFC 详解:Remote 能力、Box 原语与容错多节点系统的设计蓝图

Unison 分布式编程 RFC 详解:Remote 能力、Box 原语与容错多节点系统的设计蓝图 编程语言编译器语言运行时开发工具【免费下载链接】unisonA friendly programming language from the future项目地址https://gitcode.com/gh_mirrors/un/unison点击查看免费下载导读本文基于 Unison 开源仓库中的 docs/distributed-programming-rfc.markdown 设计文档系统讲解 Unison 语言为表达容错多节点系统包括需要零停机动态更新与重新部署的系统而设计的一组核心函数原语。你将了解到Remote能力ability如何让计算在节点间跳转、失败、异步分叉与受监督Box、Name、Durable等原语如何提供分布式可变状态与持久化抽象以及这些 API 背后的运行时实现思路、设计演化脉络与仓库中的可验证证据如 unison-src/tests/cce.u 中真实存在的本地模拟实现。读完本文你可以完整理解 Unison 分布式编程 API 的设计意图并把它与 Unison 的能力系统、内容寻址代码等核心机制联系起来。说明本文描述的 API 属于 Unison 的设计提案RFC。根据文档原文部分版本计划在新的 Unison 运行时上实现设计仍在征集反馈与迭代之中尚未全部落地为可直接调用的库函数。背景为什么 Unison 需要一套分布式编程原语Unison 是一门静态类型、基于内容寻址content-addressed代码的函数式语言函数通过其实现内容的哈希而非名称来标识代码以 AST 形式存储在代码库codebase中。这种代码即数据、数据可寻址的模型天然适合分布式场景——一段计算可以被序列化、哈希、传输到另一个节点并继续执行。本 RFC 文档开篇即点明目标描述一组用于表达容错多节点系统的核心 Unison 函数包括必须在不中断服务的情况下动态更新和重新部署的系统。文档给出三条重要说明划定了本设计的定位文档是新旧混合的产物历史背景见 History 附录部分 API 计划在新 Unison 运行时上实现当前主要目的是征集设计反馈并可能在实现前迭代设计一些原语相当底层、偏命令式文档期望用户用纯 Unison 在它们之上构建更友好的 API。与仓库中的另一份设计文档 docs/distributed-api-discussion-v1.md 相比本 RFC 是经过迭代后的 v2 版本前者大量讨论Location、Future、Token等早期概念而本 RFC 已将其精简为Node、Task、Box等更简洁的抽象详见文末历史附录。Unison 能力系统速览理解Remote的语法基础在深入 API 之前需要先理解 Unison 的**能力ability**系统因为Remote就是一个能力而不是普通的效果单子monad。仓库中的 docs/ability-typechecking.markdown 给出了精炼的解释类型a -{IO} b表示从a到b的函数需要IO能力{}应理解为附着在-上。{}中可以包含任意多个用逗号分隔的能力类型如a -{IO, Abort, State Nat} b称为函数的所需能力required abilities。Unison 的类型检查器会阻止调用所需能力在当前环境能力集合中不可用的函数每个子表达式都有一个环境能力集合ambient set。环境的判定规则是最近的外层 lambda 所需能力 被handle块消除的能力。例如在类型为a -{Remote} b的 lambda 体内{Remote}就是环境能力集合。这正是 RFC 中Remote.pure : ∀ a . a - Remote a这类签名能够成立的语言机制Remote是一个可被 handler 解释interpret的代数效应。RFC 中的 API 采用能力风格书写如Remote a表示需要Remote能力才能执行的a计算而仓库测试文件unison-src/tests/cce.u则演示了能力风格的等价写法structural ability Remote where -- Spawn a new node spawn : {Remote} Node -- Sequentially evaluate the given thunk on another node at : n - {Remote} a - {Remote} a -- Start a computation running, returning an r that can be forced fork : {Remote} a -{Remote} Future a可以看到能力风格的Remote将spawn、at、fork作为能力请求直接声明在 ability 类型中与 RFC 中的函数式风格Remote.fork : ∀ a . Remote a - Remote Task互为表里。这正是 Unison 能力系统的威力同一个Remote抽象可以用不同的 handler 实现不同解释本地模拟、真实网络分发等。核心 API 总览Remote能力RFC 将分布式编程能力浓缩为Remote能力中的一组操作。文档原文指出Unison 计算可以在节点间跳转、失败、分叉以异步执行并且可以被监督。完整 API 如下保留文档原始签名与注释-- Promote a pure value to Remote Remote.pure : ∀ a . a - Remote a -- Sequencing of remote computations Remote.bind : ∀ a b . (a - Remote b) - Remote a - Remote b -- The current node where the computation is executing Remote.here : Remote Node -- Transfer control of remainder of computation to target node Remote.transfer : Node - Remote Unit -- Explicitly fail a computation for the provided reason Remote.fail : ∀ a . Text - Remote a -- Sleep the current computation for the given duration Remote.sleep : Duration - Remote Unit -- Start running a remote computation asynchronously, returning -- a Task value that can be used for supervision Remote.fork : ∀ a . Remote a - Remote Task -- Halt a running task (and any running subtasks) using the provided Cause Task.stop : Cause - Task - Remote Unit -- Obtain the Cause that caused a running task to complete Task.supervise : Task - Remote (Remote Cause) -- Create a duration from a number of seconds Duration.seconds : Number - Duration -- this is TBD type Cause Error Text Node | Completed | Cancelled | Unresponsive Node逐项解读这批原语的设计意图Remote.pure/Remote.bind与常见的单子monad操作一致用于把纯值提升进Remote以及按顺序组合远程计算。它们是Remote计算的最小骨架保证计算可以在节点间移动参见下文运行时语义一节中运行时永不主动联系其他节点的设计原则。Remote.here查询当前计算正在执行的节点返回Node值。文档正文没有展开但 v1 讨论docs/distributed-api-discussion-v1.md中曾争论是否需要它最终结论是需要可在启动 Unison 远程服务器时获得一个节点值供应用程序使用或做受限派生。Remote.transfer把剩余计算的控制权转移给目标节点。这是跨节点执行的主力军——转移后后续计算在目标节点继续。Remote.fail以给定文本理由显式失败当前计算返回任意类型a类型层面不可恢复但可以通过外层 handler/监督捕获。Remote.sleep按给定时长休眠当前计算。Remote.fork异步启动一个远程计算立即返回Task值用于监督。Task.stop/Task.supervise停止正在运行的任务连同其所有子任务或取得导致任务完成的原因Cause。Cause类型Error Text Node | Completed | Cancelled | Unresponsive Node覆盖了错误携带错误信息与节点、正常完成、被取消、节点无响应四种结局。其中Unresponsive由监督链路中节点失联触发见下文Task.supervise实现说明。节点供给spawn系列与SandboxRFC 的第二组原语用于供给provision新节点-- Like Remote.spawn, but create the node inside a fresh sandbox Remote.spawn-sandboxed : Sandbox - Remote Node -- Like Remote.spawn-sandboxed, but use the provided symmetric key -- to communicate with the returned Node Remote.spawn-sandboxed : Key - Sandbox - Remote Node -- Create a new node in the same location as the current node, sharing -- current sandbox resources Remote.spawn : Remote Node -- Like Remote.spawn, but use the provided symmetric key -- to communicate with the returned Node. Remote.spawn : Key - Remote Node -- Statically provision a personal-info : Node node personal-info -- layout block starts here Sandbox 5% 10MB 3GB accept-from -- TBD type Sandbox Sandbox CPU% Memory Storage (∀ a . Node - Remote a - Remote a)设计要点动态供给Remote.spawn在当前节点的同一位置创建新节点共享当前沙箱资源Remote.spawn-sandboxed则在新沙箱内创建节点。带后缀的变体额外接受一个对称密钥用于与返回的Node通信加密通道见下一节。静态供给node personal-info声明块在代码编译期静态供给一个节点必须证明一个Sandbox值。文档历史附录特别指出现在有一种方法可以静态声明一个Node这对引导bootstrap一个系统很重要——即系统启动时需要先有一个可信的节点入口。Sandbox类型Sandbox CPU% Memory Storage (∀ a . Node - Remote a - Remote a)其中CPU%是 CPU 配额、Memory与Storage是内存与存储配额最后一个字段是一个Node - Remote a - Remote a类型的函数允许该节点运行的受限计算文档标注为TBD待定。静态声明示例Sandbox 5% 10MB 3GB accept-from展示了其用法CPU 5%、内存 10MB、存储 3GB接受来自指定来源的计算。仓库佐证虽然 RFC 中的Sandbox尚未落地但unison-src/tests/cce.u中Node Node Nat与Node.increment的实现展示了节点作为一种可枚举值的建模方式注释提到更真实的形态可能是(Hostname, PublicKey)对以及Remote.runLocal中step (Node.increment nid)如何为新 spawn 的节点分配新 id。加密原语任意值的加密与解密RFC 的第三组原语让任意值都可以加密/解密-- Encrypt a value, requires Remote since we use random IV / nonce encrypt : ∀ a . Key - a - Remote (Encrypted a) -- Decrypt a value, or return None if key is incorrect decrypt : ∀ a . Key - Encrypted a - Either DecryptionFailure a -- Key is just a symmetric encryption key. We might generate keys via: AES256.key : Remote Key Blowfish.key : Remote Key -- etc -- TBD type DecryptionFailure WrongKey | AlgorithmMismatch | IntegrityFailure设计意图结合文档历史背景encrypt需要Remote能力因为实现依赖随机 IV / noncedecrypt是纯函数返回Either DecryptionFailure a。密钥是对称加密密钥可通过AES256.key、Blowfish.key等生成。DecryptionFailure的三个变体覆盖了密钥错误、算法不匹配、完整性校验失败三种失败模式。关键设计决策来自早期迭代见历史附录传输中的加密由运行时透明处理而持久状态的加密密钥由程序员显式管理。这样多个节点可以共享同一个存储层而不必让所有读操作都经过一个公共节点——每个持密钥的节点都可以直接解密本地/共享存储中的数据。可变状态与并发Box原语Box是 RFC 提供的第一类可变状态原语同时也是并发原语类似有界队列/同步单元。文档原文Unison 程序可以访问可变变量它同时也充当并发原语。-- Create an ephemeral Box on the current node; just a (GUID, Node) at runtime Box.empty : ∀ a . Remote (Box a) -- Put a value into the box, or if the box is full, -- wait until a Box.take empties the box. Box.put : ∀ a . a - Box a - Remote Unit -- Remove and return the value in the box, or if the box is empty, -- wait until a Box.put fills the box. Box.take : ∀ a . Box a - Remote a -- Like Box.take, but leaves the value inside the box Box.read : ∀ a . Box a - Remote a -- Read the current value inside the box or return None immediately. -- Also returns a setter which returns True if the set was successful. -- The set is successful only if the value inside the box has not -- otherwise changed since the read, so this can be used to implement -- optimistic atomic modifies. Box.access : ∀ a . Box a - Remote (Optional a, a - Remote Bool)语义解读运行时表示Box.empty创建的Box是当前节点上的临时ephemeralBox运行时只是一个(GUID, Node)对——即全局唯一 ID 所在节点节点失联或重启后其内容不保证存活。有界同步语义Box.put在 Box 已满时等待直到Box.take清空Box.take在 Box 为空时等待直到Box.put填充。这是一个标准的**同步槽rendezvous slot**模型天然可用于跨节点通信与工作队列。Box.read与Box.take相同但不取出值非破坏性读取。Box.access一次返回当前值 设置器设置器仅在读取后值未被其他方修改时才返回True从而支持乐观并发修改optimistic atomic modify——典型场景是 CAScompare-and-set式更新。仓库佐证分布式垃圾回收文档 docs/distributed-garbage-collection.markdown 中Box 被建模为B_map :: WeakMap BoxId (MVar Value)本地 Box与C_set :: WeakMap RemoteBox远程 Box 引用集合印证了Box 底层是槽位/引用的实现思路并给出了跨节点 Box 引用的 keepalive 传递协议细节见下文配套设计。动态名称解析Name与ephemeral/durable声明RFC 让 Unison 可以在节点上动态解析引用-- Create a Name, which is a typed reference to a node-local value. Name.make : ∀ a . Remote (Name a) -- Lookup the node-local value associated with the Name. Name.resolve : ∀ a . Name a - Remote (Box a) -- Declare bob : Name Number statically. The value bound to -- the Name does not survive node restarting. ephemeral name bob : Number -- Declare cluster-peers : Name (Vector Node) statically. The current -- value of cluster-peers survives node restarting. durable name cluster-peers : Vector Node关键语义文档原文要点Name是节点本地值的类型化引用。Name.make创建它Name.resolve返回对应的Box。ephemeral name静态声明一个临时名称其绑定的值在节点重启后不存活内存级。durable name静态声明一个持久名称其当前值在节点重启后存活落盘级。durable name cluster-peers : Vector Node的示例说明它可用于保存集群成员列表这类需要跨重启保持的配置。文档在节点本地存储一节强调Name与Box的关联是节点本地的每个节点在概念上有自己独立的 durable 与 ephemeral 存储Unison 不暴露比节点更细粒度的存储概念当然可以用普通 Unison 库写出多节点存储。节点彼此隔离必须显式通信即使它们由同一个沙箱供给。持久化Durable值RFC 让任何值都可以持久化。Durable值是不可变的-- Move any value from RAM to local durable storage Durable.store : ∀ a . a - Remote (Durable a) -- Synchronize any value AND ALL TRANSITIVE DEPENDENCIES -- to local durable storage, returning True if the given Node -- has that Durable a locally and the sync was successful. Durable.sync-from : ∀ a . Node - Durable a - Remote Boolean -- Load a durable value into RAM, assuming it exists on the given node Durable.load-from : ∀ a . Node - Durable a - Remote (Optional a) -- Returns a list of nodes that the Unison runtime believes could -- successfully Durable.load-from or Durable.sync-from for the -- given Durable. Durable.peers : ∀ a . Durable a - Remote (Vector Node)设计要点Durable.store把值从 RAM 移到本地持久存储Durable值不可变immutable。Durable.sync-from把一个值及其所有传递依赖同步到本地持久存储返回Boolean表示目标节点是否有该Durable且同步成功。Durable.load-from假设值存在于给定节点将其加载进 RAM返回Optional a不存在则None。Durable.peers返回运行时认为能成功加载/同步该Durable的节点列表。文档预期Durable.load : Durable a - Remote a可以用Durable.load-fromDurable.peers实现只要不是所有节点都删除了存于他处的持久数据成功率很高——即先从 peers 列表尝试加载的启发式。Durable.peers映射的更新策略文档原文摘要RFC 给出了一张 peers 映射的维护草图这是理解持久值的发现机制的关键Durable.load-from n1 d调用若d尚未在当前节点则在 peers 映射中记录n1。接收Remote.transfer的续体continuation时为接收节点上不存在的 durables 增加 peers 条目。若发送者的dpeer 映射是[alice, bob, carol]则接收者会把这组节点加入自己的d映射若发送者映射为空说明发送者本地已有该Durable则只把发送者加入映射。成功的Durable.sync-from调用清除该Durable及其传递依赖的 peers 映射条目——既然已存在本地就不关心还能从哪里取得它了。可能需要裁剪当某个Durable有太多 peers 时可裁剪存储的 peer 数量。这套策略体现了运行时提供足够信息让纯 Unison 库实现良好的发现启发式的设计取向见历史附录加载Durable值更明确地指定了从哪里加载但运行时提供足够信息以实现从 peers 更隐式地发现Durable值的好启发式。外部函数接口Foreign最后一组原语用于声明外部函数foreign function-- Declare my-fn : Foreign (Number - Remote Number) statically -- Bindings for some of these foreign declarations would be done -- in some implementation-dependent way on Unison node container startup. foreign my-fn : Number - Remote Number -- Ask the current node if it has a binding for a Foreign a Foreign.ask : forall a . Foreign a - Remote (Optional a)设计意图foreign声明静态声明一个外部函数其绑定在Unison 节点容器启动时以某种与实现相关的方式完成文档原文注释。Foreign.ask询问当前节点是否持有某个Foreign a的绑定返回Optional a。历史附录说明Capability被拆分为Foreign外部函数接口与Name本地绑定名称两部分这解释了Foreign的职责边界。运行时语义与实现要点核心设计原则让运行时尽可能笨RFC 文档强调了一条贯穿始终的设计原则Unison 运行时绝不应在用户程序未显式指示的情况下联系另一个 Unison 节点。推论有二运行时不能运行任何后台任务去联系其他节点例如 DHT 的维护任务运行时不能隐式选择联系哪些节点例如做自动发现来找好 peer。文档的原话是让运行时**尽可能笨as dumb as possible**把所有智能都移入普通 Unison 库。这与 Unison代码即数据、显式表达的整体哲学一致——副作用与网络行为都必须显式可见。Task的监督语义Remote.fork返回的Task控制整个被分叉计算包括其分叉出的所有子任务Task.stop停止该Task时会停掉其下所有正在运行的内容。Task.supervise的实现草图文档原文运行时层面Task值包含最初分叉该任务所在节点的Node引用。为支持Task.supervise运行时在每个节点维护一个Map Task (Timestamp, Status, Optional Node)记录每个任务在当前节点上的带时间戳最近状态以及若计算被转移走Optional Node转移目标。该Map可按临时策略裁剪例如保留 30 秒数据或 5000 条条目。Task.supervise实现为追踪计算沿这些转移链接一路追踪直到获得足够新的状态更新若某个节点无响应或不可达最终把Unresponsive错误传递给监督者。节点本地存储与Durable灵活性如前所述Name/Box关联局部于节点durable name类比带类型的文件名可在任何节点解析为Box且其空/满状态在节点重启后存活。node node-name块通过证明一个Sandbox来静态声明节点。各种Durable函数给 Unison 程序在如何解析Durable值、从哪里加载上提供了灵活性。配套设计分布式垃圾回收与本地模拟实现分布式垃圾回收docs/distributed-garbage-collection.markdown该文档与 RFC 的Box、Remote.transfer概念直接配套给出了 GC 协议草案每个节点维护弱映射B_map :: WeakMap BoxId (MVar Value)本地 Box与C_set :: WeakMap RemoteBox被本地堆/Box 引用的远程 Box 集合。节点收到 BoxId 的 keepalive 消息时若(b,n)已在visited则忽略否则建立强引用一段时间keepaliveDuration 20.seconds并递归向b_subsb内容所引用的所有 Box含远程转发 keepalive。当续体或 Box 更新从节点x传到节点y时y把c引用的非本地 Box 加入C_setx在传输期间及传输完成后至少要再发一次 keepalive避免y接管 keepalive 期间的竞态。这解释了 RFC 中Box运行时只是(GUID, Node)的设计如何与引用追踪、keepalive 机制协同工作。本地模拟实现unison-src/tests/cce.u仓库测试文件unison-src/tests/cce.u是 RFC 概念已有人写过代码的最直接证据。它定义了一个structural ability Remote含spawn、at、fork并提供了一个在单机上模拟一切分布式行为的 handlerRemote.runLocal : {Remote} a - a Remote.runLocal r step nid cases {a} - a {Remote.fork t - k} - handle k (Future.fromThunk t) with step nid {Remote.spawn - k} - handle k nid with step (Node.increment nid) {Remote.at _ t - k} - handle k !t with step nid handle !r with step (Node.Node 0)在此基础上该文件用纯 Unison 实现了分布式算法Remote.forkAt node r Remote.fork (Remote.at node r)——在指定节点上分叉计算dmap : (a -{Remote} b) - [a] -{Remote} [b]——对列表分布式映射Remote.forkAt Remote.spawn (f a)为每个元素在新 spawn 的节点上执行dreduce——分布式归约分治左右两半各 fork 到新节点dsort——分布式排序dmapReduce组合实现文件末尾还跑了一个Remote.runLocal (dsort () [3,2,1,1,2,3,9182,1,2,34,1,23])的实测样例。这个文件生动印证了 RFC 文档的如下判断一些原语相当底层、偏命令式期望人们用它们在纯 Unison 之上构建更友好的 API——dmap/dreduce/dsort正是在forkat之上构建的高层 API。注意该测试文件还保留着早期运行时报错输出java.lang.ClassCastException说明这是历史演化过程中的实验性代码不能当作当前可运行的标准库使用。历史与背景Appendix: History and contextRFC 附带的 History 附录 完整梳理了设计的演化路径是理解为什么 API 长这样的关键最近基于 #142 的讨论后把Capability拆分为Foreign外部函数接口与Name本地绑定名称Durable值的加载更明确地指定从哪里加载但运行时仍提供足够信息以实现从 peers 隐式发现的好启发式新增静态声明Node的方式对引导系统至关重要。V2基于 #141 的讨论后去掉Clock和Index改为不可变持久存储概念 可变指针去掉Channel改用Box同时把CapabilityAPI 简化为直接基于Box构建去掉了一大堆函数的Heartbeat参数改用通过Remote.link建立的词法作用域环境心跳99% 的情况下正是所需必要时可在嵌套Remote.link中再压入一个心跳明确了Remote.fork计算与Remote.spawn*节点的生命周期语义总是继承当前环境心跳。文档认为这是组合性的关键因为它让关闭子计算的接口完全统一。更早之前2015 年的分布式求值文章 提出了如何用 Unison 的哈希方案构建稳健的多节点计算随后有实现并以此搭建过一个多节点搜索引擎 demo由此引发三大问题的思考直接塑造了本 RFC节点与持久数据的生命周期管理——何时销毁持久数据、何时销毁节点这催生了Heartbeat设计后又被弃用加密——静态持久存储中与传输中如何加密方案是传输中由运行时透明处理持久状态的加密密钥由程序员显式管理使多节点可共享存储层而无需所有读都经过公共节点动态更新与重新部署——如何做到不停机答案是Capability后拆分为Foreign与Name机制。与 v1 讨论文档 docs/distributed-api-discussion-v1.md 对照可见早期草案使用Location、Future、Token加密授权令牌、Future.keepalive、Future.remaining、Remote.supervise等概念并深入讨论了token 的结构与签名数量爆炸未来被 5000 台机器共享时 keepalive 洪泛如何 GC 永远不会被 force 的长任务等问题——这些问题大部分在 RFC 中被更简洁的Box/Task/环境心跳方案取代或解答。结语这份 RFC 告诉了我们什么docs/distributed-programming-rfc.markdown是一份设计蓝图而非现成 API 手册但它完整勾勒了 Unison 分布式编程的哲学与架构极简运行时、智能库运行时不做任何隐式网络行为只提供跳转Remote.transfer、供给spawn*、加密encrypt/decrypt、可变状态Box、命名Name、持久化Durable与外部绑定Foreign等正交原语其余全部由纯 Unison 库组合。显式优于隐式节点间通信、数据移动、持久化位置、加密密钥全部显式可见唯一的隐式机制环境心跳也通过词法作用域保持透明可控。能力系统承载一切Remote作为 algebraic effect既可以在本地被 handler 模拟如cce.u的Remote.runLocal也可以在未来运行时上被真实网络实现解释。对于想深入研究的读者建议按以下顺序阅读仓库中的相关文档docs/distributed-programming-rfc.markdown本文主线→ docs/distributed-api-discussion-v1.md早期设计讨论→ docs/distributed-garbage-collection.markdownBox 的 GC 协议→ docs/ability-typechecking.markdown能力系统基础并配合 unison-src/tests/cce.u 中的可运行原型理解 API 的实际形状。赞分享编程语言编译器语言运行时开发工具【免费下载链接】unisonA friendly programming language from the future项目地址https://gitcode.com/gh_mirrors/un/unison点击查看免费下载相关推荐RJCP.DLL.SerialPortStream虚拟串口测试指南无需硬件即可验证通信逻辑RJCP.DLL.SerialPortStream虚拟串口测试指南无需硬件即可验证通信逻辑 RJCP.DLL.SerialPortStream是一个独立实现的通信物联网终极存储设备诊断指南openSeaChest 如何简化你的硬盘管理工作 终极存储设备诊断指南openSeaChest 如何简化你的硬盘管理工作 你是否曾为存储设备管理而头疼面对复杂的ATA、SCSI、NVMe命令集感到无从如何零基础掌握iOS游戏修改H5GG引擎的完整使用指南如何零基础掌握iOS游戏修改H5GG引擎的完整使用指南 想象一下你正在玩一款心爱的iOS游戏却因为某个关卡太难而卡住或者想体验无限金币的畅快感。现在这逆向工程游戏开发上一篇SOLO实例分割完全指南如何快速掌握高效目标分割技术下一篇高效磁盘性能测试工具实战指南3个关键步骤全面评估存储设备创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表