This is an automated email from the ASF dual-hosted git repository.

lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git


The following commit(s) were added to refs/heads/rocketmq-studio by this push:
     new ec071158 fix: keep instance updates atomic on save failures (#732)
ec071158 is described below

commit ec07115806b8dfd12bcf6ed20a40a19e14ea1b37
Author: Rui <[email protected]>
AuthorDate: Mon Aug 3 11:15:54 2026 +0800

    fix: keep instance updates atomic on save failures (#732)
---
 .../rocketmq/studio/instance/InstanceService.java  | 28 +++++++---
 .../studio/instance/InstanceServiceTest.java       | 60 +++++++++++++++++++++-
 2 files changed, 81 insertions(+), 7 deletions(-)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceService.java 
b/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceService.java
index 4269c9d9..0e17a59c 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceService.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/instance/InstanceService.java
@@ -81,21 +81,22 @@ public class InstanceService {
             throw new BusinessException(400, "InstanceVO endpoint is 
required");
         }
 
+        InstanceVO updated = copyOf(existing);
         if (instance.getName() != null) {
-            existing.setName(instance.getName());
+            updated.setName(instance.getName());
         }
         if (instance.getType() != null) {
-            existing.setType(instance.getType());
+            updated.setType(instance.getType());
         }
         if (instance.getEndpoint() != null) {
-            existing.setEndpoint(instance.getEndpoint());
+            updated.setEndpoint(instance.getEndpoint());
         }
         if (instance.getRemark() != null) {
-            existing.setRemark(instance.getRemark());
+            updated.setRemark(instance.getRemark());
         }
-        existing.setUpdatedAt(LocalDateTime.now());
+        updated.setUpdatedAt(LocalDateTime.now());
 
-        return instanceRepository.save(existing);
+        return instanceRepository.save(updated);
     }
 
     public void deleteInstance(String id) {
@@ -109,4 +110,19 @@ public class InstanceService {
                 .orElseThrow(() -> new BusinessException(404, "InstanceVO not 
found: " + id));
         instanceRepository.deleteById(id);
     }
+
+    private InstanceVO copyOf(InstanceVO instance) {
+        InstanceVO copy = InstanceVO.builder()
+                .name(instance.getName())
+                .remark(instance.getRemark())
+                .type(instance.getType())
+                .endpoint(instance.getEndpoint())
+                .topicCount(instance.getTopicCount())
+                .consumerGroupCount(instance.getConsumerGroupCount())
+                .build();
+        copy.setId(instance.getId());
+        copy.setCreatedAt(instance.getCreatedAt());
+        copy.setUpdatedAt(instance.getUpdatedAt());
+        return copy;
+    }
 }
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/instance/InstanceServiceTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/instance/InstanceServiceTest.java
index 63dcf377..bae0806e 100644
--- 
a/server/src/test/java/org/apache/rocketmq/studio/instance/InstanceServiceTest.java
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/instance/InstanceServiceTest.java
@@ -25,6 +25,7 @@ import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.junit.jupiter.MockitoExtension;
 
+import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Optional;
 
@@ -203,13 +204,19 @@ class InstanceServiceTest {
 
     @Test
     void updateInstanceShouldMergeFieldsOntoExisting() {
+        LocalDateTime originalCreatedAt = LocalDateTime.of(2025, 1, 2, 3, 4, 
5);
+        LocalDateTime originalUpdatedAt = LocalDateTime.of(2025, 2, 3, 4, 5, 
6);
         InstanceVO existing = InstanceVO.builder()
                 .name("old-name")
                 .endpoint("10.0.1.1:8080")
                 .type(InstanceType.PROXY)
                 .remark("old remark")
+                .topicCount(7)
+                .consumerGroupCount(3)
                 .build();
         existing.setId("inst-1");
+        existing.setCreatedAt(originalCreatedAt);
+        existing.setUpdatedAt(originalUpdatedAt);
 
         InstanceVO update = InstanceVO.builder()
                 .name("new-name")
@@ -226,7 +233,58 @@ class InstanceServiceTest {
         assertThat(result.getEndpoint()).isEqualTo("10.0.1.1:8080");
         assertThat(result.getType()).isEqualTo(InstanceType.PROXY);
         assertThat(result.getRemark()).isEqualTo("new remark");
-        assertThat(result.getUpdatedAt()).isNotNull();
+        assertThat(result.getTopicCount()).isEqualTo(7);
+        assertThat(result.getConsumerGroupCount()).isEqualTo(3);
+        assertThat(result.getId()).isEqualTo("inst-1");
+        assertThat(result.getCreatedAt()).isEqualTo(originalCreatedAt);
+        assertThat(result.getUpdatedAt()).isAfter(originalUpdatedAt);
+        assertThat(result).isNotSameAs(existing);
+        assertThat(existing.getName()).isEqualTo("old-name");
+        assertThat(existing.getRemark()).isEqualTo("old remark");
+        assertThat(existing.getUpdatedAt()).isEqualTo(originalUpdatedAt);
+    }
+
+    @Test
+    void updateInstanceShouldNotMutateStoredInstanceWhenSaveFails() {
+        LocalDateTime originalCreatedAt = LocalDateTime.of(2025, 1, 2, 3, 4, 
5);
+        LocalDateTime originalUpdatedAt = LocalDateTime.of(2025, 2, 3, 4, 5, 
6);
+        InstanceVO stored = InstanceVO.builder()
+                .name("old-name")
+                .remark("old remark")
+                .type(InstanceType.PROXY)
+                .endpoint("10.0.1.1:8080")
+                .topicCount(7)
+                .consumerGroupCount(3)
+                .build();
+        stored.setId("inst-1");
+        stored.setCreatedAt(originalCreatedAt);
+        stored.setUpdatedAt(originalUpdatedAt);
+
+        InstanceVO update = InstanceVO.builder()
+                .name("new-name")
+                .remark("new remark")
+                .type(InstanceType.DIRECT)
+                .endpoint("10.0.2.2:10911")
+                .build();
+        update.setId("inst-1");
+
+        
when(instanceRepository.findById("inst-1")).thenReturn(Optional.of(stored));
+        when(instanceRepository.save(any(InstanceVO.class)))
+                .thenThrow(new IllegalStateException("storage unavailable"));
+
+        assertThatThrownBy(() -> instanceService.updateInstance(update))
+                .isInstanceOf(IllegalStateException.class)
+                .hasMessage("storage unavailable");
+
+        assertThat(stored.getName()).isEqualTo("old-name");
+        assertThat(stored.getRemark()).isEqualTo("old remark");
+        assertThat(stored.getType()).isEqualTo(InstanceType.PROXY);
+        assertThat(stored.getEndpoint()).isEqualTo("10.0.1.1:8080");
+        assertThat(stored.getTopicCount()).isEqualTo(7);
+        assertThat(stored.getConsumerGroupCount()).isEqualTo(3);
+        assertThat(stored.getId()).isEqualTo("inst-1");
+        assertThat(stored.getCreatedAt()).isEqualTo(originalCreatedAt);
+        assertThat(stored.getUpdatedAt()).isEqualTo(originalUpdatedAt);
     }
 
     @Test

Reply via email to