Skip to content

Commit 41048fb

Browse files
authored
HDDS-12563. Cache and reuse ContainerID object (#10642)
1 parent 27763e6 commit 41048fb

3 files changed

Lines changed: 176 additions & 2 deletions

File tree

hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/scm/container/ContainerID.java

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.hadoop.hdds.utils.db.DelegatedCodec;
2828
import org.apache.hadoop.hdds.utils.db.LongCodec;
2929
import org.apache.ratis.util.MemoizedSupplier;
30+
import org.apache.ratis.util.WeakValueCache;
3031

3132
/**
3233
* Container ID is an integer that is a value between 1..MAX_CONTAINER ID.
@@ -42,7 +43,9 @@ public final class ContainerID implements Comparable<ContainerID> {
4243
LongCodec.get(), ContainerID::valueOf, c -> c.id,
4344
ContainerID.class, DelegatedCodec.CopyType.SHALLOW);
4445

45-
public static final ContainerID MIN = ContainerID.valueOf(0);
46+
public static final ContainerID MIN = new ContainerID(0);
47+
private static final WeakValueCache<Long, ContainerID> CACHE
48+
= new WeakValueCache<>("containerId", ContainerID::new);
4649

4750
private final long id;
4851
private final Supplier<HddsProtos.ContainerID> proto;
@@ -71,7 +74,11 @@ private ContainerID(long id) {
7174
* @return ContainerID.
7275
*/
7376
public static ContainerID valueOf(final long containerID) {
74-
return new ContainerID(containerID);
77+
return CACHE.getOrCreate(containerID);
78+
}
79+
80+
static WeakValueCache<Long, ContainerID> getCacheForTesting() {
81+
return CACHE;
7582
}
7683

7784
/**
@@ -87,6 +94,10 @@ public long getId() {
8794
return id;
8895
}
8996

97+
public long getIdForTesting() {
98+
return id;
99+
}
100+
90101
public static byte[] getBytes(long id) {
91102
return LongCodec.get().toPersistedFormat(id);
92103
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.hadoop.hdds.scm.container;
19+
20+
import static org.apache.hadoop.hdds.utils.db.CodecTestUtil.gc;
21+
import static org.junit.jupiter.api.Assertions.assertEquals;
22+
import static org.junit.jupiter.api.Assertions.assertSame;
23+
24+
import java.util.ArrayList;
25+
import java.util.Comparator;
26+
import java.util.LinkedList;
27+
import java.util.List;
28+
import java.util.concurrent.ThreadLocalRandom;
29+
import java.util.concurrent.TimeUnit;
30+
import org.apache.ratis.util.JavaUtils;
31+
import org.apache.ratis.util.RatisUtilTestUtil;
32+
import org.apache.ratis.util.TimeDuration;
33+
import org.apache.ratis.util.WeakValueCache;
34+
import org.junit.jupiter.api.Test;
35+
import org.slf4j.Logger;
36+
import org.slf4j.LoggerFactory;
37+
38+
/** Test {@link ContainerID}. */
39+
public final class TestContainerID {
40+
private static final Logger LOG = LoggerFactory.getLogger(TestContainerID.class);
41+
42+
private static final WeakValueCache<Long, ContainerID> CACHE = ContainerID.getCacheForTesting();
43+
44+
static String dumpCache() {
45+
final List<ContainerID> values = RatisUtilTestUtil.getValues(CACHE);
46+
values.sort(Comparator.comparing(ContainerID::getIdForTesting));
47+
String header = CACHE + ": " + values.size();
48+
System.out.println(header);
49+
System.out.println(" " + values);
50+
return header;
51+
}
52+
53+
static void assertCache(IDs expectedIDs) {
54+
final List<ContainerID> computed = RatisUtilTestUtil.getValues(CACHE);
55+
computed.sort(Comparator.comparing(ContainerID::getIdForTesting));
56+
57+
final List<ContainerID> expected = expectedIDs.getIds();
58+
expected.sort(Comparator.comparing(ContainerID::getIdForTesting));
59+
60+
assertEquals(expected, computed, TestContainerID::dumpCache);
61+
}
62+
63+
void assertCacheSizeWithGC(IDs expectedIDs) throws Exception {
64+
JavaUtils.attempt(() -> {
65+
gc();
66+
assertCache(expectedIDs);
67+
}, 5, TimeDuration.valueOf(100, TimeUnit.MILLISECONDS), "assertCacheSizeWithGC", LOG);
68+
}
69+
70+
static class IDs {
71+
private final List<ContainerID> ids = new LinkedList<>();
72+
73+
List<ContainerID> getIds() {
74+
return new ArrayList<>(ids);
75+
}
76+
77+
int size() {
78+
return ids.size();
79+
}
80+
81+
ContainerID allocate() {
82+
final ContainerID id = ContainerID.valueOf(ThreadLocalRandom.current().nextLong(Long.MAX_VALUE));
83+
LOG.info("allocate {}", id);
84+
ids.add(id);
85+
return id;
86+
}
87+
88+
void release() {
89+
final int r = ThreadLocalRandom.current().nextInt(size());
90+
final ContainerID removed = ids.remove(r);
91+
LOG.info("release {}", removed);
92+
}
93+
}
94+
95+
@Test
96+
public void testCaching() throws Exception {
97+
final int n = 100;
98+
final IDs ids = new IDs();
99+
assertEquals(0, ids.size());
100+
assertCache(ids);
101+
102+
for (int i = 0; i < n; i++) {
103+
final ContainerID id = ids.allocate();
104+
assertSame(id, ContainerID.valueOf(id.getIdForTesting()));
105+
assertCache(ids);
106+
}
107+
108+
for (int i = 0; i < n / 2; i++) {
109+
ids.release();
110+
if (ThreadLocalRandom.current().nextInt(10) == 0) {
111+
assertCacheSizeWithGC(ids);
112+
}
113+
}
114+
assertCacheSizeWithGC(ids);
115+
116+
for (int i = 0; i < n / 2; i++) {
117+
final ContainerID id = ids.allocate();
118+
assertSame(id, ContainerID.valueOf(id.getIdForTesting()));
119+
assertCache(ids);
120+
}
121+
122+
123+
for (int i = 0; i < n; i++) {
124+
ids.release();
125+
if (ThreadLocalRandom.current().nextInt(10) == 0) {
126+
assertCacheSizeWithGC(ids);
127+
}
128+
}
129+
assertCacheSizeWithGC(ids);
130+
131+
assertEquals(0, ids.size());
132+
}
133+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ratis.util;
19+
20+
import java.util.List;
21+
22+
/** Test util for the {@link org.apache.ratis.util} package. */
23+
public final class RatisUtilTestUtil {
24+
25+
private RatisUtilTestUtil() { }
26+
27+
public static <K, V> List<V> getValues(WeakValueCache<K, V> cache) {
28+
return cache.getValues();
29+
}
30+
}

0 commit comments

Comments
 (0)