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 81f086b40b fix(base): resolve partial identities when removing 
metadata (#7790) (#7858)
81f086b40b is described below

commit 81f086b40b36b2631992141e716a278e1a9b410e
Author: konlue <[email protected]>
AuthorDate: Tue Oct 20 12:37:15 2026 +0800

    fix(base): resolve partial identities when removing metadata (#7790) (#7858)
---
 .../shenyu/plugin/base/cache/MetaDataCache.java    | 31 +++++++++++++-
 .../plugin/base/cache/MetaDataCacheTest.java       | 50 ++++++++++++++++++++++
 2 files changed, 79 insertions(+), 2 deletions(-)

diff --git 
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MetaDataCache.java
 
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MetaDataCache.java
index f15df36785..a6990e23d3 100644
--- 
a/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MetaDataCache.java
+++ 
b/shenyu-plugin/shenyu-plugin-base/src/main/java/org/apache/shenyu/plugin/base/cache/MetaDataCache.java
@@ -98,13 +98,40 @@ public final class MetaDataCache {
     /**
      * Remove auth data.
      *
+     * <p>Deletion events may carry only half of the identity: node sync sends
+     * an id without a path, path sync sends a path without an id (#7790).
+     * The partial identity is resolved against the cached canonical record so
+     * both the id map and the derived path results are invalidated, and the
+     * removal stays idempotent instead of throwing.</p>
+     *
      * @param data the data
      */
     public synchronized void remove(final MetaData data) {
-        META_DATA_MAP.remove(data.getId());
+        final String id = data.getId();
+        final String path = data.getPath();
+        final MetaData cached = Objects.nonNull(id) ? META_DATA_MAP.get(id) : 
findByPath(path);
+        if (Objects.nonNull(id)) {
+            META_DATA_MAP.remove(id);
+        } else if (Objects.nonNull(cached)) {
+            META_DATA_MAP.remove(cached.getId());
+        }
         indexDirty = true;
         negativeCacheDirty = true;
-        clean(data.getPath());
+        if (Objects.nonNull(path)) {
+            clean(path);
+        } else if (Objects.nonNull(cached) && 
Objects.nonNull(cached.getPath())) {
+            clean(cached.getPath());
+        }
+    }
+
+    private MetaData findByPath(final String path) {
+        if (Objects.isNull(path)) {
+            return null;
+        }
+        return META_DATA_MAP.values().stream()
+                .filter(metaData -> path.equals(metaData.getPath()))
+                .findFirst()
+                .orElse(null);
     }
 
     private void rebuildPathIndex() {
diff --git 
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MetaDataCacheTest.java
 
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MetaDataCacheTest.java
index a7e6b43690..4088c173e3 100644
--- 
a/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MetaDataCacheTest.java
+++ 
b/shenyu-plugin/shenyu-plugin-base/src/test/java/org/apache/shenyu/plugin/base/cache/MetaDataCacheTest.java
@@ -21,6 +21,8 @@ import org.apache.shenyu.common.dto.MetaData;
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.Test;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertSame;
 
 class MetaDataCacheTest {
@@ -45,6 +47,54 @@ class MetaDataCacheTest {
         assertSame(specific, cache.obtain("/api/users/42"));
     }
 
+    @Test
+    void shouldRemoveByIdOnlyRecordWithoutPath() {
+        MetaData full = createMetaData("id-1", "/rpc/dubbo/save");
+        cache.cache(full);
+        assertSame(full, cache.obtain("/rpc/dubbo/save"));
+
+        MetaData idOnly = new MetaData();
+        idOnly.setId("id-1");
+
+        assertDoesNotThrow(() -> cache.remove(idOnly));
+        assertNull(cache.obtain("/rpc/dubbo/save"));
+    }
+
+    @Test
+    void shouldRemoveByPathOnlyRecordWithoutId() {
+        MetaData full = createMetaData("id-2", "/rpc/sofa/find");
+        cache.cache(full);
+        assertSame(full, cache.obtain("/rpc/sofa/find"));
+
+        MetaData pathOnly = new MetaData();
+        pathOnly.setPath("/rpc/sofa/find");
+
+        assertDoesNotThrow(() -> cache.remove(pathOnly));
+        assertNull(cache.obtain("/rpc/sofa/find"));
+    }
+
+    @Test
+    void shouldKeepRemovingFullRecord() {
+        MetaData full = createMetaData("id-3", "/rpc/http/get");
+        cache.cache(full);
+
+        cache.remove(createMetaData("id-3", "/rpc/http/get"));
+
+        assertNull(cache.obtain("/rpc/http/get"));
+    }
+
+    @Test
+    void shouldBeIdempotentForPartialRecords() {
+        MetaData idOnly = new MetaData();
+        idOnly.setId("missing-id");
+        MetaData pathOnly = new MetaData();
+        pathOnly.setPath("/rpc/never/cached");
+
+        assertDoesNotThrow(() -> cache.remove(idOnly));
+        assertDoesNotThrow(() -> cache.remove(pathOnly));
+        assertDoesNotThrow(() -> cache.remove(idOnly));
+    }
+
     private MetaData createMetaData(final String id, final String path) {
         MetaData metaData = new MetaData();
         metaData.setId(id);

Reply via email to