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 447206b995 perf(admin): coalesce selector import events (#7181)
447206b995 is described below

commit 447206b9952631380f9c843635988cb1eac5776e
Author: Liming Deng <[email protected]>
AuthorDate: Sat Oct 17 20:48:43 2026 +0800

    perf(admin): coalesce selector import events (#7181)
---
 .../admin/service/impl/SelectorServiceImpl.java    | 48 ++++++++++++++++++----
 .../shenyu/admin/service/SelectorServiceTest.java  | 44 ++++++++++++++++++--
 2 files changed, 80 insertions(+), 12 deletions(-)

diff --git 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SelectorServiceImpl.java
 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SelectorServiceImpl.java
index 5c08df0284..84718f352f 100644
--- 
a/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SelectorServiceImpl.java
+++ 
b/shenyu-admin/src/main/java/org/apache/shenyu/admin/service/impl/SelectorServiceImpl.java
@@ -94,6 +94,7 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.LinkedHashMap;
 import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
@@ -515,6 +516,7 @@ public class SelectorServiceImpl implements SelectorService 
{
         }
         StringBuilder errorMsgBuilder = new StringBuilder();
         int successCount = 0;
+        List<SelectorData> importedSelectors = new ArrayList<>();
         Map<String, List<SelectorDO>> pluginSelectorMap = 
selectorMapper.selectAll().stream()
                 .filter(Objects::nonNull)
                 .collect(Collectors.groupingBy(SelectorDO::getPluginId));
@@ -544,12 +546,14 @@ public class SelectorServiceImpl implements 
SelectorService {
                                 .append(selectorName)
                                 .append(",");
                     } else {
-                        create(selectorDTO);
-                        successCount++;
+                        if (createForImport(selectorDTO, importedSelectors) > 
0) {
+                            successCount++;
+                        }
                     }
                 }
             }
         }
