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);
|
Long completedPlanForOrder(@Param("tenantId") long tenantId, @Param("orderId") long orderId);
|
||||||
|
|
||||||
@Insert("""
|
@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)
|
(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)
|
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,
|
int insertContainer(@Param("id") long id,@Param("planId") long planId,
|
||||||
@Param("containerBookingId") long containerBookingId,@Param("status") String status);
|
@Param("containerBookingId") long containerBookingId,@Param("status") String status);
|
||||||
|
|||||||
+4
-3
@@ -563,9 +563,10 @@ public class ContainerCustomsService {
|
|||||||
if (!"COMPLETED".equals(plan.loadingPlanStatus())) throw conflict("装柜完成后才能处理报关资料");
|
if (!"COMPLETED".equals(plan.loadingPlanStatus())) throw conflict("装柜完成后才能处理报关资料");
|
||||||
boolean refund = rows.stream().anyMatch(row -> "TAX_REFUND".equals(row.taxMode()));
|
boolean refund = rows.stream().anyMatch(row -> "TAX_REFUND".equals(row.taxMode()));
|
||||||
mapper.insertContainer(IdWorker.getId(), planId, plan.containerBookingId(), refund ? WAITING : READY);
|
mapper.insertContainer(IdWorker.getId(), planId, plan.containerBookingId(), refund ? WAITING : READY);
|
||||||
// A concurrent INSERT IGNORE may wait for another request's insert to commit. The
|
// The no-op upsert already takes an exclusive lock on a duplicate, avoiding the
|
||||||
// transaction's earlier plan reads can retain an RR snapshot that cannot see that row.
|
// shared-to-exclusive lock upgrade of INSERT IGNORE followed by FOR UPDATE.
|
||||||
// Read the winning aggregate under its row lock, rather than reusing a snapshot read.
|
// 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);
|
ContainerRow container = mapper.lockContainer(planId);
|
||||||
if (container == null) throw conflict("整柜报关记录初始化失败");
|
if (container == null) throw conflict("整柜报关记录初始化失败");
|
||||||
return refresh(planId, container);
|
return refresh(planId, container);
|
||||||
|
|||||||
+7
@@ -11,6 +11,13 @@ import java.util.List;
|
|||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class ContainerCustomsMapperContractTest {
|
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() {
|
@Test void initializationFlushesSessionCacheBeforeCurrentAggregateRead() {
|
||||||
var configuration = new org.apache.ibatis.session.Configuration();
|
var configuration = new org.apache.ibatis.session.Configuration();
|
||||||
configuration.addMapper(ContainerCustomsMapper.class);
|
configuration.addMapper(ContainerCustomsMapper.class);
|
||||||
|
|||||||
Reference in New Issue
Block a user