Skip to content

Commit ba78757

Browse files
committed
Fix PathPatternNode wildcard cache lifecycle (#18672)
1 parent 46541cc commit ba78757

3 files changed

Lines changed: 104 additions & 12 deletions

File tree

‎iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternNode.java‎

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import java.util.function.BiConsumer;
3939
import java.util.function.Consumer;
4040
import java.util.function.Supplier;
41+
import java.util.regex.Pattern;
4142

4243
import static org.apache.iotdb.commons.conf.IoTDBConstant.MULTI_LEVEL_PATH_WILDCARD;
4344
import static org.apache.iotdb.commons.conf.IoTDBConstant.ONE_LEVEL_PATH_WILDCARD;
@@ -58,9 +59,9 @@ public class PathPatternNode<V, VSerializer extends PathPatternNode.Serializer<V
5859

5960
private final VSerializer serializer;
6061

61-
// Children names with wildcard, for accelerating wildcard searching.
62-
// Here we do not include "*" or "**" to ensure that the set is empty in most cases.
63-
private final Set<String> childrenNamesWithNonTrivialWildcard = new HashSet<>();
62+
// Compiled patterns for child names with wildcard, for accelerating wildcard searching.
63+
// Here we do not include "*" or "**" to ensure that the map is empty most of the time.
64+
private final Map<String, Pattern> childrenPatternsWithNonTrivialWildcard = new HashMap<>();
6465

6566
public PathPatternNode(String name, VSerializer serializer) {
6667
this.name = name;
@@ -94,10 +95,12 @@ public List<PathPatternNode<V, VSerializer>> getMatchChildren(String nodeName) {
9495
if (children.containsKey(MULTI_LEVEL_PATH_WILDCARD)) {
9596
res.add(children.get(MULTI_LEVEL_PATH_WILDCARD));
9697
}
97-
childrenNamesWithNonTrivialWildcard.stream()
98-
.filter(path -> PathPatternUtil.isNodeMatch(path, nodeName))
99-
.map(children::get)
100-
.forEach(res::add);
98+
for (final Map.Entry<String, Pattern> entry :
99+
childrenPatternsWithNonTrivialWildcard.entrySet()) {
100+
if (entry.getValue().matcher(nodeName).matches()) {
101+
res.add(children.get(entry.getKey()));
102+
}
103+
}
101104
return res;
102105
}
103106

@@ -110,13 +113,16 @@ public void addChild(PathPatternNode<V, VSerializer> tmpNode) {
110113
if (PathPatternUtil.hasWildcard(nodeName)
111114
&& !PathPatternUtil.isMultiLevelMatchWildcard(nodeName)
112115
&& !ONE_LEVEL_PATH_WILDCARD.equals(nodeName)) {
113-
childrenNamesWithNonTrivialWildcard.add(nodeName);
116+
childrenPatternsWithNonTrivialWildcard.computeIfAbsent(
117+
nodeName, PathPatternUtil::compileNodePattern);
114118
}
115119
children.put(nodeName, tmpNode);
116120
}
117121

118-
public void deleteChild(PathPatternNode<V, VSerializer> tmpNode) {
119-
children.remove(tmpNode.getName());
122+
public void deleteChild(final PathPatternNode<V, VSerializer> tmpNode) {
123+
final String nodeName = tmpNode.getName();
124+
children.remove(nodeName);
125+
childrenPatternsWithNonTrivialWildcard.remove(nodeName);
120126
}
121127

122128
public void appendValue(V value, BiConsumer<V, Set<V>> remappingFunction) {
@@ -248,6 +254,14 @@ void serializeChildren(DataOutputStream outputStream) throws IOException {
248254
}
249255
}
250256

257+
void clear() {
258+
if (Objects.nonNull(valueSet)) {
259+
valueSet.clear();
260+
}
261+
children.clear();
262+
childrenPatternsWithNonTrivialWildcard.clear();
263+
}
264+
251265
public static <V, T extends PathPatternNode.Serializer<V>> PathPatternNode<V, T> deserializeNode(
252266
ByteBuffer buffer, T serializer, Consumer<String> nodeNameProcessor) {
253267
PathPatternNode<V, T> node =
@@ -279,7 +293,10 @@ public long ramBytesUsed() {
279293
return SHALLOW_SIZE
280294
+ RamUsageEstimator.sizeOf(name)
281295
+ RamUsageEstimator.sizeOfHashSet(valueSet)
282-
+ RamUsageEstimator.sizeOfHashSet(childrenNamesWithNonTrivialWildcard)
296+
+ RamUsageEstimator.sizeOfMapWithKnownShallowSize(
297+
childrenPatternsWithNonTrivialWildcard,
298+
RamUsageEstimator.SHALLOW_SIZE_OF_HASHMAP,
299+
RamUsageEstimator.SHALLOW_SIZE_OF_HASHMAP_ENTRY)
283300
+ RamUsageEstimator.sizeOfMapWithKnownShallowSize(
284301
children,
285302
RamUsageEstimator.SHALLOW_SIZE_OF_HASHMAP,

‎iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/path/PathPatternUtil.java‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ public static boolean isNodeMatch(String patternNode, String nodeName) {
5252
|| patternNode.equals(MULTI_LEVEL_PATH_WILDCARD)) {
5353
return true;
5454
}
55-
return Pattern.matches(patternNode.replace("*", ".*"), nodeName);
55+
return compileNodePattern(patternNode).matcher(nodeName).matches();
56+
}
57+
58+
static Pattern compileNodePattern(final String patternNode) {
59+
return Pattern.compile(patternNode.replace("*", ".*"));
5660
}
5761
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.iotdb.commons.path;
21+
22+
import org.apache.iotdb.commons.path.PathPatternNode.VoidSerializer;
23+
24+
import org.junit.Test;
25+
26+
import java.util.List;
27+
28+
import static org.junit.Assert.assertEquals;
29+
import static org.junit.Assert.assertSame;
30+
import static org.junit.Assert.assertTrue;
31+
32+
public class PathPatternNodeTest {
33+
34+
@Test
35+
public void testNonTrivialWildcardChildCacheLifecycle() {
36+
final PathPatternNode<Void, VoidSerializer> parent = newNode("parent");
37+
final PathPatternNode<Void, VoidSerializer> wildcardChild = newNode("device*");
38+
39+
parent.addChild(wildcardChild);
40+
final List<PathPatternNode<Void, VoidSerializer>> matchedChildren =
41+
parent.getMatchChildren("device1");
42+
assertEquals(1, matchedChildren.size());
43+
assertSame(wildcardChild, matchedChildren.get(0));
44+
45+
parent.deleteChild(wildcardChild);
46+
assertTrue(parent.getMatchChildren("device1").isEmpty());
47+
48+
parent.addChild(wildcardChild);
49+
parent.clear();
50+
assertTrue(parent.getMatchChildren("device1").isEmpty());
51+
}
52+
53+
@Test
54+
public void testReplacingNonTrivialWildcardChildKeepsCache() {
55+
final PathPatternNode<Void, VoidSerializer> parent = newNode("parent");
56+
final PathPatternNode<Void, VoidSerializer> originalChild = newNode("device*");
57+
final PathPatternNode<Void, VoidSerializer> replacementChild = newNode("device*");
58+
59+
parent.addChild(originalChild);
60+
parent.addChild(replacementChild);
61+
62+
final List<PathPatternNode<Void, VoidSerializer>> matchedChildren =
63+
parent.getMatchChildren("device1");
64+
assertEquals(1, matchedChildren.size());
65+
assertSame(replacementChild, matchedChildren.get(0));
66+
}
67+
68+
private PathPatternNode<Void, VoidSerializer> newNode(final String name) {
69+
return new PathPatternNode<>(name, VoidSerializer.getInstance());
70+
}
71+
}

0 commit comments

Comments
 (0)