This is an automated email from the ASF dual-hosted git repository.

jt2594838 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 9be80ead1dc Fix PathPatternNode wildcard cache lifecycle (#18672)
9be80ead1dc is described below

commit 9be80ead1dc51e1100ebc4add54be055afc2e888
Author: Caideyipi <[email protected]>
AuthorDate: Mon Sep 21 11:11:03 2026 +0800

    Fix PathPatternNode wildcard cache lifecycle (#18672)
---
 .../apache/iotdb/commons/path/PathPatternNode.java | 31 ++++++----
 .../apache/iotdb/commons/path/PathPatternUtil.java |  6 +-
 .../iotdb/commons/path/PathPatternNodeTest.java    | 71 ++++++++++++++++++++++
 3 files changed, 96 insertions(+), 12 deletions(-)

diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternNode.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternNode.java
index 2003fdfcc95..56240432f69 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternNode.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternNode.java
@@ -38,6 +38,7 @@ import java.util.Set;
 import java.util.function.BiConsumer;
 import java.util.function.Consumer;
 import java.util.function.Supplier;
+import java.util.regex.Pattern;
 
 import static 
org.apache.iotdb.commons.conf.IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD;
 import static 
org.apache.iotdb.commons.conf.IoTDBConstant.ONE_LEVEL_PATH_WILDCARD;
@@ -58,10 +59,9 @@ public class PathPatternNode<V, S extends 
PathPatternNode.Serializer<V>> impleme
 
   private final S serializer;
 
-  // Children names with wildcard, for accelerating wildcard searching
-  // Here we do not include "*" or "**"
-  // to ensure that the set is empty in most of the time, in order to save 
memory.
-  private final Set<String> childrenNamesWithNonTrivialWildcard = new 
HashSet<>();
+  // Compiled patterns for child names with wildcard, for accelerating 
wildcard searching.
+  // Here we do not include "*" or "**" to ensure that the map is empty most 
of the time.
+  private final Map<String, Pattern> childrenPatternsWithNonTrivialWildcard = 
new HashMap<>();
 
   public PathPatternNode(final String name, final S serializer) {
     this.name = name;
@@ -97,10 +97,12 @@ public class PathPatternNode<V, S extends 
PathPatternNode.Serializer<V>> impleme
     if (children.containsKey(MULTI_LEVEL_PATH_WILDCARD)) {
       res.add(children.get(MULTI_LEVEL_PATH_WILDCARD));
     }
-    childrenNamesWithNonTrivialWildcard.stream()
-        .filter(path -> PathPatternUtil.isNodeMatch(path, nodeName))
-        .map(children::get)
-        .forEach(res::add);
+    for (final Map.Entry<String, Pattern> entry :
+        childrenPatternsWithNonTrivialWildcard.entrySet()) {
+      if (entry.getValue().matcher(nodeName).matches()) {
+        res.add(children.get(entry.getKey()));
+      }
+    }
     return res;
   }
 
@@ -113,13 +115,16 @@ public class PathPatternNode<V, S extends 
PathPatternNode.Serializer<V>> impleme
     if (PathPatternUtil.hasWildcard(nodeName)
         && !PathPatternUtil.isMultiLevelMatchWildcard(nodeName)
         && !ONE_LEVEL_PATH_WILDCARD.equals(nodeName)) {
-      childrenNamesWithNonTrivialWildcard.add(nodeName);
+      childrenPatternsWithNonTrivialWildcard.computeIfAbsent(
+          nodeName, PathPatternUtil::compileNodePattern);
     }
     children.put(nodeName, tmpNode);
   }
 
   public void deleteChild(final PathPatternNode<V, S> tmpNode) {
-    children.remove(tmpNode.getName());
+    final String nodeName = tmpNode.getName();
+    children.remove(nodeName);
+    childrenPatternsWithNonTrivialWildcard.remove(nodeName);
   }
 
   public void appendValue(final V value, final BiConsumer<V, Set<V>> 
remappingFunction) {
@@ -256,6 +261,7 @@ public class PathPatternNode<V, S extends 
PathPatternNode.Serializer<V>> impleme
       valueSet.clear();
     }
     children.clear();
+    childrenPatternsWithNonTrivialWildcard.clear();
   }
 
   public static <V, T extends PathPatternNode.Serializer<V>> 
