Effect 的 Pool 怎么复用连接并限制并发?
我用 Pool.makeWithTTL 把上限设成 3,然后同时发起 6 个任务:最终只创建了 3 个连接,id 被复用。
用 Pool 复用同类资源并限制并发:固定大小用 Pool.make,要在 min 到 max 之间弹性伸缩用 Pool.makeWithTTL,借用用 Pool.use。 它不按 key 区分资源,池里的资源是可互换的。
Pool 解决的是另一类问题:同一种资源(数据库连接、worker)创建昂贵,需要一批复用。资源本身仍然由 Scope 和 acquireRelease 管理,没读过的话先看 Effect 怎么保证资源一定被释放?;如果资源需要按 key 区分,应该看 Effect 的 LayerMap、RcMap、Pool 该怎么选?。下面从代码开始。
Pool 怎么创建和使用?
import { Effect, Pool } from "effect"
let created = 0
const program = Effect.gen(function*() {
const pool = yield* Pool.makeWithTTL({
acquire: Effect.acquireRelease(
Effect.sync(() => ({ id: ++created })),
() => Effect.void
),
min: 1,
max: 3,
timeToLive: "100 millis"
})
// 6 个并发任务,最多只会创建 3 个连接
const ids = yield* Effect.all(
[1, 2, 3, 4, 5, 6].map(() =>
Pool.use(pool, (conn) => Effect.as(Effect.sleep("50 millis"), conn.id))
),
{ concurrency: "unbounded" }
)
console.log(ids, created)
// 发现某个连接坏了:invalidate 之后池会惰性补一个新的
yield* Effect.scoped(
Effect.gen(function*() {
const conn = yield* Pool.get(pool)
yield* Pool.invalidate(pool, conn)
})
)
})
Effect.runPromise(Effect.scoped(program))跑出来的结果:6 个并发任务只创建了 3 个连接,id 被复用;等待超过 TTL 后,创建过 3 个、关闭了 2 个,收缩回 min = 1;invalidate 之后下一次取到的是新建的第 4 个。
make 和 makeWithTTL 有什么区别?
Pool.make 是固定大小。Pool.makeWithTTL 是 min 到 max 弹性伸缩,超出 min 的空闲项在 timeToLive 后回收,这就是上面例子里「创建 3 个、关闭 2 个、收缩回 1 个」的来源。
Pool.use 和 Pool.get 用哪个?
单个借用用 Pool.use,它不需要额外分配 scope;需要把借用期绑定到某个 scope 才用 Pool.get,比如上面例子里在 Effect.scoped 内取出连接再 invalidate。
一个连接能同时给几个任务用?
默认不能:concurrency 默认是 1,即每个连接同一时刻只给一个 fiber。支持多路复用的客户端可以把它调大。
连接坏了怎么处理,TTL 多久回收?
连接坏了就 Pool.invalidate,池会惰性补一个新的。注意 invalidate 按严格相等匹配,传一个「长得一样」的对象不会生效,必须传从池里取出的那个引用。
timeToLive 默认按「最后一次归还」计时,且按 TTL 周期检查,所以实际回收最长可能拖到两倍 TTL,别拿它做精确的时间保证。
Pool 在 4.0.5 里是 stable,可以放心用在生产代码里。
版本是 effect@4.0.5。上面代码的结果(3 个连接、关闭 2 个、第 4 个新建)在这个版本上跑过;stable标签来自扫描该版本的dist/*.d.ts。默认并发为 1、invalidate严格相等、回收最长两倍 TTL 这几条是对行为的描述,别当作跨版本的契约。