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 6f5535c57 fix(nameserver): make registry replacements atomic (#7231)
6f5535c57 is described below

commit 6f5535c576f6054d14fc6616a6b5216dee0fd552
Author: aias00 <[email protected]>
AuthorDate: Mon Oct 26 16:35:28 2026 +0800

    fix(nameserver): make registry replacements atomic (#7231)
    
    * fix(nameserver): make registry replacements atomic
---
 .../nameserver/NameserverRegistryService.java      |   2 +
 .../NameserverRegistryTransactionTest.java         | 130 +++++++++++++++++++++
 2 files changed, 132 insertions(+)

diff --git 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryService.java
 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryService.java
index c964291ae..01546ba86 100644
--- 
a/server/src/main/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryService.java
+++ 
b/server/src/main/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryService.java
@@ -24,6 +24,7 @@ import 
org.apache.rocketmq.studio.persistence.entity.RmqNameserver;
 import org.apache.rocketmq.studio.persistence.mapper.RmqNameserverMapper;
 import org.springframework.dao.DataIntegrityViolationException;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
 
 import java.util.List;
 
@@ -75,6 +76,7 @@ public class NameserverRegistryService {
         return toVO(stored);
     }
 
+    @Transactional
     public NameserverRegistryVO update(UpdateNameserverRegistryDTO command) {
         RmqNameserver entity = nameserverMapper.selectById(command.getId());
         if (entity == null) {
diff --git 
a/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryTransactionTest.java
 
b/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryTransactionTest.java
new file mode 100644
index 000000000..6e98ba90d
--- /dev/null
+++ 
b/server/src/test/java/org/apache/rocketmq/studio/cluster/nameserver/NameserverRegistryTransactionTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.rocketmq.studio.cluster.nameserver;
+
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
+import org.apache.rocketmq.studio.persistence.entity.RmqNameserver;
+import org.apache.rocketmq.studio.persistence.mapper.RmqNameserverMapper;
+import org.junit.jupiter.api.Test;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.bean.override.mockito.MockitoSpyBean;
+
+import java.util.UUID;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.isNull;
+import static org.mockito.Mockito.doAnswer;
+
+@SpringBootTest(properties = {
+    
"spring.datasource.url=jdbc:h2:mem:nameserver_registry_atomic;MODE=MySQL;DB_CLOSE_DELAY=-1;"
+            + "DATABASE_TO_LOWER=TRUE;NON_KEYWORDS=VALUE",
+    "studio.auth.login-required=false"
+})
+@ActiveProfiles("dev")
+class NameserverRegistryTransactionTest {
+
+    @Autowired
+    private NameserverRegistryService service;
+
+    @Autowired
+    private JdbcTemplate jdbcTemplate;
+
+    @MockitoSpyBean
+    private RmqNameserverMapper nameserverMapper;
+
+    @Test
+    void failedOptionalColumnCleanupShouldRollBackTheRegistryUpdateTest() {
+        RmqNameserver original = insertEntry();
+        String updatedName = "renamed-" + UUID.randomUUID();
+        AtomicBoolean cleanupReached = new AtomicBoolean();
+        try {
+            doAnswer(invocation -> {
+                assertThat(jdbcTemplate.queryForObject(
+                        "SELECT name FROM rmq_nameserver WHERE id = ?", 
String.class, original.getId()))
+                        .isEqualTo(updatedName);
+                cleanupReached.set(true);
+                throw new IllegalStateException("optional column cleanup 
failed");
+            }).when(nameserverMapper).update(isNull(), 
any(UpdateWrapper.class));
+
+            assertThatThrownBy(() -> 
service.update(updateCommand(original.getId(), updatedName)))
+                    .isInstanceOf(IllegalStateException.class)
+                    .hasMessage("optional column cleanup failed");
+
+            assertThat(cleanupReached).isTrue();
+            RmqNameserver persisted = 
nameserverMapper.selectById(original.getId());
+            assertThat(persisted.getName()).isEqualTo(original.getName());
+            
assertThat(persisted.getNamesrvAddr()).isEqualTo(original.getNamesrvAddr());
+            
assertThat(persisted.getK8sNamespace()).isEqualTo(original.getK8sNamespace());
+            assertThat(persisted.getK8sId()).isEqualTo(original.getK8sId());
+            
assertThat(persisted.getDescription()).isEqualTo(original.getDescription());
+        } finally {
+            deleteEntry(original.getId());
+        }
+    }
+
+    @Test
+    void 
successfulUpdateShouldCommitTheReplacementAndClearOptionalColumnsTest() {
+        RmqNameserver original = insertEntry();
+        String updatedName = "renamed-" + UUID.randomUUID();
+        try {
+            NameserverRegistryVO result = 
service.update(updateCommand(original.getId(), updatedName));
+
+            RmqNameserver persisted = 
nameserverMapper.selectById(original.getId());
+            assertThat(persisted.getName()).isEqualTo(updatedName);
+            
assertThat(persisted.getNamesrvAddr()).isEqualTo("new-nameserver:9876");
+            assertThat(persisted.getK8sNamespace()).isNull();
+            assertThat(persisted.getK8sId()).isNull();
+            assertThat(persisted.getDescription()).isNull();
+            assertThat(result.getName()).isEqualTo(persisted.getName());
+            
assertThat(result.getNamesrvAddr()).isEqualTo(persisted.getNamesrvAddr());
+            assertThat(result.getK8sNamespace()).isNull();
+            assertThat(result.getK8sId()).isNull();
+            assertThat(result.getDescription()).isNull();
+        } finally {
+            deleteEntry(original.getId());
+        }
+    }
+
+    private RmqNameserver insertEntry() {
+        RmqNameserver entry = new RmqNameserver();
+        entry.setName("original-" + UUID.randomUUID());
+        entry.setNamesrvAddr("old-nameserver:9876");
+        entry.setK8sNamespace("old-namespace");
+        entry.setK8sId("old-k8s");
+        entry.setDescription("old description");
+        nameserverMapper.insert(entry);
+        return entry;
+    }
+
+    private UpdateNameserverRegistryDTO updateCommand(Long id, String name) {
+        return UpdateNameserverRegistryDTO.builder()
+                .id(id)
+                .name(name)
+                .namesrvAddr("new-nameserver:9876")
+                .build();
+    }
+
+    private void deleteEntry(Long id) {
+        jdbcTemplate.update("DELETE FROM rmq_nameserver WHERE id = ?", id);
+    }
+}

Reply via email to