PathPatternNode<V, T> deserializeNode(
@@ -289,7 +295,10 @@ public class PathPatternNode<V, S extends 
PathPatternNode.Serializer<V>> impleme
     return SHALLOW_SIZE
         + RamUsageEstimator.sizeOf(name)
         + RamUsageEstimator.sizeOfHashSet(valueSet)
-        + RamUsageEstimator.sizeOfHashSet(childrenNamesWithNonTrivialWildcard)
+        + RamUsageEstimator.sizeOfMapWithKnownShallowSize(
+            childrenPatternsWithNonTrivialWildcard,
+            RamUsageEstimator.SHALLOW_SIZE_OF_HASHMAP,
+            RamUsageEstimator.SHALLOW_SIZE_OF_HASHMAP_ENTRY)
         + RamUsageEstimator.sizeOfMapWithKnownShallowSize(
             children,
             RamUsageEstimator.SHALLOW_SIZE_OF_HASHMAP,
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternUtil.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternUtil.java
index caf65e849a1..44347b8959f 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternUtil.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternUtil.java
@@ -53,6 +53,10 @@ public class PathPatternUtil {
         || patternNode.equals(MULTI_LEVEL_PATH_WILDCARD)) {
       return true;
     }
-    return Pattern.matches(patternNode.replace("*", ".*"), nodeName);
+    return compileNodePattern(patternNode).matcher(nodeName).matches();
+  }
+
+  static Pattern compileNodePattern(final String patternNode) {
+    return Pattern.compile(patternNode.replace("*", ".*"));
   }
 }
diff --git 
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PathPatternNodeTest.java
 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PathPatternNodeTest.java
new file mode 100644
index 00000000000..7206c9cc8f4
--- /dev/null
+++ 
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/path/PathPatternNodeTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.iotdb.commons.path;
+
+import org.apache.iotdb.commons.path.PathPatternNode.VoidSerializer;
+
+import org.junit.Test;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+public class PathPatternNodeTest {
+
+  @Test
+  public void testNonTrivialWildcardChildCacheLifecycle() {
+    final PathPatternNode<Void, VoidSerializer> parent = newNode("parent");
+    final PathPatternNode<Void, VoidSerializer> wildcardChild = 
newNode("device*");
+
+    parent.addChild(wildcardChild);
+    final List<PathPatternNode<Void, VoidSerializer>> matchedChildren =
+        parent.getMatchChildren("device1");
+    assertEquals(1, matchedChildren.size());
+    assertSame(wildcardChild, matchedChildren.get(0));
+
+    parent.deleteChild(wildcardChild);
+    assertTrue(parent.getMatchChildren("device1").isEmpty());
+
+    parent.addChild(wildcardChild);
+    parent.clear();
+    assertTrue(parent.getMatchChildren("device1").isEmpty());
+  }
+
+  @Test
+  public void testReplacingNonTrivialWildcardChildKeepsCache() {
+    final PathPatternNode<Void, VoidSerializer> parent = newNode("parent");
+    final PathPatternNode<Void, VoidSerializer> originalChild = 
newNode("device*");
+    final PathPatternNode<Void, VoidSerializer> replacementChild = 
newNode("device*");
+
+    parent.addChild(originalChild);
+    parent.addChild(replacementChild);
+
+    final List<PathPatternNode<Void, VoidSerializer>> matchedChildren =
+        parent.getMatchChildren("device1");
+    assertEquals(1, matchedChildren.size());
+    assertSame(replacementChild, matchedChildren.get(0));
+  }
+
+  private PathPatternNode<Void, VoidSerializer> newNode(final String name) {
+    return new PathPatternNode<>(name, VoidSerializer.getInstance());
+  }
+}

Reply via email to