ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Effect 事务内存(STM)模块演进:TxDeferred、TxPubSub、TxReentrantLock 与显式 `Effect.tx` 事务边界

Effect 事务内存(STM)模块演进:TxDeferred、TxPubSub、TxReentrantLock 与显式 `Effect.tx` 事务边界 Effect 事务内存STM模块演进TxDeferred、TxPubSub、TxReentrantLock 与显式Effect.tx事务边界【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect导读本文基于 Effect 开源仓库中的 changeset 变更记录.changeset/pre/add-missing-tx-modules.md系统梳理软件事务内存Software Transactional Memory, STM在 v4 中重构后的完整图景新增的五个事务模块TxDeferred、TxPriorityQueue、TxPubSub、TxReentrantLock、TxSubscriptionRef各自解决什么并发问题、Effect.atomic/Effect.atomicWith为何被移除、显式Effect.tx事务边界如何工作以及TxSubscriptionRef.changes竞态修复与TxRandom模块移除的动机。读完本文你将掌握在 Effect 中正确编写全有或全无事务代码的完整方法论并能根据源码理解事务提交、重试与组合的底层机制。变更总览一次事务模型的全面重构该 changeset 标记为effect: patch属于一次增补 破坏性重构的组合变更核心内容可归纳为四条新增五个事务Tx模块TxDeferred、TxPriorityQueue、TxPubSub、TxReentrantLock、TxSubscriptionRef补齐了 STM 生态中的一次性完成原语优先级队列发布订阅可重入读写锁可订阅事务引用。重构事务模型移除Effect.atomic与Effect.atomicWith所有 Tx 操作统一返回EffectA, E, Transaction要求在边界处显式调用Effect.tx(...)。暴露组合原语新增TxPubSub.acquireSubscriber/releaseSubscriber使订阅的注册与注销可以嵌入更大的事务中同时修复TxSubscriptionRef.changes的竞态条件保证当前值一定先于后续更新送达订阅者。清理模块移除TxRandom模块。其中第 2 条是语义上的破坏性变更breaking change也是理解整套新 API 的关键钥匙。事务模型重构从Effect.atomic到显式Effect.tx旧模型的局限在旧模型中开发者通过Effect.atomic/Effect.atomicWith显式圈定原子区域事务语义与 Effect 本身耦合较紧。这种设计的痛点在于事务边界只能由是否包在 atomic 里隐式推断难以将多个事务操作读、写、阻塞等待、发布自由组合成一个更大范围的原子工作流也难以在类型层面表达这段代码必须在事务中运行的约束。新模型EffectA, E, Transaction类型约束重构后的模型将事务能力建模为 Effect 环境中的一个服务标签Transaction。所有 Tx 操作的返回类型都带有Transaction需求例如TxRef.get/TxRef.set/TxRef.modifyTxQueue.offer/TxQueue.takeTxPubSub.publish/TxPubSub.subscribeTxDeferred.await/TxDeferred.succeed它们的类型签名形如EffectA, E, Transaction意味着不使用Effect.tx包裹这些操作无法在普通非事务上下文中运行——类型系统会直接报错要求开发者显式声明事务边界。Effect.tx边界的源码实现Effect.tx定义于 packages/effect/src/Effect.ts 的 Transactions 小节category transactionssince 4.0.0其核心逻辑为export const tx A, E, R(effect: EffectA, E, R): EffectA, E, ExcludeR, Transaction withFiber((fiber) { let state Context.getOrUndefined(fiber.context, Transaction) if (state) { // 已处于事务中组合进当前事务复用其 journal 与 retry 状态 return effect as EffectA, E, ExcludeR, Transaction } // 仅在最外层边界创建事务状态 state { journal: new Map(), retry: false } ... })关键语义与源码注释一致内层tx是组合而非嵌套如果调用时已经处于某个事务中tx直接复用当前事务的 journal未提交变更记录与 retry 标志而不是新建边界。因此把多个 Tx 操作分别包上Effect.tx最终会合并成同一个全有或全无的事务。最外层tx负责提交/回滚只有最外层的tx调用创建事务状态journal: new Map(), retry: false执行事务体然后进入提交或清理流程。乐观并发 版本校验提交前通过isTransactionConsistent检查 journal 中所有TxRef的版本号是否与事务读取时一致只要有任何引用被其他事务抢先修改当前事务立即清空重试见 Effect.ts 中的isTransactionConsistent、commitTransaction与clearTransaction。可中断性控制事务体在uninterruptibleMask下运行避免提交过程中被外部中断导致状态不一致commitTransaction中会递增版本号并通过fiber.currentDispatcher.scheduleTask(pending, 0)唤醒所有等待该引用的 pending 事务。手动重试原语Effect.txRetry与Effect.tx配套的是Effect.txRetry同样位于 Effect.ts 的 transactions 小节。它是一个Effectnever, never, Transaction作用是把当前事务标记为需要重试并中断事务体export const txRetry: Effectnever, never, Transaction flatMap( Transaction, (state) { state.retry true return interrupt } )典型用法是等待条件成立在事务体内读取TxRef若不满足条件则调用Effect.txRetry事务被挂起当任何被访问的TxRef因其他事务提交而改变时该事务会被唤醒并重新执行。awaitPendingTransaction会在所有被访问引用上注册 pending 回调一旦某个引用被提交就解除等待并重跑整个事务。Transaction服务本身Transaction是定义在 Effect.ts 中的一个Context.Service承载两个字段retry: boolean记录当前事务是否应当重试journal: MapTxRefany, { version: number; value: any }记录尚未提交的 TxRef 变更及其读取时的版本。开发者极少直接构造它示例中仅用于教学演示Effect.provideService但它解释了整个事务机制的状态来源。新增事务模块逐个解析以下五个模块全部标注since 4.0.0均以事务内可安全使用 可通过类型系统强制边界为设计原则。1. TxDeferred事务内的一次性完成原语TxDeferredA, Epackages/effect/src/TxDeferred.ts是一个写一次单元格其完成结果以ResultA, E形式存储在TxRefOptionResultA, E中make创建空 deferredsucceed/fail/done完成它只有第一次写入返回true后续写入是 no-op返回false保证恰好完成一次await在事务内等待完成——若尚未完成则调用Effect.txRetry阻塞重试完成时携带成功值或类型化失败继续poll非阻塞地查看当前完成状态未完成返回Option.none()。await的实现直接体现了 STM 的阻塞模型读到Option.none()就Effect.txRetry靠版本号唤醒机制在别的提交完成后自动重跑。适用于事务内协调等待某个一次性事件的场景例如多个事务等待某个初始化完成。2. TxPriorityQueue带排序的优先级事务队列TxPriorityQueueApackages/effect/src/TxPriorityQueue.ts的状态存放在TxRefChunkA中元素按构造时传入的OrderA升序排列empty(ord)按指定排序创建空队列offer通过二分插入insertSorted保持有序take取出最小元素按排序定义peek只观察不删除空队列上的peek/take会事务性地等待自动重试因此可以与其他事务读写组合成一个原子工作流。适合多 fiber 通过共享队列协调、且出队/入队需要与其它事务状态变更保持原子性的场景。3. TxPubSub事务化发布订阅TxPubSubApackages/effect/src/TxPubSub.ts是事务语义下的发布/订阅中枢。每个订阅者持有一个TxQueue发布时把消息投递给发布时刻已注册的所有订阅者队列。四种容量策略bounded/dropping/sliding/unbounded与TxQueue一一对应bounded订阅者队列满时发布者所在事务会重试直到腾出空间背压dropping队列满时丢弃新消息publish返回falsesliding队列满时挤掉最旧消息unbounded容量为Number.POSITIVE_INFINITY总是接受。publish/publishAll向当前所有订阅者广播返回是否全部投递成功hub 关闭后返回false。subscribe基于Effect.acquireRelease的作用域订阅返回TxQueue作用域关闭时自动注销并关闭队列。shutdown/awaitShutdown/isShutdown/size/isEmpty/isFull生命周期与容量查询全部是事务操作awaitShutdown在未关闭时靠Effect.txRetry挂起等待。import { Effect, TxPubSub, TxQueue } from effect const program Effect.gen(function*() { const hub yield* TxPubSub.unboundedstring() return yield* Effect.scoped( Effect.gen(function*() { const sub yield* TxPubSub.subscribe(hub) yield* TxPubSub.publish(hub, hello) return yield* TxQueue.take(sub) }) ) }) await Effect.runPromise(program) // hello4. TxReentrantLock可重入的事务读写锁TxReentrantLockpackages/effect/src/TxReentrantLock.ts允许多个读者并发持有或单个写者独占。其内部状态LockState由两部分组成readers: HashMapnumber, number按 fiberId 记录的读锁持有数writer: Option[fiberId: number, count: number]当前写者及其重入计数。可重入语义锁归属按 fiber 记录已持有锁的 fiber 可以再次获取读锁、写锁均可嵌套并需要对应次数的释放。事务性等待无法立即获取锁时获取操作会事务性重试直到锁可用。模块同时提供手动acquireReadLock/releaseReadLock等、作用域withReadLock/withWriteLock两种风格适合在事务中保护读多写少的共享资源访问。5. TxSubscriptionRef可订阅的事务引用含竞态修复TxSubscriptionRefApackages/effect/src/TxSubscriptionRef.ts把TxRefA当前值与事务化 pub/sub 通道组合订阅者先收到当前值再收到后续每次已提交更新的新值。读写族make、get、set、update、modify、getAndSet、getAndUpdate、updateAndGet——所有变更都在提交时原子地发布给订阅者订阅族changes返回作用域内的TxQueue与changesStream返回Stream。竞态修复的源码证据changeset 明确指出修复TxSubscriptionRef.changes竞态条件确保当前值先送达。看 TxSubscriptionRef.ts 中changes的实现export const changes A( self: TxSubscriptionRefA ): Effect.EffectTxQueue.TxQueueA, never, Scope.Scope Effect.acquireRelease( Effect.tx( Effect.gen(function*() { const sub yield* TxPubSub.acquireSubscriber(self.pubsub) const current yield* TxRef.get(self.ref) yield* TxQueue.offer(sub, current) return sub }) ), (queue) Effect.tx(TxPubSub.releaseSubscriber(self.pubsub, queue)) )关键点在于订阅者注册与读取当前值 投递当前值发生在同一个事务中。这正是 changeset 中ExposeTxPubSub.acquireSubscriber/releaseSubscriber的目的——如果不把它们暴露出来changes只能先注册订阅再在事务外读当前值中间就可能漏掉或错序一个已提交的更新而现在注册 → 读当前值 → 投递当前值原子完成后续所有更新都从该事务的提交点之后开始计数从根本上消除了订阅者错过更新或收到顺序错乱的竞态。changesStream则基于changes构建把每次TxQueue.take包上Effect.tx后重复执行形成Stream。边界组合实战用acquireSubscriber组合自定义事务changeset 将TxPubSub.acquireSubscriber/releaseSubscriber从subscribe的内部实现中独立暴露使其成为一等 APIsince 4.0.0acquireSubscriber(self)创建订阅者队列并注册到 hub返回EffectTxQueueA, never, TransactionreleaseSubscriber(self, queue)从 hub 移除该队列并将其关闭返回Effectvoid, never, Transactionsubscribe本身正是Effect.acquireRelease(Effect.tx(acquireSubscriber(...)), (q) Effect.tx(releaseSubscriber(...)))的组合。因此开发者可以在单个事务中完成注册订阅 读取当前值 执行其它事务读写 注销订阅例如为TxSubscriptionRef.changes这类复合原语自定义行为。注意releaseSubscriber的 Gotcha传入的队列必须是本 hub 通过acquireSubscriber获取的因为释放时会对该队列执行TxQueue.shutdown。TxRandom的移除与其它配套调整changeset 同时移除了TxRandom模块。结合变更方向可以推断随机数生成本质上是无状态副作用与可重试的乐观事务语义存在根本冲突——事务重试会导致随机序列被重复消费破坏一致性因此从 STM 生态中移除。这一移除与Effect.atomic/atomicWith的移除同属收窄事务边界、强化事务原语纯度的整体设计取向属于基于源码结构与变更说明的推断仓库中已不存在 packages/effect/src/TxRandom.ts 文件。此外本次新增的五个模块均已在 packages/effect/src/index.ts 中导出使用时直接从effect包导入即可import { Effect, TxDeferred, TxPriorityQueue, TxPubSub, TxReentrantLock, TxSubscriptionRef } from effect迁移指南从旧模型升级到显式事务边界对正在使用旧事务 API 的开发者迁移路径非常直接删除Effect.atomic/Effect.atomicWith不再存在这两个入口源码中已无定义所有 Tx 操作的类型都带Transaction环境需求。把事务体包进Effect.tx在事务的最外层边界调用Effect.tx(effect)所有内部的 Tx 操作自然组合进同一事务嵌套的Effect.tx是组合而非新建边界。利用类型系统排查遗漏任何 Tx 操作若不处于Effect.tx内会产生Transaction环境未满足的类型错误编译器会精确指出缺失的边界。改用新模块需要一次性完成原语用TxDeferred需要可订阅事务状态用TxSubscriptionRef需要优先级调度用TxPriorityQueue需要广播与背压用TxPubSub需要读写互斥用TxReentrantLock。注意行为细节publish的返回布尔值在不同策略下含义不同bounded 背压重试、dropping 返回false、sliding 挤旧值releaseSubscriber会关闭队列TxDeferred只能成功完成一次。总结这次 changeset 标志着 Effect v4 的 STM 设计走向成熟五个新模块补齐了事务生态的能力拼图Effect.tx显式边界配合EffectA, E, Transaction类型约束把必须在事务中运行从约定提升为编译期保证TxPubSub.acquireSubscriber/releaseSubscriber的暴露使订阅注册、当前值投递与注销可以原子组合TxSubscriptionRef.changes的竞态修复则展示了把注册与初值投递放进同一事务这一关键模式。对于编写高并发、强一致 TypeScript 应用的开发者这套 API 提供了从锁 手动协调到乐观事务 自动重试的范式升级。【免费下载链接】effectBuild production-ready applications in TypeScript项目地址: https://gitcode.com/GitHub_Trending/ef/effect创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表