From cd743ac6c68847d2a8d02faaf6b67ffc1a967e2a Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Fri, 26 Dec 2025 16:53:44 +0530 Subject: [PATCH 01/24] Support multiple service configuration listeners per service --- .../integration/RegistryProtocol.java | 173 +++++++----------- .../integration/RegistryProtocolTest.java | 134 ++++++++++++-- 2 files changed, 183 insertions(+), 124 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index 17d941f55ca7..c0ad0986c249 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -23,7 +23,6 @@ import org.apache.dubbo.common.extension.ExtensionLoader; import org.apache.dubbo.common.logger.ErrorTypeAwareLogger; import org.apache.dubbo.common.logger.LoggerFactory; -import org.apache.dubbo.common.threadpool.manager.FrameworkExecutorRepository; import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.CollectionUtils; @@ -39,7 +38,6 @@ import org.apache.dubbo.registry.RegistryFactory; import org.apache.dubbo.registry.RegistryService; import org.apache.dubbo.registry.client.ServiceDiscoveryRegistryDirectory; -import org.apache.dubbo.registry.client.migration.MigrationClusterInvoker; import org.apache.dubbo.registry.client.migration.ServiceDiscoveryMigrationInvoker; import org.apache.dubbo.registry.retry.ReExportTask; import org.apache.dubbo.registry.support.SkipFailbackWrapperException; @@ -69,11 +67,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @@ -101,7 +100,6 @@ import static org.apache.dubbo.common.constants.CommonConstants.USERNAME_KEY; import static org.apache.dubbo.common.constants.CommonConstants.VERSION_KEY; import static org.apache.dubbo.common.constants.LoggerCodeConstants.INTERNAL_ERROR; -import static org.apache.dubbo.common.constants.LoggerCodeConstants.REGISTRY_UNSUPPORTED_CATEGORY; import static org.apache.dubbo.common.constants.RegistryConstants.ALL_CATEGORIES; import static org.apache.dubbo.common.constants.RegistryConstants.CATEGORY_KEY; import static org.apache.dubbo.common.constants.RegistryConstants.CONFIGURATORS_CATEGORY; @@ -178,7 +176,8 @@ public class RegistryProtocol implements Protocol, ScopeModelAware { private static final ErrorTypeAwareLogger logger = LoggerFactory.getErrorTypeAwareLogger(RegistryProtocol.class); - private final Map serviceConfigurationListeners = new ConcurrentHashMap<>(); + private final Map> serviceConfigurationListeners = + new ConcurrentHashMap<>(); // To solve the problem of RMI repeated exposure port conflicts, the services that have been exposed are no longer // exposed. // provider url <--> registry url <--> exporter @@ -187,8 +186,8 @@ public class RegistryProtocol implements Protocol, ScopeModelAware { protected Protocol protocol; protected ProxyFactory proxyFactory; - private ConcurrentMap reExportFailedTasks = new ConcurrentHashMap<>(); - private HashedWheelTimer retryTimer = new HashedWheelTimer( + private final ConcurrentMap reExportFailedTasks = new ConcurrentHashMap<>(); + private final HashedWheelTimer retryTimer = new HashedWheelTimer( new NamedThreadFactory("DubboReexportTimer", true), DEFAULT_REGISTRY_RETRY_PERIOD, TimeUnit.MILLISECONDS, @@ -208,35 +207,11 @@ public void setProtocol(Protocol protocol) { this.protocol = protocol; } - public void setProxyFactory(ProxyFactory proxyFactory) { - this.proxyFactory = proxyFactory; - } - @Override public int getDefaultPort() { return 9090; } - public Map> getOverrideListeners() { - Map> map = new HashMap<>(); - List applicationModels = frameworkModel.getApplicationModels(); - if (applicationModels.size() == 1) { - return applicationModels - .get(0) - .getBeanFactory() - .getBean(ProviderConfigurationListener.class) - .getOverrideListeners(); - } else { - for (ApplicationModel applicationModel : applicationModels) { - map.putAll(applicationModel - .getBeanFactory() - .getBean(ProviderConfigurationListener.class) - .getOverrideListeners()); - } - } - return map; - } - private static void register(Registry registry, URL registeredProviderUrl) { ApplicationDeployer deployer = registeredProviderUrl.getOrDefaultApplicationModel().getDeployer(); @@ -282,7 +257,8 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker); ConcurrentHashMap> overrideListeners = getProviderConfigurationListener(overrideSubscribeUrl).getOverrideListeners(); - ConcurrentHashMapUtils.computeIfAbsent(overrideListeners, overrideSubscribeUrl, k -> new ConcurrentHashSet<>()) + Objects.requireNonNull(ConcurrentHashMapUtils.computeIfAbsent( + overrideListeners, overrideSubscribeUrl, k -> new ConcurrentHashSet<>())) .add(overrideSubscribeListener); providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); @@ -341,7 +317,9 @@ private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) { ServiceConfigurationListener serviceConfigurationListener = new ServiceConfigurationListener(providerUrl.getOrDefaultModuleModel(), providerUrl, listener); - serviceConfigurationListeners.put(providerUrl.getServiceKey(), serviceConfigurationListener); + serviceConfigurationListeners + .computeIfAbsent(providerUrl.getServiceKey(), k -> new CopyOnWriteArrayList<>()) + .add(serviceConfigurationListener); return serviceConfigurationListener.overrideUrl(providerUrl); } @@ -359,14 +337,6 @@ private ExporterChangeableWrapper doLocalExport(final Invoker originIn s -> new ExporterChangeableWrapper<>((ReferenceCountExporter) exporter, originInvoker)); } - public void reExport(Exporter exporter, URL newInvokerUrl) { - if (exporter instanceof ExporterChangeableWrapper) { - ExporterChangeableWrapper exporterWrapper = (ExporterChangeableWrapper) exporter; - Invoker originInvoker = exporterWrapper.getOriginInvoker(); - reExport(originInvoker, newInvokerUrl); - } - } - /** * Reexport the invoker of the modified url * @@ -609,10 +579,10 @@ protected ClusterInvoker getMigrationInvoker( } /** - * This method tries to load all RegistryProtocolListener definitions, which are used to control the behaviour of invoker by interacting with defined, then uses those listeners to - * change the status and behaviour of the MigrationInvoker. + * This method tries to load all RegistryProtocolListener definitions, which are used to control the behavior of invoker by interacting with defined, then uses those listeners to + * change the status and behavior of the MigrationInvoker. *

- * Currently available Listener is MigrationRuleListener, one used to control the Migration behaviour with dynamically changing rules. + * Currently available Listener is MigrationRuleListener, one used to control the Migration behavior with dynamically changing rules. * * @param invoker MigrationInvoker that determines which type of invoker list to use * @param url The original url generated during refer, more like a registry:// style url @@ -669,21 +639,6 @@ protected ClusterInvoker doCreateInvoker( return (ClusterInvoker) cluster.join(directory, true); } - public void reRefer(ClusterInvoker invoker, URL newSubscribeUrl) { - if (!(invoker instanceof MigrationClusterInvoker)) { - logger.error( - REGISTRY_UNSUPPORTED_CATEGORY, - "", - "", - "Only invoker type of MigrationClusterInvoker supports reRefer, current invoker is " - + invoker.getClass()); - return; - } - - MigrationClusterInvoker migrationClusterInvoker = (MigrationClusterInvoker) invoker; - migrationClusterInvoker.reRefer(newSubscribeUrl); - } - public static URL toSubscribeUrl(URL url) { return url.addParameter(CATEGORY_KEY, ALL_CATEGORIES); } @@ -779,7 +734,7 @@ public Invoker getInvoker() { private static class DestroyableExporter implements Exporter { - private Exporter exporter; + private final Exporter exporter; public DestroyableExporter(Exporter exporter) { this.exporter = exporter; @@ -895,8 +850,13 @@ public synchronized void doOverrideIfNecessary() { URL newUrl = getConfiguredInvokerUrl(configurators, originUrl); newUrl = getConfiguredInvokerUrl( getProviderConfigurationListener(originUrl).getConfigurators(), newUrl); - newUrl = getConfiguredInvokerUrl( - serviceConfigurationListeners.get(originUrl.getServiceKey()).getConfigurators(), newUrl); + List listeners = serviceConfigurationListeners.get(originUrl.getServiceKey()); + + if (listeners != null) { + for (ServiceConfigurationListener l : listeners) { + newUrl = getConfiguredInvokerUrl(l.getConfigurators(), newUrl); + } + } if (!newUrl.equals(currentUrl)) { if (newUrl.getParameter(Constants.NEED_REEXPORT, true)) { RegistryProtocol.this.reExport(originInvoker, newUrl); @@ -936,14 +896,13 @@ private ProviderConfigurationListener getProviderConfigurationListener(ModuleMod } private class ServiceConfigurationListener extends AbstractConfiguratorListener { - private URL providerUrl; - private OverrideListener notifyListener; + + private final OverrideListener notifyListener; private final ModuleModel moduleModel; public ServiceConfigurationListener(ModuleModel moduleModel, URL providerUrl, OverrideListener notifyListener) { super(moduleModel); - this.providerUrl = providerUrl; this.notifyListener = notifyListener; this.moduleModel = moduleModel; if (moduleModel @@ -954,7 +913,7 @@ public ServiceConfigurationListener(ModuleModel moduleModel, URL providerUrl, Ov } } - private URL overrideUrl(URL providerUrl) { + private URL overrideUrl(URL providerUrl) { return RegistryProtocol.getConfiguredInvokerUrl(configurators, providerUrl); } @@ -989,10 +948,9 @@ public ProviderConfigurationListener(ModuleModel moduleModel) { * Get existing configuration rule and override provider url before exporting. * * @param providerUrl - * @param * @return */ - private URL overrideUrl(URL providerUrl) { + private URL overrideUrl(URL providerUrl) { return RegistryProtocol.getConfiguredInvokerUrl(configurators, providerUrl); } @@ -1025,8 +983,6 @@ public ConcurrentHashMap> getOverrideListeners() { */ private class ExporterChangeableWrapper implements Exporter { - private final ScheduledExecutorService executor; - private final Invoker originInvoker; private Exporter exporter; private URL subscribeUrl; @@ -1039,12 +995,6 @@ public ExporterChangeableWrapper(ReferenceCountExporter exporter, Invoker this.exporter = exporter; exporter.increaseCount(); this.originInvoker = originInvoker; - FrameworkExecutorRepository frameworkExecutorRepository = originInvoker - .getUrl() - .getOrDefaultFrameworkModel() - .getBeanFactory() - .getBean(FrameworkExecutorRepository.class); - this.executor = frameworkExecutorRepository.getSharedScheduledExecutor(); } public Invoker getOriginInvoker() { @@ -1111,44 +1061,47 @@ public synchronized void unregister() { try { if (subscribeUrl != null) { - Map> overrideListeners = - getProviderConfigurationListener(subscribeUrl).getOverrideListeners(); - Set listeners = overrideListeners.get(subscribeUrl); - if (listeners != null) { - if (listeners.remove(notifyListener)) { - ApplicationModel applicationModel = getApplicationModel(registerUrl.getScopeModel()); - if (applicationModel - .modelEnvironment() - .getConfiguration() - .convert(Boolean.class, ENABLE_26X_CONFIGURATION_LISTEN, true)) { - if (!registry.isServiceDiscovery()) { - registry.unsubscribe(subscribeUrl, notifyListener); - } - } - if (applicationModel - .modelEnvironment() - .getConfiguration() - .convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) { - for (ModuleModel moduleModel : applicationModel.getPubModuleModels()) { - if (null != moduleModel.getServiceRepository() - && !moduleModel - .getServiceRepository() - .getExportedServices() - .isEmpty()) { - moduleModel - .getExtensionLoader(GovernanceRuleRepository.class) - .getDefaultExtension() - .removeListener( - subscribeUrl.getServiceKey() + CONFIGURATORS_SUFFIX, - serviceConfigurationListeners.remove( - subscribeUrl.getServiceKey())); + ApplicationModel applicationModel = getApplicationModel(registerUrl.getScopeModel()); + if (applicationModel + .modelEnvironment() + .getConfiguration() + .convert(Boolean.class, ENABLE_CONFIGURATION_LISTEN, true)) { + + String serviceKey = subscribeUrl.getServiceKey(); + String ruleKey = serviceKey + CONFIGURATORS_SUFFIX; + + for (ModuleModel moduleModel : applicationModel.getPubModuleModels()) { + if (moduleModel.getServiceRepository() != null + && !moduleModel + .getServiceRepository() + .getExportedServices() + .isEmpty()) { + + // Retrieve the list of configuration listeners for this specific service + CopyOnWriteArrayList serviceListeners = + serviceConfigurationListeners.get(serviceKey); + + if (serviceListeners != null) { + + // Governance repository manages dynamic configuration listeners + GovernanceRuleRepository repository = moduleModel + .getExtensionLoader(GovernanceRuleRepository.class) + .getDefaultExtension(); + + serviceListeners.removeIf(listener -> { + if (listener.notifyListener == notifyListener) { + repository.removeListener(ruleKey, listener); + return true; + } + return false; + }); + + if (serviceListeners.isEmpty()) { + serviceConfigurationListeners.remove(serviceKey); } } } } - if (listeners.isEmpty()) { - overrideListeners.remove(subscribeUrl); - } } } } catch (Throwable t) { diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java index 41688511a127..ef505a3f9b5b 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java @@ -28,17 +28,23 @@ import org.apache.dubbo.registry.client.migration.MigrationRuleListener; import org.apache.dubbo.rpc.Invoker; import org.apache.dubbo.rpc.cluster.Cluster; +import org.apache.dubbo.rpc.cluster.Configurator; import org.apache.dubbo.rpc.cluster.support.FailoverCluster; +import org.apache.dubbo.rpc.cluster.support.MergeableCluster; import org.apache.dubbo.rpc.cluster.support.wrapper.MockClusterWrapper; import org.apache.dubbo.rpc.cluster.support.wrapper.ScopeClusterWrapper; import org.apache.dubbo.rpc.model.ApplicationModel; import org.apache.dubbo.rpc.model.ModuleModel; import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.CopyOnWriteArrayList; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; @@ -114,7 +120,7 @@ void testConsumerUrlWithoutProtocol() { .getApplicationModel() .getApplicationConfigManager() .setApplication(new ApplicationConfig("application1")); - ExtensionLoader extensionLoaderMock = mock(ExtensionLoader.class); + ExtensionLoader extensionLoaderMock = mock(ExtensionLoader.class); Mockito.when(moduleModel.getExtensionLoader(RegistryProtocolListener.class)) .thenReturn(extensionLoaderMock); Mockito.when(extensionLoaderMock.getActivateExtension(url, REGISTRY_PROTOCOL_LISTENER_KEY)) @@ -127,7 +133,7 @@ void testConsumerUrlWithoutProtocol() { Invoker invoker = registryProtocol.doRefer(cluster, registry, DemoService.class, url, parameters); - Assertions.assertTrue(invoker instanceof MigrationInvoker); + Assertions.assertInstanceOf(MigrationInvoker.class, invoker); URL consumerUrl = ((MigrationInvoker) invoker).getConsumerUrl(); Assertions.assertTrue((consumerUrl != null)); @@ -195,7 +201,7 @@ void testConsumerUrlWithProtocol() { Invoker invoker = registryProtocol.doRefer(cluster, registry, DemoService.class, url, parameters); - Assertions.assertTrue(invoker instanceof MigrationInvoker); + Assertions.assertInstanceOf(MigrationInvoker.class, invoker); URL consumerUrl = ((MigrationInvoker) invoker).getConsumerUrl(); Assertions.assertTrue((consumerUrl != null)); @@ -263,15 +269,15 @@ void testReferWithoutGroup() { Invoker invoker = registryProtocol.refer(DemoService.class, url); - Assertions.assertTrue(invoker instanceof MigrationInvoker); - Assertions.assertTrue(((MigrationInvoker) invoker).getCluster() instanceof ScopeClusterWrapper); - Assertions.assertTrue( - ((ScopeClusterWrapper) ((MigrationInvoker) invoker).getCluster()).getCluster() - instanceof MockClusterWrapper); - Assertions.assertTrue( + Assertions.assertInstanceOf(MigrationInvoker.class, invoker); + Assertions.assertInstanceOf(ScopeClusterWrapper.class, ((MigrationInvoker) invoker).getCluster()); + Assertions.assertInstanceOf( + MockClusterWrapper.class, + ((ScopeClusterWrapper) ((MigrationInvoker) invoker).getCluster()).getCluster()); + Assertions.assertInstanceOf( + FailoverCluster.class, ((MockClusterWrapper) ((ScopeClusterWrapper) ((MigrationInvoker) invoker).getCluster()).getCluster()) - .getCluster() - instanceof FailoverCluster); + .getCluster()); } /** @@ -330,9 +336,9 @@ void testReferWithGroup() { Invoker invoker = registryProtocol.refer(DemoService.class, url); - Assertions.assertTrue(invoker instanceof MigrationInvoker); + Assertions.assertInstanceOf(MigrationInvoker.class, invoker); - Assertions.assertTrue(((MigrationInvoker) invoker).getCluster() instanceof ScopeClusterWrapper); + Assertions.assertInstanceOf(ScopeClusterWrapper.class, ((MigrationInvoker) invoker).getCluster()); // Assertions.assertTrue(((ScopeClusterWrapper) ((MigrationInvoker) // invoker).getCluster()).getCluster() instanceof MockClusterWrapper); @@ -525,7 +531,7 @@ void testRegisterConsumerUrl() { Invoker invoker = registryProtocol.doRefer(cluster, registry, DemoService.class, url, parameters); - Assertions.assertTrue(invoker instanceof MigrationInvoker); + Assertions.assertInstanceOf(MigrationInvoker.class, invoker); URL consumerUrl = ((MigrationInvoker) invoker).getConsumerUrl(); Assertions.assertTrue((consumerUrl != null)); @@ -544,4 +550,104 @@ void testRegisterConsumerUrl() { verify(registry, times(1)).register(registeredConsumerUrl); } + + /** + * Verifies that multiple ServiceConfigurationListeners registered for the same + * service are preserved and that their configurators are applied cumulatively. + */ + @Test + void testServiceConfigurationListenersAggregation() throws Exception { + ApplicationModel.defaultModel().getApplicationConfigManager().setApplication(new ApplicationConfig("test-app")); + + RegistryProtocol registryProtocol = new RegistryProtocol(); + + ModuleModel moduleModel = ApplicationModel.defaultModel().getDefaultModule(); + + Map params = new HashMap<>(); + params.put(INTERFACE_KEY, DemoService.class.getName()); + + ServiceConfigURL providerUrl = + new ServiceConfigURL("dubbo", "127.0.0.1", 20880, DemoService.class.getName(), params); + + URL url = providerUrl.setScopeModel(moduleModel); + + Invoker invoker = mock(Invoker.class); + when(invoker.getUrl()).thenReturn(url); + + Class overrideListenerClass = null; + for (Class c : RegistryProtocol.class.getDeclaredClasses()) { + if ("OverrideListener".equals(c.getSimpleName())) { + overrideListenerClass = c; + break; + } + } + Assertions.assertNotNull(overrideListenerClass); + + Constructor ctor = + overrideListenerClass.getDeclaredConstructor(RegistryProtocol.class, URL.class, Invoker.class); + ctor.setAccessible(true); + + Object listener1 = ctor.newInstance(registryProtocol, url, invoker); + Object listener2 = ctor.newInstance(registryProtocol, url, invoker); + + Method method = + RegistryProtocol.class.getDeclaredMethod("overrideUrlWithConfig", URL.class, overrideListenerClass); + method.setAccessible(true); + + method.invoke(registryProtocol, url, listener1); + method.invoke(registryProtocol, url, listener2); + + Field field = RegistryProtocol.class.getDeclaredField("serviceConfigurationListeners"); + field.setAccessible(true); + + @SuppressWarnings("unchecked") + Map> map = (Map>) field.get(registryProtocol); + + CopyOnWriteArrayList listeners = map.get(url.getServiceKey()); + Assertions.assertEquals(2, listeners.size()); + + Field cfgField = listeners.get(0).getClass().getSuperclass().getDeclaredField("configurators"); + cfgField.setAccessible(true); + + Configurator c1 = new Configurator() { + public URL configure(URL u) { + return u.addParameter("a", "1"); + } + + public URL getUrl() { + return URL.valueOf("override://0.0.0.0"); + } + }; + Configurator c2 = new Configurator() { + public URL configure(URL u) { + return u.addParameter("b", "2"); + } + + public URL getUrl() { + return URL.valueOf("override://0.0.0.0"); + } + }; + + List l1 = new ArrayList<>(); + l1.add(c1); + + List l2 = new ArrayList<>(); + l2.add(c2); + + cfgField.set(listeners.get(0), l1); + cfgField.set(listeners.get(1), l2); + + Method agg = RegistryProtocol.class.getDeclaredMethod("getConfiguredInvokerUrl", List.class, URL.class); + agg.setAccessible(true); + + URL result = url; + for (Object l : listeners) { + @SuppressWarnings("unchecked") + List cs = (List) cfgField.get(l); + result = (URL) agg.invoke(null, cs, result); + } + + Assertions.assertEquals("1", result.getParameter("a")); + Assertions.assertEquals("2", result.getParameter("b")); + } } From 83faf5dc292ed275d6a2074cbd7647fd0e304bda Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Fri, 26 Dec 2025 17:32:37 +0530 Subject: [PATCH 02/24] chore: trigger CI re-run From 4ac8a72807228d2765b598e25dc739f88592d868 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Fri, 26 Dec 2025 18:08:37 +0530 Subject: [PATCH 03/24] chore: trigger CI re-run From 6a647bdf5899565249c705bd0a589e724a2297d2 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Fri, 26 Dec 2025 21:31:49 +0530 Subject: [PATCH 04/24] chore: trigger CI re-run From 15c7c3713953122919c6aed401eb1bdb1e8933bb Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Mon, 29 Dec 2025 14:18:35 +0530 Subject: [PATCH 05/24] Resolved required changes --- .../apache/dubbo/registry/integration/RegistryProtocol.java | 5 +---- .../dubbo/registry/integration/RegistryProtocolTest.java | 6 ++++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index c0ad0986c249..d15be561b816 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -67,7 +67,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -257,9 +256,7 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker); ConcurrentHashMap> overrideListeners = getProviderConfigurationListener(overrideSubscribeUrl).getOverrideListeners(); - Objects.requireNonNull(ConcurrentHashMapUtils.computeIfAbsent( - overrideListeners, overrideSubscribeUrl, k -> new ConcurrentHashSet<>())) - .add(overrideSubscribeListener); + ConcurrentHashMapUtils.computeIfAbsent(overrideListeners, overrideSubscribeUrl, k -> new ConcurrentHashSet<>()); providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); // export invoker diff --git a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java index ef505a3f9b5b..96a95b70f625 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java +++ b/dubbo-registry/dubbo-registry-api/src/test/java/org/apache/dubbo/registry/integration/RegistryProtocolTest.java @@ -609,20 +609,25 @@ void testServiceConfigurationListenersAggregation() throws Exception { Field cfgField = listeners.get(0).getClass().getSuperclass().getDeclaredField("configurators"); cfgField.setAccessible(true); + // Mock Configurators to simulate distinct override rules from different registries Configurator c1 = new Configurator() { + @Override public URL configure(URL u) { return u.addParameter("a", "1"); } + @Override public URL getUrl() { return URL.valueOf("override://0.0.0.0"); } }; Configurator c2 = new Configurator() { + @Override public URL configure(URL u) { return u.addParameter("b", "2"); } + @Override public URL getUrl() { return URL.valueOf("override://0.0.0.0"); } @@ -637,6 +642,7 @@ public URL getUrl() { cfgField.set(listeners.get(0), l1); cfgField.set(listeners.get(1), l2); + // Uses reflection to validate internal aggregation behavior without exposing test-only APIs Method agg = RegistryProtocol.class.getDeclaredMethod("getConfiguredInvokerUrl", List.class, URL.class); agg.setAccessible(true); From c518673806f6a84bce77cdc296478f6f3cf7dfd9 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Mon, 29 Dec 2025 14:54:01 +0530 Subject: [PATCH 06/24] chore: trigger CI re-run From b03db9679b801f02cc7550d8b8598b6d9e17b4e0 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Mon, 29 Dec 2025 16:37:34 +0530 Subject: [PATCH 07/24] chore: trigger CI re-run From ede22c504c23da6a74152c0594050edbe25cf67b Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Mon, 29 Dec 2025 21:17:41 +0530 Subject: [PATCH 08/24] chore: trigger CI re-run From 03e0716d6fb6d34b03b350e133971171b51ffd64 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 30 Dec 2025 19:08:00 +0530 Subject: [PATCH 09/24] Resolved race condition bug --- .../integration/RegistryProtocol.java | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index d15be561b816..b6290b8f4546 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -312,12 +312,21 @@ private URL overrideUrlWithConfig(URL providerUrl, OverrideListener listener) { ProviderConfigurationListener providerConfigurationListener = getProviderConfigurationListener(providerUrl); providerUrl = providerConfigurationListener.overrideUrl(providerUrl); - ServiceConfigurationListener serviceConfigurationListener = - new ServiceConfigurationListener(providerUrl.getOrDefaultModuleModel(), providerUrl, listener); - serviceConfigurationListeners - .computeIfAbsent(providerUrl.getServiceKey(), k -> new CopyOnWriteArrayList<>()) - .add(serviceConfigurationListener); - return serviceConfigurationListener.overrideUrl(providerUrl); + CopyOnWriteArrayList listeners = serviceConfigurationListeners.computeIfAbsent( + providerUrl.getServiceKey(), k -> new CopyOnWriteArrayList<>()); + + synchronized (listeners) { + for (ServiceConfigurationListener existing : listeners) { + if (existing.notifyListener == listener) { + return existing.overrideUrl(providerUrl); + } + } + + ServiceConfigurationListener serviceConfigurationListener = + new ServiceConfigurationListener(providerUrl.getOrDefaultModuleModel(), providerUrl, listener); + listeners.add(serviceConfigurationListener); + return serviceConfigurationListener.overrideUrl(providerUrl); + } } @SuppressWarnings("unchecked") From 26dede2dd99b23d21cbeda343a5647be9b340445 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 30 Dec 2025 19:44:30 +0530 Subject: [PATCH 10/24] Resolved stale provider bug --- .../apache/dubbo/registry/integration/RegistryProtocol.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index b6290b8f4546..120c2a87ec00 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -1101,10 +1101,6 @@ public synchronized void unregister() { } return false; }); - - if (serviceListeners.isEmpty()) { - serviceConfigurationListeners.remove(serviceKey); - } } } } From 731a20f5d248adba87cc334e8951dd8929a227b4 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 30 Dec 2025 20:14:56 +0530 Subject: [PATCH 11/24] Resolved race condition due to export thread --- .../org/apache/dubbo/registry/integration/RegistryProtocol.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index 120c2a87ec00..2b4eb87653b6 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -1116,7 +1116,7 @@ public synchronized void unregister() { public synchronized void unexport() { String providerUrlKey = getProviderUrlKey(this.originInvoker); String registryUrlKey = getRegistryUrlKey(this.originInvoker); - Map> exporterMap = bounds.remove(providerUrlKey); + Map> exporterMap = bounds.get(providerUrlKey); if (exporterMap != null) { exporterMap.remove(registryUrlKey); } From d9bf10ca62febf90c881986e8cf6352268ec89e7 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 30 Dec 2025 21:31:27 +0530 Subject: [PATCH 12/24] Resolved stale state bug --- .../integration/RegistryProtocol.java | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index 2b4eb87653b6..1fe9b9f4e413 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -1083,25 +1083,25 @@ public synchronized void unregister() { .getExportedServices() .isEmpty()) { - // Retrieve the list of configuration listeners for this specific service - CopyOnWriteArrayList serviceListeners = - serviceConfigurationListeners.get(serviceKey); - - if (serviceListeners != null) { - - // Governance repository manages dynamic configuration listeners - GovernanceRuleRepository repository = moduleModel - .getExtensionLoader(GovernanceRuleRepository.class) - .getDefaultExtension(); - - serviceListeners.removeIf(listener -> { - if (listener.notifyListener == notifyListener) { - repository.removeListener(ruleKey, listener); - return true; + GovernanceRuleRepository repository = moduleModel + .getExtensionLoader(GovernanceRuleRepository.class) + .getDefaultExtension(); + + serviceConfigurationListeners.compute(serviceKey, (k, listeners) -> { + if (listeners != null) { + listeners.removeIf(listener -> { + if (listener.notifyListener == notifyListener) { + repository.removeListener(ruleKey, listener); + return true; + } + return false; + }); + if (listeners.isEmpty()) { + return null; } - return false; - }); - } + } + return listeners; + }); } } } @@ -1114,14 +1114,18 @@ public synchronized void unregister() { @Override public synchronized void unexport() { + unregister(); String providerUrlKey = getProviderUrlKey(this.originInvoker); String registryUrlKey = getRegistryUrlKey(this.originInvoker); - Map> exporterMap = bounds.get(providerUrlKey); - if (exporterMap != null) { - exporterMap.remove(registryUrlKey); - } - - unregister(); + bounds.compute(providerUrlKey, (k, exporterMap) -> { + if (exporterMap != null) { + exporterMap.remove(registryUrlKey); + if (exporterMap.isEmpty()) { + return null; + } + } + return exporterMap; + }); doUnExport(); } From 71c2d12417399606a10d2c0a62f4152f1bc6dee1 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 30 Dec 2025 21:54:02 +0530 Subject: [PATCH 13/24] Resolved ghost service bug --- .../registry/integration/RegistryProtocol.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index 1fe9b9f4e413..bd70a1ac0450 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -337,10 +337,17 @@ private ExporterChangeableWrapper doLocalExport(final Invoker originIn ReferenceCountExporter exporter = exporterFactory.createExporter(providerUrlKey, () -> protocol.export(invokerDelegate)); - return (ExporterChangeableWrapper) ConcurrentHashMapUtils.computeIfAbsent( - ConcurrentHashMapUtils.computeIfAbsent(bounds, providerUrlKey, k -> new ConcurrentHashMap<>()), - registryUrlKey, - s -> new ExporterChangeableWrapper<>((ReferenceCountExporter) exporter, originInvoker)); + Map> registryMap = bounds.compute(providerUrlKey, (k, map) -> { + if (map == null) { + map = new ConcurrentHashMap<>(); + } + map.computeIfAbsent( + registryUrlKey, + s -> new ExporterChangeableWrapper<>((ReferenceCountExporter) exporter, originInvoker)); + return map; + }); + + return (ExporterChangeableWrapper) registryMap.get(registryUrlKey); } /** From 042850fe37c26f69732bb7e095491d37e607d7ab Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 30 Dec 2025 22:30:46 +0530 Subject: [PATCH 14/24] Handled exporter missing case --- .../integration/RegistryProtocol.java | 41 +++++++++++++------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index bd70a1ac0450..b56748cdead1 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -363,22 +363,39 @@ public void reExport(final Invoker originInvoker, URL newInvokerUrl) { String registryUrlKey = getRegistryUrlKey(originInvoker); Map> registryMap = bounds.get(providerUrlKey); if (registryMap == null) { - logger.warn( - INTERNAL_ERROR, - "error state, exporterMap can not be null", - "", - "error state, exporterMap can not be null", - new IllegalStateException("error state, exporterMap can not be null")); + ReExportTask oldTask = reExportFailedTasks.get(newInvokerUrl); + if (oldTask != null) { + return; + } + ReExportTask task = new ReExportTask(() -> reExport(originInvoker, newInvokerUrl), newInvokerUrl, null); + oldTask = reExportFailedTasks.putIfAbsent(newInvokerUrl, task); + if (oldTask == null) { + retryTimer.newTimeout( + task, + getRegistryUrl(originInvoker) + .getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), + TimeUnit.MILLISECONDS); + } + logger.info("exporterMap missing for providerKey=" + providerUrlKey + ", scheduled retry"); return; } ExporterChangeableWrapper exporter = (ExporterChangeableWrapper) registryMap.get(registryUrlKey); if (exporter == null) { - logger.warn( - INTERNAL_ERROR, - "error state, exporterMap can not be null", - "", - "error state, exporterMap can not be null", - new IllegalStateException("error state, exporterMap can not be null")); + ReExportTask oldTask = reExportFailedTasks.get(newInvokerUrl); + if (oldTask != null) { + return; + } + ReExportTask task = new ReExportTask(() -> reExport(originInvoker, newInvokerUrl), newInvokerUrl, null); + oldTask = reExportFailedTasks.putIfAbsent(newInvokerUrl, task); + if (oldTask == null) { + retryTimer.newTimeout( + task, + getRegistryUrl(originInvoker) + .getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), + TimeUnit.MILLISECONDS); + } + logger.info("exporter missing for providerKey=" + providerUrlKey + " registryKey=" + registryUrlKey + + ", scheduled retry"); return; } URL registeredUrl = exporter.getRegisterUrl(); From 9270df159cf734a27e29cad765f5139102ad0f39 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Tue, 30 Dec 2025 23:11:15 +0530 Subject: [PATCH 15/24] Resolved wrong configuration bug --- .../integration/RegistryProtocol.java | 67 +++++++++++++++---- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index b56748cdead1..76f73169fb8a 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -855,22 +855,65 @@ public synchronized void doOverrideIfNecessary() { String registryUrlKey = getRegistryUrlKey(originInvoker); Map> exporterMap = bounds.get(providerUrlKey); if (exporterMap == null) { - logger.warn( - INTERNAL_ERROR, - "error state, exporterMap can not be null", - "", - "error state, exporterMap can not be null", - new IllegalStateException("error state, exporterMap can not be null")); + try { + URL providerUrl = RegistryProtocol.this.getProviderUrl(originInvoker); + ReExportTask oldTask = reExportFailedTasks.get(providerUrl); + if (oldTask == null) { + ReExportTask task = new ReExportTask( + () -> { + try { + doOverrideIfNecessary(); + } catch (Throwable ignore) { + } + }, + providerUrl, + null); + ReExportTask prev = reExportFailedTasks.putIfAbsent(providerUrl, task); + if (prev == null) { + retryTimer.newTimeout( + task, + getRegistryUrl(originInvoker) + .getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), + TimeUnit.MILLISECONDS); + } + } + } catch (Throwable t) { + logger.warn(INTERNAL_ERROR, "failed to schedule override retry", "", t.getMessage(), t); + } + logger.info("ExporterMap missing for providerKey={}, scheduled retry", providerUrlKey); return; } ExporterChangeableWrapper exporter = exporterMap.get(registryUrlKey); if (exporter == null) { - logger.warn( - INTERNAL_ERROR, - "unknown error in registry module", - "", - "error state, exporter should not be null", - new IllegalStateException("error state, exporter should not be null")); + try { + URL providerUrl = RegistryProtocol.this.getProviderUrl(originInvoker); + ReExportTask oldTask = reExportFailedTasks.get(providerUrl); + if (oldTask == null) { + ReExportTask task = new ReExportTask( + () -> { + try { + doOverrideIfNecessary(); + } catch (Throwable ignore) { + } + }, + providerUrl, + null); + ReExportTask prev = reExportFailedTasks.putIfAbsent(providerUrl, task); + if (prev == null) { + retryTimer.newTimeout( + task, + getRegistryUrl(originInvoker) + .getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), + TimeUnit.MILLISECONDS); + } + } + } catch (Throwable t) { + logger.warn(INTERNAL_ERROR, "failed to schedule override retry", "", t.getMessage(), t); + } + logger.info( + "Exporter missing for providerKey={} registryKey={}, scheduled retry", + providerUrlKey, + registryUrlKey); return; } // The current, may have been merged many times From a1d7abbfef273b7518f4b8a19a3a5fc22c82f79e Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 31 Dec 2025 00:03:07 +0530 Subject: [PATCH 16/24] Improved state lifecycle --- .../integration/RegistryProtocol.java | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index 76f73169fb8a..f74196c11943 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -26,8 +26,6 @@ import org.apache.dubbo.common.timer.HashedWheelTimer; import org.apache.dubbo.common.url.component.ServiceConfigURL; import org.apache.dubbo.common.utils.CollectionUtils; -import org.apache.dubbo.common.utils.ConcurrentHashMapUtils; -import org.apache.dubbo.common.utils.ConcurrentHashSet; import org.apache.dubbo.common.utils.NamedThreadFactory; import org.apache.dubbo.common.utils.StringUtils; import org.apache.dubbo.common.utils.UrlUtils; @@ -254,13 +252,9 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio // subscription information to cover. final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl); final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker); - ConcurrentHashMap> overrideListeners = - getProviderConfigurationListener(overrideSubscribeUrl).getOverrideListeners(); - ConcurrentHashMapUtils.computeIfAbsent(overrideListeners, overrideSubscribeUrl, k -> new ConcurrentHashSet<>()); - providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); - // export invoker final ExporterChangeableWrapper exporter = doLocalExport(originInvoker, providerUrl); + providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); // url to registry final Registry registry = getRegistry(registryUrl); @@ -268,18 +262,18 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio // decide if we need to delay publish (provider itself and registry should both need to register) boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true); - if (register) { - register(registry, registeredProviderUrl); - } - - // register stated url on provider model - registerStatedUrl(registryUrl, registeredProviderUrl, register); exporter.setRegisterUrl(registeredProviderUrl); exporter.setSubscribeUrl(overrideSubscribeUrl); exporter.setNotifyListener(overrideSubscribeListener); exporter.setRegistered(register); + if (register) { + register(registry, registeredProviderUrl); + } + + registerStatedUrl(registryUrl, registeredProviderUrl, register); + ApplicationModel applicationModel = getApplicationModel(providerUrl.getScopeModel()); if (applicationModel .modelEnvironment() From bfa29bd150e75481cad8b5edadd30fbb52be8670 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 31 Dec 2025 00:38:07 +0530 Subject: [PATCH 17/24] Updated exporter creation logic --- .../org/apache/dubbo/registry/integration/RegistryProtocol.java | 2 +- .../dubbo-registry-api/src/test/resources/dubbo.properties | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index f74196c11943..ed312ce04e52 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -266,12 +266,12 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio exporter.setRegisterUrl(registeredProviderUrl); exporter.setSubscribeUrl(overrideSubscribeUrl); exporter.setNotifyListener(overrideSubscribeListener); - exporter.setRegistered(register); if (register) { register(registry, registeredProviderUrl); } + exporter.setRegistered(register); registerStatedUrl(registryUrl, registeredProviderUrl, register); ApplicationModel applicationModel = getApplicationModel(providerUrl.getScopeModel()); diff --git a/dubbo-registry/dubbo-registry-api/src/test/resources/dubbo.properties b/dubbo-registry/dubbo-registry-api/src/test/resources/dubbo.properties index 1aade88a5619..5ba2759a30a9 100644 --- a/dubbo-registry/dubbo-registry-api/src/test/resources/dubbo.properties +++ b/dubbo-registry/dubbo-registry-api/src/test/resources/dubbo.properties @@ -1,2 +1,3 @@ dubbo.application.enable-file-cache=false dubbo.service.shutdown.wait=200 +dubbo.application.service-discovery.migration=APPLICATION_FIRST From 9b539c059e611453b7168521097c5b633ae90199 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 31 Dec 2025 01:18:44 +0530 Subject: [PATCH 18/24] Handled registered flag --- .../integration/RegistryProtocol.java | 84 ++++--------------- 1 file changed, 18 insertions(+), 66 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index ed312ce04e52..35a84bd994dd 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -355,14 +355,15 @@ private ExporterChangeableWrapper doLocalExport(final Invoker originIn public void reExport(final Invoker originInvoker, URL newInvokerUrl) { String providerUrlKey = getProviderUrlKey(originInvoker); String registryUrlKey = getRegistryUrlKey(originInvoker); + URL retryKey = newInvokerUrl; Map> registryMap = bounds.get(providerUrlKey); if (registryMap == null) { - ReExportTask oldTask = reExportFailedTasks.get(newInvokerUrl); + ReExportTask oldTask = reExportFailedTasks.get(retryKey); if (oldTask != null) { return; } - ReExportTask task = new ReExportTask(() -> reExport(originInvoker, newInvokerUrl), newInvokerUrl, null); - oldTask = reExportFailedTasks.putIfAbsent(newInvokerUrl, task); + ReExportTask task = new ReExportTask(() -> reExport(originInvoker, newInvokerUrl), retryKey, null); + oldTask = reExportFailedTasks.putIfAbsent(retryKey, task); if (oldTask == null) { retryTimer.newTimeout( task, @@ -370,17 +371,17 @@ public void reExport(final Invoker originInvoker, URL newInvokerUrl) { .getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), TimeUnit.MILLISECONDS); } - logger.info("exporterMap missing for providerKey=" + providerUrlKey + ", scheduled retry"); + logger.info("[reExport] exporterMap missing for providerKey=" + providerUrlKey + ", scheduled retry"); return; } ExporterChangeableWrapper exporter = (ExporterChangeableWrapper) registryMap.get(registryUrlKey); if (exporter == null) { - ReExportTask oldTask = reExportFailedTasks.get(newInvokerUrl); + ReExportTask oldTask = reExportFailedTasks.get(retryKey); if (oldTask != null) { return; } - ReExportTask task = new ReExportTask(() -> reExport(originInvoker, newInvokerUrl), newInvokerUrl, null); - oldTask = reExportFailedTasks.putIfAbsent(newInvokerUrl, task); + ReExportTask task = new ReExportTask(() -> reExport(originInvoker, newInvokerUrl), retryKey, null); + oldTask = reExportFailedTasks.putIfAbsent(retryKey, task); if (oldTask == null) { retryTimer.newTimeout( task, @@ -388,10 +389,16 @@ public void reExport(final Invoker originInvoker, URL newInvokerUrl) { .getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), TimeUnit.MILLISECONDS); } - logger.info("exporter missing for providerKey=" + providerUrlKey + " registryKey=" + registryUrlKey + logger.info("Exporter missing for providerKey=" + providerUrlKey + " registryKey=" + registryUrlKey + ", scheduled retry"); return; } + + if (!exporter.isRegistered()) { + logger.info("Skipping reExport registry operations to avoid race with initial export."); + return; + } + URL registeredUrl = exporter.getRegisterUrl(); URL registryUrl = getRegistryUrl(originInvoker); @@ -406,15 +413,15 @@ public void reExport(final Invoker originInvoker, URL newInvokerUrl) { try { doReExport(originInvoker, exporter, registryUrl, registeredUrl, newProviderUrl); } catch (Exception e) { - ReExportTask oldTask = reExportFailedTasks.get(registeredUrl); + ReExportTask oldTask = reExportFailedTasks.get(retryKey); if (oldTask != null) { return; } ReExportTask task = new ReExportTask( () -> doReExport(originInvoker, exporter, registryUrl, registeredUrl, newProviderUrl), - registeredUrl, + retryKey, null); - oldTask = reExportFailedTasks.putIfAbsent(registeredUrl, task); + oldTask = reExportFailedTasks.putIfAbsent(retryKey, task); if (oldTask == null) { // never has a retry task. then start a new task for retry. retryTimer.newTimeout( @@ -849,65 +856,10 @@ public synchronized void doOverrideIfNecessary() { String registryUrlKey = getRegistryUrlKey(originInvoker); Map> exporterMap = bounds.get(providerUrlKey); if (exporterMap == null) { - try { - URL providerUrl = RegistryProtocol.this.getProviderUrl(originInvoker); - ReExportTask oldTask = reExportFailedTasks.get(providerUrl); - if (oldTask == null) { - ReExportTask task = new ReExportTask( - () -> { - try { - doOverrideIfNecessary(); - } catch (Throwable ignore) { - } - }, - providerUrl, - null); - ReExportTask prev = reExportFailedTasks.putIfAbsent(providerUrl, task); - if (prev == null) { - retryTimer.newTimeout( - task, - getRegistryUrl(originInvoker) - .getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), - TimeUnit.MILLISECONDS); - } - } - } catch (Throwable t) { - logger.warn(INTERNAL_ERROR, "failed to schedule override retry", "", t.getMessage(), t); - } - logger.info("ExporterMap missing for providerKey={}, scheduled retry", providerUrlKey); return; } ExporterChangeableWrapper exporter = exporterMap.get(registryUrlKey); if (exporter == null) { - try { - URL providerUrl = RegistryProtocol.this.getProviderUrl(originInvoker); - ReExportTask oldTask = reExportFailedTasks.get(providerUrl); - if (oldTask == null) { - ReExportTask task = new ReExportTask( - () -> { - try { - doOverrideIfNecessary(); - } catch (Throwable ignore) { - } - }, - providerUrl, - null); - ReExportTask prev = reExportFailedTasks.putIfAbsent(providerUrl, task); - if (prev == null) { - retryTimer.newTimeout( - task, - getRegistryUrl(originInvoker) - .getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), - TimeUnit.MILLISECONDS); - } - } - } catch (Throwable t) { - logger.warn(INTERNAL_ERROR, "failed to schedule override retry", "", t.getMessage(), t); - } - logger.info( - "Exporter missing for providerKey={} registryKey={}, scheduled retry", - providerUrlKey, - registryUrlKey); return; } // The current, may have been merged many times From aaf2e07e348b37bccc4aad0635a35b8db1da9223 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 31 Dec 2025 01:52:14 +0530 Subject: [PATCH 19/24] Added lost updates protection --- .../integration/RegistryProtocol.java | 92 +++++++++---------- 1 file changed, 41 insertions(+), 51 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index 35a84bd994dd..226d47db7407 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -355,53 +355,40 @@ private ExporterChangeableWrapper doLocalExport(final Invoker originIn public void reExport(final Invoker originInvoker, URL newInvokerUrl) { String providerUrlKey = getProviderUrlKey(originInvoker); String registryUrlKey = getRegistryUrlKey(originInvoker); + URL registryUrl = getRegistryUrl(originInvoker); URL retryKey = newInvokerUrl; Map> registryMap = bounds.get(providerUrlKey); if (registryMap == null) { - ReExportTask oldTask = reExportFailedTasks.get(retryKey); - if (oldTask != null) { - return; - } - ReExportTask task = new ReExportTask(() -> reExport(originInvoker, newInvokerUrl), retryKey, null); - oldTask = reExportFailedTasks.putIfAbsent(retryKey, task); - if (oldTask == null) { - retryTimer.newTimeout( - task, - getRegistryUrl(originInvoker) - .getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), - TimeUnit.MILLISECONDS); - } - logger.info("[reExport] exporterMap missing for providerKey=" + providerUrlKey + ", scheduled retry"); + scheduleReExportRetry( + originInvoker, + newInvokerUrl, + retryKey, + registryUrl, + "ExporterMap missing for providerKey=" + providerUrlKey); return; } ExporterChangeableWrapper exporter = (ExporterChangeableWrapper) registryMap.get(registryUrlKey); if (exporter == null) { - ReExportTask oldTask = reExportFailedTasks.get(retryKey); - if (oldTask != null) { - return; - } - ReExportTask task = new ReExportTask(() -> reExport(originInvoker, newInvokerUrl), retryKey, null); - oldTask = reExportFailedTasks.putIfAbsent(retryKey, task); - if (oldTask == null) { - retryTimer.newTimeout( - task, - getRegistryUrl(originInvoker) - .getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), - TimeUnit.MILLISECONDS); - } - logger.info("Exporter missing for providerKey=" + providerUrlKey + " registryKey=" + registryUrlKey - + ", scheduled retry"); + scheduleReExportRetry( + originInvoker, + newInvokerUrl, + retryKey, + registryUrl, + "Exporter missing for providerKey=" + providerUrlKey + " registryKey=" + registryUrlKey); return; } if (!exporter.isRegistered()) { - logger.info("Skipping reExport registry operations to avoid race with initial export."); + scheduleReExportRetry( + originInvoker, + newInvokerUrl, + retryKey, + registryUrl, + "Exporter exists but not registered yet for providerKey=" + providerUrlKey); return; } URL registeredUrl = exporter.getRegisterUrl(); - - URL registryUrl = getRegistryUrl(originInvoker); URL newProviderUrl = customizeURL(newInvokerUrl, registryUrl); // update local exporter @@ -413,26 +400,33 @@ public void reExport(final Invoker originInvoker, URL newInvokerUrl) { try { doReExport(originInvoker, exporter, registryUrl, registeredUrl, newProviderUrl); } catch (Exception e) { - ReExportTask oldTask = reExportFailedTasks.get(retryKey); - if (oldTask != null) { - return; - } - ReExportTask task = new ReExportTask( - () -> doReExport(originInvoker, exporter, registryUrl, registeredUrl, newProviderUrl), + scheduleReExportRetry( + originInvoker, + newInvokerUrl, retryKey, - null); - oldTask = reExportFailedTasks.putIfAbsent(retryKey, task); - if (oldTask == null) { - // never has a retry task. then start a new task for retry. - retryTimer.newTimeout( - task, - registryUrl.getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), - TimeUnit.MILLISECONDS); - } + registryUrl, + "Failed to doReExport, error: " + e.getMessage()); } } } + private void scheduleReExportRetry( + Invoker originInvoker, URL newInvokerUrl, URL retryKey, URL registryUrl, String logMessage) { + ReExportTask oldTask = reExportFailedTasks.get(retryKey); + if (oldTask != null) { + return; + } + ReExportTask task = new ReExportTask(() -> reExport(originInvoker, newInvokerUrl), retryKey, null); + ReExportTask prev = reExportFailedTasks.putIfAbsent(retryKey, task); + if (prev == null) { + retryTimer.newTimeout( + task, + registryUrl.getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD), + TimeUnit.MILLISECONDS); + logger.info(logMessage + ", scheduled retry"); + } + } + private void doReExport( final Invoker originInvoker, ExporterChangeableWrapper exporter, @@ -988,10 +982,6 @@ protected void notifyOverrides() { deployer.decreaseServiceRefreshCount(); } } - - public ConcurrentHashMap> getOverrideListeners() { - return overrideListeners; - } } /** From 561817772c6b8c979e0c3e68d9ab8c5a1137adf5 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 31 Dec 2025 02:01:00 +0530 Subject: [PATCH 20/24] chore: trigger CI re-run From 1777d0aa8baff8b6521a3d402e7a13d3915a3179 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 31 Dec 2025 03:08:10 +0530 Subject: [PATCH 21/24] Updated exporter cycle --- .../dubbo/registry/integration/RegistryProtocol.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index 226d47db7407..411cb8dd790f 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -254,14 +254,13 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker); final ExporterChangeableWrapper exporter = doLocalExport(originInvoker, providerUrl); - providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); - // url to registry + URL overriddenUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); final Registry registry = getRegistry(registryUrl); - final URL registeredProviderUrl = customizeURL(providerUrl, registryUrl); + final URL registeredProviderUrl = customizeURL(overriddenUrl, registryUrl); - // decide if we need to delay publish (provider itself and registry should both need to register) - boolean register = providerUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true); + boolean register = + overriddenUrl.getParameter(REGISTER_KEY, true) && registryUrl.getParameter(REGISTER_KEY, true); exporter.setRegisterUrl(registeredProviderUrl); exporter.setSubscribeUrl(overrideSubscribeUrl); From 4c845d8be2abf4baedcc1f328d7bbd219d0aec17 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 31 Dec 2025 03:35:05 +0530 Subject: [PATCH 22/24] Updated export block --- .../org/apache/dubbo/registry/integration/RegistryProtocol.java | 1 + 1 file changed, 1 insertion(+) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index 411cb8dd790f..d2631d183ef0 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -281,6 +281,7 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio if (!registry.isServiceDiscovery()) { // Deprecated! Subscribe to override rules in 2.6.x or before. registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener); + overrideSubscribeListener.doOverrideIfNecessary(); } } From e29c652d99bb5d704b06ed9f1376f95c75005511 Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 31 Dec 2025 03:59:39 +0530 Subject: [PATCH 23/24] Updated export block --- .../apache/dubbo/registry/integration/RegistryProtocol.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index d2631d183ef0..c9325acdd18c 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -256,6 +256,10 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio final ExporterChangeableWrapper exporter = doLocalExport(originInvoker, providerUrl); URL overriddenUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); + if (!overriddenUrl.equals(providerUrl)) { + providerUrl = overriddenUrl; + exporter.setExporter(protocol.export(new InvokerDelegate<>(originInvoker, providerUrl))); + } final Registry registry = getRegistry(registryUrl); final URL registeredProviderUrl = customizeURL(overriddenUrl, registryUrl); From 5a811662ad84c11cf000d9b3194e16260ffa6bde Mon Sep 17 00:00:00 2001 From: somiljain2006 Date: Wed, 31 Dec 2025 04:33:00 +0530 Subject: [PATCH 24/24] Updated export block --- .../dubbo/registry/integration/RegistryProtocol.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java index c9325acdd18c..316f246c15c3 100644 --- a/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java +++ b/dubbo-registry/dubbo-registry-api/src/main/java/org/apache/dubbo/registry/integration/RegistryProtocol.java @@ -253,13 +253,8 @@ public Exporter export(final Invoker originInvoker) throws RpcExceptio final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl); final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker); - final ExporterChangeableWrapper exporter = doLocalExport(originInvoker, providerUrl); - URL overriddenUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener); - if (!overriddenUrl.equals(providerUrl)) { - providerUrl = overriddenUrl; - exporter.setExporter(protocol.export(new InvokerDelegate<>(originInvoker, providerUrl))); - } + final ExporterChangeableWrapper exporter = doLocalExport(originInvoker, overriddenUrl); final Registry registry = getRegistry(registryUrl); final URL registeredProviderUrl = customizeURL(overriddenUrl, registryUrl);