Asymmetric transaction rollback 表示 Magento 认为当前资源连接的事务嵌套层级已经失衡。常见情况是核心保存流程开启事务后,自定义插件、Observer 或第三方扩展又直接调用了 commit() 或 rollBack(),导致外层结束时找不到对应事务。它不是“数据库偶尔抽风”,重复点击保存可能扩大部分写入的问题。
保留一次完整失败现场
先复制商品 SKU、管理员、发生时间和本次修改字段。随后查看同一秒内的异常链:
grep -n -B20 -A80 'Asymmetric transaction' var/log/exception.log
grep -n -B20 -A80 'Asymmetric transaction' var/log/system.log
tail -n 200 var/log/debug.log
如果生产模式没有足够堆栈,在预发布环境用相同商品数据复现。不要为了取堆栈长时间打开前台 display_errors。
先判断是否只有特定商品或特定操作触发
| 测试 | 结果指向 |
|---|---|
| 只改商品名称 | 基础保存、URL rewrite 或索引插件 |
| 只改库存 | MSI、ERP 同步或库存 Observer |
| 只改图片 | 媒体库、图片处理扩展 |
| 新建最小商品 | 全局插件或数据库触发器 |
每次只改变一个变量,并使用新测试 SKU,避免前一次半保存状态影响判断。
扫描可疑的事务控制代码
grep -R --line-number --include='*.php' -E '\->(beginTransaction|commit|rollBack)\(' app/code vendor/vendorname 2>/dev/null
grep -R --line-number --include='di.xml' 'ProductRepository\|Product\\Save' app/code vendor/vendorname 2>/dev/null
grep -R --line-number --include='events.xml' 'catalog_product_save' app/code vendor/vendorname 2>/dev/null
优先检查最近上线模块与堆栈中出现的命名空间。不要修改 vendor 里的文件验证;可以通过 bin/magento module:disable Vendor_Module 在预发布环境做 A/B 测试,然后重新编译和清缓存。
错误写法与正确边界
// 风险:插件进入时外层可能已经在事务中
$connection->beginTransaction();
try {
$subject->save($entity);
$connection->commit();
} catch (\Throwable $e) {
$connection->rollBack();
throw $e;
}
插件不应再次调用同一个 repository 的 save,也不应擅自结束由核心流程开启的事务。若业务确实需要同时写自定义表,优先把写入放在明确的服务层,让同一 ResourceConnection 管理完整边界;或者在提交后事件中执行可补偿的异步任务。
检查数据库是否已有部分写入
保存失败后,用 SKU 查主实体的更新时间,再核对自定义表、库存和 URL rewrite。只读查询示例:
SELECT entity_id, sku, updated_at
FROM catalog_product_entity WHERE sku='TEST-SKU';
SELECT request_path, target_path, store_id
FROM url_rewrite
WHERE entity_type='product' AND entity_id=123;
若核心商品已更新、自定义表未更新,说明存在部分提交,修复代码后还要补数据。正式发布前执行:新商品保存、已有商品保存、批量属性更新、导入、库存同步五组回归;同时观察异常日志与索引状态。仅仅让当前商品能保存,不足以证明事务边界已经正确。