+        publishImportEvent(importedSelectors);
         if (StringUtils.isNotEmpty(errorMsgBuilder)) {
             errorMsgBuilder.setLength(errorMsgBuilder.length() - 1);
             return ConfigImportResult
@@ -567,6 +571,7 @@ public class SelectorServiceImpl implements SelectorService 
{
         Map<String, String> selectorIdMapping = context.getSelectorIdMapping();
         StringBuilder errorMsgBuilder = new StringBuilder();
         int successCount = 0;
+        List<SelectorData> importedSelectors = new ArrayList<>();
         Map<String, Map<String, String>> pluginSelectorMap = 
selectorMapper.selectAllByNamespaceId(namespace).stream()
                 .filter(Objects::nonNull)
                 .collect(Collectors.groupingBy(SelectorDO::getPluginId,
@@ -604,7 +609,7 @@ public class SelectorServiceImpl implements SelectorService 
{
                                             c.setSelectorId(selectorId);
                                             c.setId(null);
                                         });
-                        if (create(selectorDTO) > 0) {
+                        if (createForImport(selectorDTO, importedSelectors) > 
0) {
                             existSelectorSet.put(selectorName, selectorId);
                             selectorIdMapping.put(oldSelectorId, selectorId);
                             successCount++;
@@ -613,6 +618,7 @@ public class SelectorServiceImpl implements SelectorService 
{
                 }
             }
         }
+        publishImportEvent(importedSelectors);
         if (StringUtils.isNotEmpty(errorMsgBuilder)) {
             errorMsgBuilder.setLength(errorMsgBuilder.length() - 1);
             return ConfigImportResult
@@ -683,14 +689,40 @@ public class SelectorServiceImpl implements 
SelectorService {
     }
 
     private void publishEvent(final SelectorDO selectorDO, final 
List<SelectorConditionDTO> selectorConditions, final List<SelectorConditionDO> 
beforeSelectorCondition) {
+        SelectorData selectorData = buildSelectorData(selectorDO, 
selectorConditions, beforeSelectorCondition);
+        eventPublisher.publishEvent(new 
DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE,
+                Collections.singletonList(selectorData)));
+    }
+
+    private int createForImport(final SelectorDTO selectorDTO, final 
List<SelectorData> importedSelectors) {
+        SelectorDO selectorDO = SelectorDO.buildSelectorDO(selectorDTO);
+        final int selectorCount = selectorMapper.insertSelective(selectorDO);
+        selectorDTO.setId(selectorDO.getId());
+        createCondition(selectorDO.getId(), 
selectorDTO.getSelectorConditions());
+        if (selectorCount > 0) {
+            importedSelectors.add(buildSelectorData(selectorDO, 
selectorDTO.getSelectorConditions(), Collections.emptyList()));
+            selectorEventPublisher.onCreated(selectorDO);
+        }
+        return selectorCount;
+    }
+
+    private void publishImportEvent(final List<SelectorData> 
importedSelectors) {
+        Map<String, List<SelectorData>> selectorsByNamespace = new 
LinkedHashMap<>();
+        for (SelectorData selector : importedSelectors) {
+            String namespaceId = 
StringUtils.defaultIfEmpty(selector.getNamespaceId(), SYS_DEFAULT_NAMESPACE_ID);
+            selector.setNamespaceId(namespaceId);
+            selectorsByNamespace.computeIfAbsent(namespaceId, key -> new 
ArrayList<>()).add(selector);
+        }
+        selectorsByNamespace.values().forEach(selectors ->
+                eventPublisher.publishEvent(new 
DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE, 
selectors)));
+    }
+
+    private SelectorData buildSelectorData(final SelectorDO selectorDO, final 
List<SelectorConditionDTO> selectorConditions,
+                                           final List<SelectorConditionDO> 
beforeSelectorCondition) {
         PluginDO pluginDO = pluginMapper.selectById(selectorDO.getPluginId());
         List<ConditionData> conditionDataList = 
ListUtil.map(selectorConditions, ConditionTransfer.INSTANCE::mapToSelectorDTO);
         List<ConditionData> beforeConditionDataList = 
ListUtil.map(beforeSelectorCondition, 
ConditionTransfer.INSTANCE::mapToSelectorDO);
-        // build selector data.
-        SelectorData selectorData = SelectorDO.transFrom(selectorDO, 
pluginDO.getName(), conditionDataList, beforeConditionDataList);
-        // publish change event.
-        eventPublisher.publishEvent(new 
DataChangedEvent(ConfigGroupEnum.SELECTOR, DataEventTypeEnum.UPDATE,
-                Collections.singletonList(selectorData)));
+        return SelectorDO.transFrom(selectorDO, pluginDO.getName(), 
conditionDataList, beforeConditionDataList);
     }
 
     private SelectorData buildSelectorData(final SelectorDO selectorDO) {
diff --git 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/SelectorServiceTest.java
 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/SelectorServiceTest.java
index 65cf361621..7aa746687b 100644
--- 
a/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/SelectorServiceTest.java
+++ 
b/shenyu-admin/src/test/java/org/apache/shenyu/admin/service/SelectorServiceTest.java
@@ -20,6 +20,7 @@ package org.apache.shenyu.admin.service;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.shenyu.admin.discovery.DiscoveryProcessor;
 import org.apache.shenyu.admin.discovery.DiscoveryProcessorHolder;
+import org.apache.shenyu.admin.listener.DataChangedEvent;
 import org.apache.shenyu.admin.exception.ShenyuAdminException;
 import org.apache.shenyu.admin.mapper.DataPermissionMapper;
 import org.apache.shenyu.admin.mapper.DiscoveryHandlerMapper;
@@ -51,11 +52,13 @@ import 
org.apache.shenyu.admin.service.impl.SelectorServiceImpl;
 import org.apache.shenyu.admin.service.publish.SelectorEventPublisher;
 import org.apache.shenyu.admin.utils.JwtUtils;
 import org.apache.shenyu.common.dto.SelectorData;
+import org.apache.shenyu.common.enums.ConfigGroupEnum;
 import org.apache.shenyu.common.enums.SelectorTypeEnum;
 import org.apache.shenyu.register.common.dto.MetaDataRegisterDTO;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.ArgumentCaptor;
 import org.mockito.InjectMocks;
 import org.mockito.Mock;
 import org.mockito.MockedStatic;
@@ -91,8 +94,9 @@ import static org.mockito.BDDMockito.given;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.mockStatic;
-import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verifyNoInteractions;
 import static org.mockito.Mockito.when;
 
@@ -347,7 +351,7 @@ public final class SelectorServiceTest {
         final List<SelectorDO> selectorDOs = buildSelectorDOList();
         given(this.selectorMapper.selectAll()).willReturn(selectorDOs);
 
-        final List<SelectorDTO> selectorDTOs = buildSelectorDTOList();
+        final List<SelectorDTO> selectorDTOs = 
Arrays.asList(buildSelectorDTO("456"), buildSelectorDTO("457"));
         given(this.selectorMapper.insertSelective(any())).willReturn(1);
 
         given(this.pluginMapper.selectById(any())).willReturn(buildPluginDO());
@@ -356,10 +360,42 @@ public final class SelectorServiceTest {
 
         assertNotNull(configImportResult);
         assertEquals(configImportResult.getSuccessCount(), 
selectorDTOs.size());
+        ArgumentCaptor<DataChangedEvent> eventCaptor = 
ArgumentCaptor.forClass(DataChangedEvent.class);
+        verify(eventPublisher, times(1)).publishEvent(eventCaptor.capture());
+        assertEquals(selectorDTOs.size(), 
eventCaptor.getValue().getSource().size());
     }
 
-    private List<SelectorDTO> buildSelectorDTOList() {
-        return Collections.singletonList(buildSelectorDTO("456"));
+    @Test
+    public void testGlobalImportPublishesOneEventPerNamespace() {
+        SelectorDTO first = buildSelectorDTO("first");
+        first.setNamespaceId("namespace-a");
+        SelectorDTO second = buildSelectorDTO("second");
+        second.setNamespaceId("namespace-b");
+        SelectorDTO third = buildSelectorDTO("third");
+        third.setNamespaceId("namespace-a");
+        SelectorDTO defaultSelector = buildSelectorDTO("default");
+        defaultSelector.setNamespaceId(null);
+        SelectorDTO emptyNamespace = buildSelectorDTO("empty");
+        emptyNamespace.setNamespaceId("");
+        given(selectorMapper.selectAll()).willReturn(Collections.emptyList());
+        given(selectorMapper.insertSelective(any())).willReturn(1);
+        given(pluginMapper.selectById(any())).willReturn(buildPluginDO());
+
+        ConfigImportResult result = 
selectorService.importData(Arrays.asList(first, second, third, defaultSelector, 
emptyNamespace));
+
+        assertEquals(5, result.getSuccessCount());
+        verify(selectorEventPublisher, 
times(5)).onCreated(any(SelectorDO.class));
+        ArgumentCaptor<DataChangedEvent> events = 
ArgumentCaptor.forClass(DataChangedEvent.class);
+        verify(eventPublisher, times(3)).publishEvent(events.capture());
+        List<DataChangedEvent> published = events.getAllValues();
+        assertEquals(Arrays.asList(2, 1, 2), published.stream().map(event -> 
event.getSource().size()).toList());
+        List<String> namespaces = Arrays.asList("namespace-a", "namespace-b", 
SYS_DEFAULT_NAMESPACE_ID);
+        for (int index = 0; index < published.size(); index++) {
+            assertEquals(ConfigGroupEnum.SELECTOR, 
published.get(index).getGroupKey());
+            for (Object data : published.get(index).getSource()) {
+                assertEquals(namespaces.get(index), ((SelectorData) 
data).getNamespaceId());
+            }
+        }
     }
 
     private void testUpdate() {

Reply via email to