fix(firstleg): avoid shared lock upgrades when initializing customs
This commit is contained in:
+2
-1
@@ -88,9 +88,10 @@ public interface ContainerCustomsMapper {
|
||||
Long completedPlanForOrder(@Param("tenantId") long tenantId, @Param("orderId") long orderId);
|
||||
|
||||
@Insert("""
|
||||
INSERT IGNORE INTO fl_customs_container
|
||||
INSERT INTO fl_customs_container
|
||||
(id,loading_plan_id,container_booking_id,status,created_at,updated_at,version)
|
||||
VALUES (#{id},#{planId},#{containerBookingId},#{status},CURRENT_TIMESTAMP(3),CURRENT_TIMESTAMP(3),0)
|
||||
ON DUPLICATE KEY UPDATE id=id
|
||||
""")
|
||||
int insertContainer(@Param("id") long id,@Param("planId") long planId,
|
||||
@Param("containerBookingId") long containerBookingId,@Param("status") String status);
|
||||
|
||||
+4
-3
@@ -563,9 +563,10 @@ public class ContainerCustomsService {
|
||||
if (!"COMPLETED".equals(plan.loadingPlanStatus())) throw conflict("装柜完成后才能处理报关资料");
|
||||
boolean refund = rows.stream().anyMatch(row -> "TAX_REFUND".equals(row.taxMode()));
|
||||
mapper.insertContainer(IdWorker.getId(), planId, plan.containerBookingId(), refund ? WAITING : READY);
|
||||
// A concurrent INSERT IGNORE may wait for another request's insert to commit. The
|
||||
// transaction's earlier plan reads can retain an RR snapshot that cannot see that row.
|
||||
// Read the winning aggregate under its row lock, rather than reusing a snapshot read.
|
||||
// The no-op upsert already takes an exclusive lock on a duplicate, avoiding the
|
||||
// shared-to-exclusive lock upgrade of INSERT IGNORE followed by FOR UPDATE.
|
||||
// Earlier plan reads may retain an RR snapshot predating the winning insert, so
|
||||
// retrieve the real aggregate with a current read, not a snapshot read.
|
||||
ContainerRow container = mapper.lockContainer(planId);
|
||||
if (container == null) throw conflict("整柜报关记录初始化失败");
|
||||
return refresh(planId, container);
|
||||
|
||||
+7
@@ -11,6 +11,13 @@ import java.util.List;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
class ContainerCustomsMapperContractTest {
|
||||
@Test void duplicateInitializationTakesUpdateLockWithoutChangingExistingFacts() throws Exception {
|
||||
String sql = String.join(" ", ContainerCustomsMapper.class.getMethod("insertContainer",
|
||||
long.class,long.class,long.class,String.class).getAnnotation(org.apache.ibatis.annotations.Insert.class).value());
|
||||
assertThat(sql).contains("ON DUPLICATE KEY UPDATE id=id").doesNotContain("INSERT IGNORE");
|
||||
assertThat(sql.substring(sql.indexOf("ON DUPLICATE KEY UPDATE"))).isEqualTo("ON DUPLICATE KEY UPDATE id=id\n");
|
||||
}
|
||||
|
||||
@Test void initializationFlushesSessionCacheBeforeCurrentAggregateRead() {
|
||||
var configuration = new org.apache.ibatis.session.Configuration();
|
||||
configuration.addMapper(ContainerCustomsMapper.class);
|
||||
|
||||
Reference in New Issue
Block a user