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());
+ }
+}