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()