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;

若核心商品已更新、自定义表未更新,说明存在部分提交,修复代码后还要补数据。正式发布前执行:新商品保存、已有商品保存、批量属性更新、导入、库存同步五组回归;同时观察异常日志与索引状态。仅仅让当前商品能保存,不足以证明事务边界已经正确。