AWS SDK(JS)调用transactWriteItems遇TransactionInProgressException排查
我之前在使用AWS JS SDK的Document Client处理事务时也遇到过一模一样的问题,当时排查了好久,结合你说的「仅触发一次请求」的场景,整理几个最可能的根本原因:
SDK自动重试导致的重复请求:AWS JS SDK(尤其是v2版本)默认会对网络错误、超时等情况进行自动重试,而且为了保证幂等性,重试时会复用同一个
ClientRequestToken。如果第一次请求已经到达DynamoDB并启动了事务,但客户端因为网络波动没收到响应,SDK就会自动发起重试,这时候DynamoDB就会检测到同一个令牌的事务还在处理中,抛出这个异常。哪怕你代码里只调用了一次,但SDK的重试机制会偷偷发起第二次请求。异步逻辑中的隐式重复调用:Node.js的异步环境很容易踩这个坑——你以为代码只执行了一次,但实际上因为事件绑定、回调嵌套或者Promise错误处理的问题,
transactWriteItems被触发了多次。比如:- 某个Express路由的中间件被重复注册,导致同一个请求触发两次事务调用
- Promise的
.catch()里不小心再次调用了事务函数,而第一次的事务还在处理 - 事件监听回调被绑定了多次,比如
stream.on('data', ...)没做去重
旧版本SDK的令牌生成bug:早期版本的AWS JS SDK v2的Document Client在生成自动令牌时,可能存在短时间内的随机数碰撞问题。如果你的服务在高并发场景下,两个不同的请求可能会生成相同的
ClientRequestToken,当第一个事务还在处理时,第二个请求就会触发这个异常。DynamoDB事务的锁延迟:如果你的事务涉及到热门的表项,DynamoDB会因为锁竞争而延长事务的处理时间。这时候哪怕你只触发了一次请求,但如果你的代码设置的超时时间过短,手动发起了重试(或者SDK的重试触发条件包含了超时),也会遇到这个错误。
排查建议
- 临时禁用SDK重试:在初始化Document Client时设置
maxRetries: 0,看是否还会出现异常。如果消失了,那就是重试机制的问题。 - 手动指定并记录令牌:在调用
transactWriteItems时手动传入ClientRequestToken参数(比如用UUID生成),并在日志里记录每次调用的令牌值,这样就能明确是否有重复令牌的请求被发送。 - 排查异步逻辑:在事务调用前后加详细日志,记录调用的时间、调用栈,看是否有重复执行的情况。
- 升级SDK版本:如果用的是旧版SDK,升级到最新的v2稳定版或者v3版本,新版本修复了很多令牌生成和重试的bug。
内容的提问来源于stack exchange,提问作者cvyrtik

