Appearance
数据库并发边界:Transaction、Isolation、Optimistic Concurrency 与 Pool
前置:Runtime 数据库怎样设计 Key 和 Index:Schema、Key 与 Index
核心问题:两个 Worker 同时更新同一 Run 时,数据库怎样阻止 lost update,又怎样避免用过重锁把吞吐拖垮?
先在短事务里 claim node,提交后调用外部 Tool;完成再开新事务写 result。两个 worker claim 同一 READY node,用 UPDATE ... WHERE status=READY 的影响行数决定谁成功。
Transaction
transaction 提供原子性/隔离,但只覆盖同一数据库资源。隔离级别决定并发可见性与冲突,不要长期持锁等待 LLM/HTTP。Transaction 让同一数据库中的多步写入满足原子提交/回滚,但不要跨慢 LLM/HTTP 调用长期持有事务,否则锁和连接被占用。
长事务会占连接和锁,不应等待外部 Agent 操作。
Isolation 基础
Isolation 解决并发事务彼此能看到什么、冲突时谁可以提交。Harness 最常见的问题不是背四种隔离级别,而是避免两个 Worker 同时把同一个 Run 当成自己拥有。
例如:
text
worker A reads version=7
worker B reads version=7
A UPDATE ... WHERE version=7 → success, version=8
B UPDATE ... WHERE version=7 → 0 rows → conflict这种短事务 + optimistic concurrency 通常比把整个长 Run 放在高隔离事务里更合理。长时间持锁会让 Agent 的网络/模型等待直接拖垮数据库。
Optimistic Concurrency
用 version/updated at compare-and-swap 检测并发更新,适合低冲突 RunState;冲突后重新加载/合并而非覆盖。
通过 version/etag 条件更新检测并发冲突,适合状态记录读多写少、冲突可重试。冲突意味着要重新读取事实,而不是覆盖。
Database Connection Pool
增加并行度最终受 provider quota、DB pool、CPU、网络限制。bounded queue/backpressure 让上游在系统饱和时减速,而非继续制造内存任务。
数据库 Connection Pool 是昂贵 DB session 的容量边界。Agent 的 coroutine 数通常远大于连接数,因此要控制事务持有时间、acquire timeout 和 per-DB concurrency,尤其不能跨模型/HTTP await 长时间占连接。
两个 Worker 同时 Resume 一个 Run 会发生什么
假设 worker A、B 都读到:
text
run_id=r-42, status=READY, version=17如果两边随后都直接 UPDATE runs SET status='RUNNING',就可能同时认为自己拿到了执行权。一个简单的 optimistic claim 可以是:
sql
UPDATE runs
SET status = 'RUNNING', version = 18, owner_id = :worker
WHERE run_id = 'r-42'
AND status = 'READY'
AND version = 17;只有 rowcount == 1 的 worker 获得 claim;另一个必须重新读取状态。也可以使用数据库锁或 queue/lease 方案,但目标相同:并发控制要把“谁拥有执行权”变成数据库能证明的事实。
Transaction isolation 决定并发事务能观察到什么,但不会自动替你设计 Run ownership。隔离级别、optimistic version、unique constraint、lease/fencing 分别解决不同层的问题。
Failure Modes
数据库 Transaction 中等待长 LLM/HTTP
症状: begin 后调用模型/远程 Tool 数十秒再提交。
为什么危险: 连接和锁长期占用。
处理: 事务只覆盖短本地修改;外部动作通过状态机/outbox/execution record 协调。
如何验证
| Case | 断言 |
|---|---|
| 数据库 Transaction 中等待长 LLM/HTTP | 外部调用慢 10x,DB transaction duration 仍保持短。 |
自测
- 两个 Worker 同时把同一个 Run 从 WAITING 更新到 RUNNING。怎样用 transaction/version check 避免 lost update?
- 连接池耗尽与数据库行锁等待都会让请求变慢,如何从等待位置区分这两种瓶颈?