From 892e45141a55bfea0e28c6a34a384f2ce8ee7d16 Mon Sep 17 00:00:00 2001
From: Junjie <fallin.jie@qq.com>
Date: 星期一, 27 四月 2026 13:20:17 +0800
Subject: [PATCH] fix: make auto tune rollback auditable atomically
---
src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java | 92 ++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 92 insertions(+), 0 deletions(-)
diff --git a/src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java b/src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java
index 138d1f1..13c5bcd 100644
--- a/src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java
+++ b/src/test/java/com/zy/ai/service/AutoTuneApplyServiceImplTest.java
@@ -427,6 +427,98 @@
verify(configService).saveConfigValue("conveyorStationTaskLimit", "10");
verify(configService).refreshSystemConfigCache();
verify(basStationService).update(any(Wrapper.class));
+ verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString());
+ assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount());
+ assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount());
+ }
+
+ @Test
+ void rollbackAuditSaveBatchFailureRollsBackTargetWriteAndReturnsFailedAudit() {
+ AiAutoTuneJob latestRealJob = job(10L, "manual", "success");
+ AiAutoTuneChange configChange = successChange(10L, "sys_config", "", "conveyorStationTaskLimit", "10", "15");
+ when(aiAutoTuneChangeService.list(any(Wrapper.class)))
+ .thenReturn(List.of(configChange))
+ .thenReturn(List.of(configChange));
+ when(aiAutoTuneJobService.getById(10L)).thenReturn(latestRealJob);
+ when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "15"));
+ when(aiAutoTuneChangeService.saveBatch(any(Collection.class)))
+ .thenThrow(new IllegalStateException("rollback audit failed"))
+ .thenReturn(true);
+
+ AutoTuneApplyResult result = service.rollbackLastSuccessfulJob("manual rollback");
+
+ List<AiAutoTuneChange> changes = savedChanges();
+ AiAutoTuneJob updatedJob = updatedJob();
+ assertFalse(result.getSuccess());
+ assertEquals("failed", updatedJob.getStatus());
+ assertEquals("failed", changes.get(0).getResultStatus());
+ assertTrue(changes.get(0).getRejectReason().contains("rollback audit failed"));
+ assertEquals(1, transactionManager.getRollbackCount());
+ assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0);
+ assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0);
+ assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount());
+ assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount());
+ verify(configService).saveConfigValue("conveyorStationTaskLimit", "10");
+ verify(configService, never()).refreshSystemConfigCache();
+ verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString());
+ }
+
+ @Test
+ void rollbackJobUpdateFailureRollsBackAndDoesNotSaveRunningJobOutsideTransaction() {
+ AiAutoTuneJob latestRealJob = job(10L, "manual", "success");
+ AiAutoTuneChange configChange = successChange(10L, "sys_config", "", "conveyorStationTaskLimit", "10", "15");
+ when(aiAutoTuneChangeService.list(any(Wrapper.class)))
+ .thenReturn(List.of(configChange))
+ .thenReturn(List.of(configChange));
+ when(aiAutoTuneJobService.getById(10L)).thenReturn(latestRealJob);
+ when(configService.getOne(any(Wrapper.class))).thenReturn(config("conveyorStationTaskLimit", "15"));
+ when(aiAutoTuneJobService.updateById(any(AiAutoTuneJob.class))).thenAnswer(invocation -> {
+ transactionManager.recordJobUpdateCall();
+ return false;
+ });
+
+ IllegalStateException exception = assertThrows(IllegalStateException.class,
+ () -> service.rollbackLastSuccessfulJob("manual rollback"));
+
+ assertTrue(exception.getMessage().contains("鏇存柊璋冨弬浠诲姟鐘舵�佸け璐�"));
+ assertEquals(2, transactionManager.getRollbackCount());
+ assertEquals(0, transactionManager.getCommitCount());
+ assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0);
+ assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0);
+ assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount());
+ assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount());
+ verify(configService).saveConfigValue("conveyorStationTaskLimit", "10");
+ verify(configService, never()).refreshSystemConfigCache();
+ verify(redisUtil).compareAndDelete(eq(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key), anyString());
+ }
+
+ @Test
+ void rollbackLockNotAcquiredReturnsFailedAuditWithoutTargetWrite() {
+ AiAutoTuneJob latestRealJob = job(10L, "manual", "success");
+ AiAutoTuneChange configChange = successChange(10L, "sys_config", "", "conveyorStationTaskLimit", "10", "15");
+ when(aiAutoTuneChangeService.list(any(Wrapper.class)))
+ .thenReturn(List.of(configChange))
+ .thenReturn(List.of(configChange));
+ when(aiAutoTuneJobService.getById(10L)).thenReturn(latestRealJob);
+ when(redisUtil.trySetStringIfAbsent(anyString(), anyString(), anyLong())).thenReturn(false);
+
+ AutoTuneApplyResult result = service.rollbackLastSuccessfulJob("manual rollback");
+
+ List<AiAutoTuneChange> changes = savedChanges();
+ AiAutoTuneJob updatedJob = updatedJob();
+ assertFalse(result.getSuccess());
+ assertEquals("failed", updatedJob.getStatus());
+ assertEquals("failed", changes.get(0).getResultStatus());
+ assertTrue(changes.get(0).getRejectReason().contains("閿佷笉鍙敤"));
+ assertTrue(changes.get(0).getRejectReason().contains("Redis"));
+ assertTrue(transactionManager.getJobSaveInsideTransactionCount() > 0);
+ assertTrue(transactionManager.getJobUpdateInsideTransactionCount() > 0);
+ assertEquals(0, transactionManager.getJobSaveOutsideTransactionCount());
+ assertEquals(0, transactionManager.getJobUpdateOutsideTransactionCount());
+ verify(configService, never()).saveConfigValue(any(), any());
+ verify(configService, never()).refreshSystemConfigCache();
+ verify(redisUtil).hasKey(RedisKeyType.AI_AUTO_TUNE_APPLY_LOCK.key);
+ verify(redisUtil, never()).compareAndDelete(anyString(), anyString());
}
@Test
--
Gitblit v1.9.1