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

Aias00 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shenyu.git


The following commit(s) were added to refs/heads/master by this push:
     new f7248f4c0b fix(admin): activate discovery bindings after transaction 
commit (#7241)
f7248f4c0b is described below

commit f7248f4c0bc17ebfa6a279bcf3b1441e83c2c260
Author: Liming Deng <[email protected]>
AuthorDate: Wed Sep 30 09:59:08 2026 +0800

    fix(admin): activate discovery bindings after transaction commit (#7241)
---
 .../admin/service/impl/DiscoveryServiceImpl.java   | 31 +++++++++--
 .../admin/service/DiscoveryServiceImplTest.java    | 62 +++++++++++++++++++++-
 2 files changed, 89 insertions(+), 4 deletions(-)

diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DiscoveryServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DiscoveryServiceImpl.java
index a905be9c43..cbbdf9dbef 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DiscoveryServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/DiscoveryServiceImpl.java
@@ -55,6 +55,11 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.support.TransactionSynchronization;
+import 
org.springframework.transaction.support.TransactionSynchronizationManager;
+import org.springframework.transaction.support.TransactionTemplate;
 
 import java.sql.Timestamp;
 import java.util.List;
@@ -84,13 +89,16 @@ public class DiscoveryServiceImpl implements 
DiscoveryService {
 
     private final DiscoveryProcessorHolder discoveryProcessorHolder;
 
+    private final TransactionTemplate discoveryActivation;
+
     public DiscoveryServiceImpl(final DiscoveryMapper discoveryMapper,
                                 final ProxySelectorMapper proxySelectorMapper,
                                 final DiscoveryRelMapper discoveryRelMapper,
                                 final DiscoveryHandlerMapper 
discoveryHandlerMapper,
                                 final SelectorService selectorService,
                                 final SelectorMapper selectorMapper,
-                                final DiscoveryProcessorHolder 
discoveryProcessorHolder) {
+                                final DiscoveryProcessorHolder 
discoveryProcessorHolder,
+                                final PlatformTransactionManager 
transactionManager) {
         this.discoveryMapper = discoveryMapper;
         this.discoveryProcessorHolder = discoveryProcessorHolder;
         this.proxySelectorMapper = proxySelectorMapper;
@@ -98,6 +106,8 @@ public class DiscoveryServiceImpl implements 
DiscoveryService {
         this.discoveryHandlerMapper = discoveryHandlerMapper;
         this.selectorService = selectorService;
         this.selectorMapper = selectorMapper;
+        this.discoveryActivation = new TransactionTemplate(transactionManager);
+        
this.discoveryActivation.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED);
     }
 
     @Override
@@ -183,8 +193,23 @@ public class DiscoveryServiceImpl implements 
DiscoveryService {
             discoveryHandlerMapper.insertSelective(discoveryHandlerDO);
         }
         DiscoveryProcessor discoveryProcessor = 
discoveryProcessorHolder.chooseProcessor(discoveryConfigRegisterDTO.getDiscoveryType());
-        discoveryProcessor.createDiscovery(discoveryDO);
-        
discoveryProcessor.createProxySelector(DiscoveryTransfer.INSTANCE.mapToDTO(discoveryHandlerDO),
 proxySelectorDTO);
+        final DiscoveryDO registeredDiscovery = discoveryDO;
+        final DiscoveryHandlerDO registeredHandler = discoveryHandlerDO;
+        Runnable activate = () -> {
+            discoveryProcessor.createDiscovery(registeredDiscovery);
+            
discoveryProcessor.createProxySelector(DiscoveryTransfer.INSTANCE.mapToDTO(registeredHandler),
 proxySelectorDTO);
+        };
+        if (!TransactionSynchronizationManager.isSynchronizationActive()) {
+            activate.run();
+            return;
+        }
+        TransactionSynchronizationManager.registerSynchronization(new 
TransactionSynchronization() {
+            @Override
+            public void afterCommit() {
+                // Watchers may synchronously persist upstreams; suspend the 
already committed transaction resources.
+                discoveryActivation.executeWithoutResult(status -> 
activate.run());
+            }
+        });
     }
 
     @Override
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DiscoveryServiceImplTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DiscoveryServiceImplTest.java
index 060d490cb7..c84d994b1e 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DiscoveryServiceImplTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/DiscoveryServiceImplTest.java
@@ -25,6 +25,12 @@ import org.apache.shenyu.admin.mapper.DiscoveryRelMapper;
 import org.apache.shenyu.admin.mapper.ProxySelectorMapper;
 import org.apache.shenyu.admin.mapper.SelectorMapper;
 import org.apache.shenyu.admin.model.entity.DiscoveryDO;
+import org.apache.shenyu.admin.model.entity.SelectorDO;
+import org.apache.shenyu.register.common.dto.DiscoveryConfigRegisterDTO;
+import org.springframework.transaction.PlatformTransactionManager;
+import org.springframework.transaction.support.SimpleTransactionStatus;
+import org.springframework.transaction.support.TransactionSynchronization;
+import 
org.springframework.transaction.support.TransactionSynchronizationManager;
 import org.apache.shenyu.admin.service.impl.DiscoveryServiceImpl;
 import org.apache.shenyu.admin.utils.ShenyuResultMessage;
 import org.apache.shenyu.common.exception.ShenyuException;
@@ -46,6 +52,8 @@ import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoInteractions;
+import static org.mockito.Mockito.inOrder;
 
 /**
  * Test cases for DiscoveryServiceImpl.
@@ -78,15 +86,67 @@ public final class DiscoveryServiceImplTest {
     @Mock
     private DiscoveryProcessor discoveryProcessor;
 
+    @Mock
+    private PlatformTransactionManager transactionManager;
+
     private DiscoveryServiceImpl discoveryService;
 
     @BeforeEach
     public void setUp() {
         discoveryService = new DiscoveryServiceImpl(discoveryMapper, 
proxySelectorMapper, discoveryRelMapper,
-                discoveryHandlerMapper, selectorService, selectorMapper, 
discoveryProcessorHolder);
+                discoveryHandlerMapper, selectorService, selectorMapper, 
discoveryProcessorHolder, transactionManager);
         
given(discoveryProcessorHolder.chooseProcessor(anyString())).willReturn(discoveryProcessor);
     }
 
+    @Test
+    public void testBindingActivatesAfterCommit() {
+        given(transactionManager.getTransaction(any())).willReturn(new 
SimpleTransactionStatus());
+        TransactionSynchronizationManager.initSynchronization();
+        try {
+            discoveryService.registerDiscoveryConfig(bindingConfig());
+            verifyNoInteractions(discoveryProcessor);
+            
TransactionSynchronizationManager.getSynchronizations().forEach(TransactionSynchronization::afterCommit);
+            org.mockito.InOrder order = inOrder(discoveryProcessor);
+            order.verify(discoveryProcessor).createDiscovery(any());
+            order.verify(discoveryProcessor).createProxySelector(any(), any());
+        } finally {
+            TransactionSynchronizationManager.clearSynchronization();
+        }
+    }
+
+    @Test
+    public void testRolledBackBindingDoesNotActivate() {
+        TransactionSynchronizationManager.initSynchronization();
+        try {
+            discoveryService.registerDiscoveryConfig(bindingConfig());
+            
TransactionSynchronizationManager.getSynchronizations().forEach(sync -> 
sync.afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK));
+            verifyNoInteractions(discoveryProcessor, transactionManager);
+        } finally {
+            TransactionSynchronizationManager.clearSynchronization();
+        }
+    }
+
+    @Test
+    public void testBindingWithoutTransactionActivatesImmediately() {
+        discoveryService.registerDiscoveryConfig(bindingConfig());
+        verify(discoveryProcessor).createDiscovery(any());
+        verify(discoveryProcessor).createProxySelector(any(), any());
+    }
+
+    private DiscoveryConfigRegisterDTO bindingConfig() {
+        SelectorDO selector = new SelectorDO();
+        selector.setId("selector");
+        selector.setSelectorName("test");
+        selector.setNamespaceId(SYS_DEFAULT_NAMESPACE_ID);
+        
given(selectorService.findByNameAndPluginNameAndNamespaceIdForUpdate("test", 
"divide", SYS_DEFAULT_NAMESPACE_ID)).willReturn(selector);
+        DiscoveryConfigRegisterDTO config = new DiscoveryConfigRegisterDTO();
+        config.setSelectorName("test");
+        config.setPluginName("divide");
+        config.setNamespaceId(SYS_DEFAULT_NAMESPACE_ID);
+        config.setDiscoveryType("local");
+        return config;
+    }
+
     @Test
     public void testDelete() {
         DiscoveryDO discoveryDO = DiscoveryDO.builder()

Reply via email to