diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/cleaner/ExpiredMetadataCleaner.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/cleaner/ExpiredMetadataCleaner.java index 98602a6bc5f..2ece2c82cbc 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/cleaner/ExpiredMetadataCleaner.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/cleaner/ExpiredMetadataCleaner.java @@ -16,14 +16,20 @@ package com.alibaba.nacos.naming.core.v2.cleaner; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ServiceInfo; +import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.metadata.ExpiredMetadataInfo; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService; +import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; +import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.naming.misc.GlobalConfig; import com.alibaba.nacos.naming.misc.GlobalExecutor; import com.alibaba.nacos.naming.misc.Loggers; import org.springframework.stereotype.Component; +import java.util.Iterator; import java.util.concurrent.TimeUnit; /** @@ -42,10 +48,13 @@ public class ExpiredMetadataCleaner extends AbstractNamingCleaner { private final NamingMetadataOperateService metadataOperateService; + private final ServiceStorage serviceStorage; + public ExpiredMetadataCleaner(NamingMetadataManager metadataManager, - NamingMetadataOperateService metadataOperateService) { + NamingMetadataOperateService metadataOperateService, ServiceStorage serviceStorage) { this.metadataManager = metadataManager; this.metadataOperateService = metadataOperateService; + this.serviceStorage = serviceStorage; GlobalExecutor.scheduleExpiredClientCleaner(this, INITIAL_DELAY, GlobalConfig.getExpiredMetadataCleanInterval(), TimeUnit.MILLISECONDS); } @@ -58,23 +67,49 @@ public String getType() { @Override public void doClean() { long currentTime = System.currentTimeMillis(); - for (ExpiredMetadataInfo each : metadataManager.getExpiredMetadataInfos()) { + Iterator it = metadataManager.getExpiredMetadataInfos().iterator(); + while (it.hasNext()) { + ExpiredMetadataInfo each = it.next(); if (currentTime - each.getCreateTime() > GlobalConfig.getExpiredMetadataExpiredTime()) { - removeExpiredMetadata(each); + if (!removeExpiredMetadata(each)) { + it.remove(); + } } } } - private void removeExpiredMetadata(ExpiredMetadataInfo expiredInfo) { + private boolean removeExpiredMetadata(ExpiredMetadataInfo expiredInfo) { Loggers.SRV_LOG.info("Remove expired metadata {}", expiredInfo); if (null == expiredInfo.getMetadataId()) { if (metadataManager.containServiceMetadata(expiredInfo.getService())) { metadataOperateService.deleteServiceMetadata(expiredInfo.getService()); } } else { + Instance instance = queryInstance(expiredInfo); + if (instance != null) { + Loggers.SRV_LOG.warn("Instance exists, abort removing metadata {}", expiredInfo); + return false; + } if (metadataManager.containInstanceMetadata(expiredInfo.getService(), expiredInfo.getMetadataId())) { metadataOperateService.deleteInstanceMetadata(expiredInfo.getService(), expiredInfo.getMetadataId()); } } + return true; + } + + private Instance queryInstance(ExpiredMetadataInfo expiredInfo) { + Instance instance = null; + String cluster = InstancePublishInfo.getClusterFromMetadataId(expiredInfo.getMetadataId()); + String ip = InstancePublishInfo.getIpFromMetadataId(expiredInfo.getMetadataId()); + int port = InstancePublishInfo.getPortFromMetadataId(expiredInfo.getMetadataId()); + Service service = expiredInfo.getService(); + ServiceInfo serviceInfo = serviceStorage.getPushData(service); + for (Instance each : serviceInfo.getHosts()) { + if (cluster.equals(each.getClusterName()) && ip.equals(each.getIp()) && port == each.getPort()) { + instance = each; + break; + } + } + return instance; } } diff --git a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/InstancePublishInfo.java b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/InstancePublishInfo.java index 1632fa88ea7..e4893902940 100644 --- a/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/InstancePublishInfo.java +++ b/naming/src/main/java/com/alibaba/nacos/naming/core/v2/pojo/InstancePublishInfo.java @@ -17,6 +17,7 @@ package com.alibaba.nacos.naming.core.v2.pojo; import com.alibaba.nacos.common.utils.InternetAddressUtil; +import com.alibaba.nacos.common.utils.NumberUtils; import java.io.Serializable; import java.util.HashMap; @@ -126,4 +127,16 @@ public String toString() { public static String genMetadataId(String ip, int port, String cluster) { return ip + InternetAddressUtil.IP_PORT_SPLITER + port + InternetAddressUtil.IP_PORT_SPLITER + cluster; } + + public static String getClusterFromMetadataId(String metadataId) { + return metadataId.split(InternetAddressUtil.IP_PORT_SPLITER)[2]; + } + + public static String getIpFromMetadataId(String metadataId) { + return metadataId.split(InternetAddressUtil.IP_PORT_SPLITER)[0]; + } + + public static int getPortFromMetadataId(String metadataId) { + return NumberUtils.toInt(metadataId.split(InternetAddressUtil.IP_PORT_SPLITER)[1]); + } } diff --git a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/cleaner/ExpiredMetadataCleanerTest.java b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/cleaner/ExpiredMetadataCleanerTest.java index 8f88df9cb0d..8dbcc3eed5d 100644 --- a/naming/src/test/java/com/alibaba/nacos/naming/core/v2/cleaner/ExpiredMetadataCleanerTest.java +++ b/naming/src/test/java/com/alibaba/nacos/naming/core/v2/cleaner/ExpiredMetadataCleanerTest.java @@ -16,12 +16,19 @@ package com.alibaba.nacos.naming.core.v2.cleaner; +import com.alibaba.nacos.api.common.Constants; +import com.alibaba.nacos.api.naming.pojo.Instance; +import com.alibaba.nacos.api.naming.pojo.ServiceInfo; import com.alibaba.nacos.common.utils.ConcurrentHashSet; +import com.alibaba.nacos.naming.core.v2.index.ServiceStorage; import com.alibaba.nacos.naming.core.v2.metadata.ExpiredMetadataInfo; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataManager; import com.alibaba.nacos.naming.core.v2.metadata.NamingMetadataOperateService; +import com.alibaba.nacos.naming.core.v2.pojo.InstancePublishInfo; +import com.alibaba.nacos.naming.core.v2.pojo.Service; import com.alibaba.nacos.sys.env.EnvUtil; import junit.framework.TestCase; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -29,8 +36,11 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.mock.env.MockEnvironment; +import java.util.ArrayList; +import java.util.List; import java.util.Set; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -49,12 +59,18 @@ public class ExpiredMetadataCleanerTest extends TestCase { @Mock private ExpiredMetadataInfo expiredMetadataInfoMock; + + @Mock + private ExpiredMetadataInfo existingMetadataInfoMock; + + @Mock + private ServiceStorage serviceStorage; @Before public void setUp() throws Exception { super.setUp(); EnvUtil.setEnvironment(new MockEnvironment()); - expiredMetadataCleaner = new ExpiredMetadataCleaner(metadataManagerMock, metadataOperateServiceMock); + expiredMetadataCleaner = new ExpiredMetadataCleaner(metadataManagerMock, metadataOperateServiceMock, serviceStorage); set.add(expiredMetadataInfoMock); @@ -69,4 +85,41 @@ public void testDoClean() { verify(metadataManagerMock).getExpiredMetadataInfos(); verify(metadataOperateServiceMock).deleteServiceMetadata(expiredMetadataInfoMock.getService()); } -} \ No newline at end of file + + @Test + public void testCleanExistingInstanceMetadata() { + String serviceName = "test.1"; + String ip = "1.1.1.1"; + int port = 7001; + ServiceInfo serviceInfo = new ServiceInfo(); + serviceInfo.setName(serviceName); + Instance instance = new Instance(); + instance.setIp(ip); + instance.setPort(port); + instance.setClusterName(Constants.DEFAULT_CLUSTER_NAME); + List instances = new ArrayList<>(); + instances.add(instance); + serviceInfo.setHosts(instances); + + Service service = Service.newService(Constants.DEFAULT_NAMESPACE_ID, Constants.DEFAULT_GROUP, serviceName); + when(existingMetadataInfoMock.getService()).thenReturn(service); + when(existingMetadataInfoMock.getCreateTime()).thenReturn(0L); + when(existingMetadataInfoMock.getMetadataId()) + .thenReturn(InstancePublishInfo.genMetadataId(ip, port, Constants.DEFAULT_CLUSTER_NAME)); + + set.add(existingMetadataInfoMock); + + Assert.assertEquals(2, set.size()); + + when(serviceStorage.getPushData(service)).thenReturn(serviceInfo); + + expiredMetadataCleaner.doClean(); + + verify(metadataManagerMock).getExpiredMetadataInfos(); + verify(metadataOperateServiceMock).deleteServiceMetadata(expiredMetadataInfoMock.getService()); + verify(metadataOperateServiceMock, never()).deleteServiceMetadata(service); + verify(serviceStorage).getPushData(service); + + Assert.assertEquals(1, set.size()); + } +}