问题描述

@GlobalTransactional
public void inStock(Input input) {
	...
	rpc.xxx;
	repository.generateSku(skuIds);
	if (Objects.equals(CommodityType.ASSIT,input.getCommodityType())) {
		throw new XXXException("xxx");
	}
	rpc.xxx;
	...
}

@Transactional
public void generateSku(Set<Integer> skuIds) {
	...
	mapper.insertIgnore(xxxPO);
	...
}
<insert id="insertIgnore">
        INSERT INTO commodity_sku ( sku_id,...)
        VALUES ( #{sku_id} )
            ON CONFLICT (sku_id) DO NOTHING;
    </insert>

当执行了带有insert on conflict语句后,如果发生冲突,数据库中原来就有某条数据,是不会执行insert操作;且后续执行过程中,程序抛出了业务异常;那么seata就会误认为这条原来就在的数据是insert进去的,并在回滚中将其删除,导致原有的数据消失。

排查过程

在generateSku的方法下一段代码(if判断处)打断点(seata会在一个事务执行完后才会插入undo_log),此时查看undo_log的rollback_info(Text)信息;可以看到seata将这条sql标记为普通的INSERT类型;
undo_log的rollback信息格式化后
同时,在io.seata.sqlparser.SQLType中,我们也找不到一个类型能够对应上pg的insert on conflict;(INSERT_ON_DUPLICATE_UPDATE是针对mysql的);查阅官网,也可以得知,seata并不支持对postgres数据库的insert on conflict回滚。

解决方案

把insert on conflict改成先查一遍,对比数据后,按需正常insert插入没有的数据即可;

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