From 02d5ba9c7d1cbdd1bee36390a563cc0f1a90449d Mon Sep 17 00:00:00 2001 From: Tsz-Wo Nicholas Sze Date: Sun, 16 Mar 2025 15:23:58 -0700 Subject: [PATCH 1/3] RATIS-2258. Caching TermIndex objects. --- .../ratis/server/protocol/TermIndex.java | 39 +---- .../ratis/server/protocol/TermIndexImpl.java | 144 ++++++++++++++++++ .../server/protocol/TestTermIndexImpl.java | 97 ++++++++++++ 3 files changed, 242 insertions(+), 38 deletions(-) create mode 100644 ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndexImpl.java create mode 100644 ratis-test/src/test/java/org/apache/ratis/server/protocol/TestTermIndexImpl.java diff --git a/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java b/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java index dac1a51d23..3f0f5bdb49 100644 --- a/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java +++ b/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java @@ -73,43 +73,6 @@ static TermIndex valueOf(LogEntryProto proto) { /** @return a {@link TermIndex} object. */ static TermIndex valueOf(long term, long index) { - return new TermIndex() { - @Override - public long getTerm() { - return term; - } - - @Override - public long getIndex() { - return index; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } else if (!(obj instanceof TermIndex)) { - return false; - } - - final TermIndex that = (TermIndex) obj; - return this.getTerm() == that.getTerm() - && this.getIndex() == that.getIndex(); - } - - @Override - public int hashCode() { - return Long.hashCode(term) ^ Long.hashCode(index); - } - - private String longToString(long n) { - return n >= 0L? String.valueOf(n) : "~"; - } - - @Override - public String toString() { - return String.format("(t:%s, i:%s)", longToString(term), longToString(index)); - } - }; + return TermIndexImpl.valueOf(term, index); } } \ No newline at end of file diff --git a/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndexImpl.java b/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndexImpl.java new file mode 100644 index 0000000000..d516728527 --- /dev/null +++ b/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndexImpl.java @@ -0,0 +1,144 @@ +/* + * 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.ratis.server.protocol; + +import org.apache.ratis.thirdparty.com.google.common.collect.MapMaker; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +/** + * An implementation of {@link TermIndex}. + * Note that this is not a public API. + */ +class TermIndexImpl implements TermIndex { + private static final AtomicInteger OBJECT_COUNT = new AtomicInteger(); + + /** Weak value cache -- the same {@link TermIndex} will not be created more than once. */ + static final class Cache { + private Cache() {} + + private static ConcurrentMap newMap() { + return new MapMaker().weakValues().makeMap(); + } + + /** Map: term -> (index -> {@link TermIndex}). */ + private static final ConcurrentMap> MAP = new ConcurrentHashMap<>(); + + private static TermIndex getOrCreate(long term, long index) { + final ConcurrentMap indexMap = MAP.computeIfAbsent(term, k -> newMap()); + final TermIndex computed = indexMap.computeIfAbsent(index, i -> new TermIndexImpl(term, i)); + if ((OBJECT_COUNT.get() & 0xFFFF) == 0) { + cleanupEmptyInnerMaps(); // cleanup empty maps once in a while + } + return computed; + } + + static int indexCount(long term) { + final ConcurrentMap indexMap = MAP.get(term); + if (indexMap == null) { + return 0; + } + + // size() may return incorrect result; see Guava MapMaker javadoc + int n = 0; + for (Long ignored : indexMap.keySet()) { + n++; + } + return n; + } + + static void cleanupEmptyInnerMaps() { + // isEmpty() may return incorrect result; see Guava MapMaker javadoc + MAP.values().removeIf(e -> !e.entrySet().iterator().hasNext()); + } + + static int dump(Consumer out) { + out.accept("TermIndex Cache:\n"); + int emptyCount = 0; + for (Map.Entry> entry : MAP.entrySet()) { + final long term = entry.getKey(); + final ConcurrentMap indexMap = entry.getValue(); + final int count = indexCount(term); + if (count == 0) { + emptyCount++; + } + + out.accept(" term=" + term); + out.accept(indexMap.keySet()); + out.accept(" count=" + indexCount(term)); + out.accept(", size=" + indexMap.size()); + out.accept("\n"); + } + return emptyCount; + } + } + + static TermIndex valueOf(long term, long index) { + return Cache.getOrCreate(term, index); + } + + private final long term; + private final long index; + + TermIndexImpl(long term, long index) { + this.term = term; + this.index = index; + OBJECT_COUNT.incrementAndGet(); + } + + @Override + public long getTerm() { + return term; + } + + @Override + public long getIndex() { + return index; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (!(obj instanceof TermIndexImpl)) { + return false; + } + + final TermIndexImpl that = (TermIndexImpl) obj; + return this.getTerm() == that.getTerm() + && this.getIndex() == that.getIndex(); + } + + @Override + public int hashCode() { + return Long.hashCode(term) ^ Long.hashCode(index); + } + + private String longToString(long n) { + return n >= 0L ? String.valueOf(n) : "~"; + } + + @Override + public String toString() { + return String.format("(t:%s, i:%s)", longToString(term), longToString(index)); + } +} \ No newline at end of file diff --git a/ratis-test/src/test/java/org/apache/ratis/server/protocol/TestTermIndexImpl.java b/ratis-test/src/test/java/org/apache/ratis/server/protocol/TestTermIndexImpl.java new file mode 100644 index 0000000000..48cd00ebe4 --- /dev/null +++ b/ratis-test/src/test/java/org/apache/ratis/server/protocol/TestTermIndexImpl.java @@ -0,0 +1,97 @@ +/* + * 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.ratis.server.protocol; + +import org.apache.ratis.BaseTest; +import org.apache.ratis.RaftTestUtil; +import org.apache.ratis.util.JavaUtils; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** Testing {@link TermIndexImpl}. */ +public class TestTermIndexImpl extends BaseTest { + static void dumpCache(Integer expectedEmptyCount) { + final int computed = TermIndexImpl.Cache.dump(System.out::print); + if (expectedEmptyCount != null) { + assertEquals(expectedEmptyCount, computed); + } + System.out.flush(); + } + + static void assertCacheSize(int expectedSize, long term) { + final int computed = TermIndexImpl.Cache.indexCount(term); + if (computed != expectedSize) { + dumpCache(null); + } + assertEquals(expectedSize, computed); + } + + void assertCacheSizeWithGC(int expectedSize, long term) throws Exception{ + JavaUtils.attempt(() -> { + RaftTestUtil.gc(); + assertCacheSize(expectedSize, term); + }, 5, HUNDRED_MILLIS, "assertCacheSizeWithGC", LOG); + } + + static void initTermIndex(TermIndex[][] ti, int term, int index) { + ti[term][index] = TermIndex.valueOf(term, index); + } + + @Test + public void testCaching() throws Exception { + final int n = 9; + final TermIndex[][] ti = new TermIndex[n][n]; + final long[] terms = new long[n]; + final long[] indices = new long[n]; + for(int j = 0; j < n; j++) { + terms[j] = j; + indices[j] = j; + } + + assertCacheSize(0, terms[1]); + initTermIndex(ti, 1, 1); + assertSame(ti[1][1], TermIndex.valueOf(terms[1], indices[1])); + assertCacheSize(1, terms[1]); + + initTermIndex(ti, 1, 2); + assertSame(ti[1][1], TermIndex.valueOf(terms[1], indices[1])); + assertSame(ti[1][2], TermIndex.valueOf(terms[1], indices[2])); + assertCacheSize(2, terms[1]); + dumpCache(0); + + initTermIndex(ti, 2, 2); + assertSame(ti[1][1], TermIndex.valueOf(terms[1], indices[1])); + assertSame(ti[1][2], TermIndex.valueOf(terms[1], indices[2])); + assertSame(ti[2][2], TermIndex.valueOf(terms[2], indices[2])); + assertCacheSize(2, terms[1]); + assertCacheSize(1, terms[2]); + dumpCache(0); + + ti[1][1] = null; // release ti[1][1]; + assertCacheSizeWithGC(1, terms[1]); + dumpCache(0); + + ti[1][2] = null; // release ti[1][2]; + assertCacheSizeWithGC(0, terms[1]); + dumpCache(1); + + TermIndexImpl.Cache.cleanupEmptyInnerMaps(); + dumpCache(0); + } +} From 907a9f8bbcd6530e37e368ee506eaaf5f82f02fe Mon Sep 17 00:00:00 2001 From: Tsz-Wo Nicholas Sze Date: Tue, 18 Mar 2025 15:03:41 -0700 Subject: [PATCH 2/3] Address review comment and code refactoring. --- .../apache/ratis/util/BiWeakValueCache.java | 127 +++++++++++++++ .../ratis/server/protocol/TermIndex.java | 57 ++++++- .../ratis/server/protocol/TermIndexImpl.java | 144 ------------------ .../server/protocol/ProtocolTestUtils.java | 26 ++++ .../TestTermIndex.java} | 17 ++- 5 files changed, 219 insertions(+), 152 deletions(-) create mode 100644 ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java delete mode 100644 ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndexImpl.java create mode 100644 ratis-test/src/test/java/org/apache/ratis/server/protocol/ProtocolTestUtils.java rename ratis-test/src/test/java/org/apache/ratis/{server/protocol/TestTermIndexImpl.java => util/TestTermIndex.java} (86%) diff --git a/ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java b/ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java new file mode 100644 index 0000000000..85f6e6b0f0 --- /dev/null +++ b/ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java @@ -0,0 +1,127 @@ +package org.apache.ratis.util; + +import org.apache.ratis.thirdparty.com.google.common.collect.MapMaker; + +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BiFunction; +import java.util.function.Consumer; + +/** + * Weak Value Cache: ({@link OUTER}, {@link INNER}) -> {@link T}. + *

+ * Note that the cached values are weakly referenced. + * A cached value could be garage-collected (i.e. evicted from the cache) + * when there are no external (strong) references. + * + * @param the type of the outer keys. + * @param the type of the inner keys. + * @param the type to be cached. + */ +public final class BiWeakValueCache { + private static ConcurrentMap newMap() { + return new MapMaker().weakValues().makeMap(); + } + + private final String outerName; + private final String innerName; + private final String name; + + /** For constructing {@link T} values from ({@link OUTER}, {@link INNER}) keys. */ + private final BiFunction constructor; + /** Count the number of {@link T} values constructed. */ + private final AtomicInteger valueCount = new AtomicInteger(0); + + /** + * Actual map {@link OUTER} -> ({@link INNER} -> {@link T}) + * for the logical view ({@link OUTER}, {@link INNER}) -> {@link T}. + */ + private final ConcurrentMap> map = new ConcurrentHashMap<>(); + + /** + * Create a cache for mapping ({@link OUTER}, {@link INNER}) keys to {@link T} values. + * + * @param outerName the name of the outer long. + * @param innerName the name of the inner long. + * @param constructor for constructing {@link T} values. + */ + public BiWeakValueCache(String outerName, String innerName, BiFunction constructor) { + this.outerName = outerName; + this.innerName = innerName; + this.name = "(" + outerName + ", " + innerName + ")-cache"; + this.constructor = constructor; + } + + private T construct(OUTER outer, INNER inner) { + final T constructed = constructor.apply(outer, inner); + Objects.requireNonNull(constructed, "constructed == null"); + valueCount.incrementAndGet(); + return constructed; + } + + /** + * If the key ({@link OUTER}, {@link INNER}) is in the cache, return the cached values. + * Otherwise, create a new value and then return it. + */ + public T getOrCreate(OUTER outer, INNER inner) { + Objects.requireNonNull(outer, () -> outerName + " (outer) == null"); + Objects.requireNonNull(inner, () -> innerName + " (inner) == null"); + final ConcurrentMap innerMap = map.computeIfAbsent(outer, k -> newMap()); + final T computed = innerMap.computeIfAbsent(inner, i -> construct(outer, i)); + if ((valueCount.get() & 0xFFF) == 0) { + cleanupEmptyInnerMaps(); // cleanup empty maps once in a while + } + return computed; + } + + /** @return the value count for the given outer key. */ + int count(OUTER outer) { + final ConcurrentMap innerMap = map.get(outer); + if (innerMap == null) { + return 0; + } + + // size() may return incorrect result; see Guava MapMaker javadoc + int n = 0; + for (INNER ignored : innerMap.keySet()) { + n++; + } + return n; + } + + void cleanupEmptyInnerMaps() { + // isEmpty() may return incorrect result; see Guava MapMaker javadoc + map.values().removeIf(e -> !e.entrySet().iterator().hasNext()); + } + + @Override + public String toString() { + return name; + } + + /** The cache content for debugging. */ + int dump(Consumer out) { + out.accept(name + ":\n"); + int emptyCount = 0; + for (Map.Entry> entry : map.entrySet()) { + final OUTER outer = entry.getKey(); + final ConcurrentMap innerMap = entry.getValue(); + final int count = count(outer); + if (count == 0) { + emptyCount++; + } + + out.accept(" " + outerName + ":" + outer); + out.accept(", " + innerName + ":" + innerMap.keySet()); + out.accept(", count=" + count); + out.accept(", size=" + innerMap.size()); + out.accept("\n"); + } + out.accept(" emptyCount=" + emptyCount); + out.accept("\n"); + return emptyCount; + } +} diff --git a/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java b/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java index 3f0f5bdb49..79d1d582c2 100644 --- a/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java +++ b/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java @@ -20,6 +20,7 @@ import org.apache.ratis.proto.RaftProtos.LogEntryProto; import org.apache.ratis.proto.RaftProtos.TermIndexProto; import org.apache.ratis.server.raftlog.RaftLog; +import org.apache.ratis.util.BiWeakValueCache; import java.util.Comparator; import java.util.Optional; @@ -73,6 +74,60 @@ static TermIndex valueOf(LogEntryProto proto) { /** @return a {@link TermIndex} object. */ static TermIndex valueOf(long term, long index) { - return TermIndexImpl.valueOf(term, index); + return Impl.getCache().getOrCreate(term, index); + } + + /** + * An implementation for private use. + * Note that this is not a public API, although this is public class. + */ + class Impl { + private static final BiWeakValueCache CACHE + = new BiWeakValueCache<>("term", "index", Impl::newTermIndex); + + static BiWeakValueCache getCache() { + return CACHE; + } + + private static TermIndex newTermIndex(long term, long index) { + return new TermIndex() { + @Override + public long getTerm() { + return term; + } + + @Override + public long getIndex() { + return index; + } + + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (!(obj instanceof TermIndex)) { + return false; + } + + final TermIndex that = (TermIndex) obj; + return this.getTerm() == that.getTerm() + && this.getIndex() == that.getIndex(); + } + + @Override + public int hashCode() { + return Long.hashCode(term) ^ Long.hashCode(index); + } + + private String longToString(long n) { + return n >= 0L ? String.valueOf(n) : "~"; + } + + @Override + public String toString() { + return String.format("(t:%s, i:%s)", longToString(term), longToString(index)); + } + }; + } } } \ No newline at end of file diff --git a/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndexImpl.java b/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndexImpl.java deleted file mode 100644 index d516728527..0000000000 --- a/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndexImpl.java +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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.ratis.server.protocol; - -import org.apache.ratis.thirdparty.com.google.common.collect.MapMaker; - -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - -/** - * An implementation of {@link TermIndex}. - * Note that this is not a public API. - */ -class TermIndexImpl implements TermIndex { - private static final AtomicInteger OBJECT_COUNT = new AtomicInteger(); - - /** Weak value cache -- the same {@link TermIndex} will not be created more than once. */ - static final class Cache { - private Cache() {} - - private static ConcurrentMap newMap() { - return new MapMaker().weakValues().makeMap(); - } - - /** Map: term -> (index -> {@link TermIndex}). */ - private static final ConcurrentMap> MAP = new ConcurrentHashMap<>(); - - private static TermIndex getOrCreate(long term, long index) { - final ConcurrentMap indexMap = MAP.computeIfAbsent(term, k -> newMap()); - final TermIndex computed = indexMap.computeIfAbsent(index, i -> new TermIndexImpl(term, i)); - if ((OBJECT_COUNT.get() & 0xFFFF) == 0) { - cleanupEmptyInnerMaps(); // cleanup empty maps once in a while - } - return computed; - } - - static int indexCount(long term) { - final ConcurrentMap indexMap = MAP.get(term); - if (indexMap == null) { - return 0; - } - - // size() may return incorrect result; see Guava MapMaker javadoc - int n = 0; - for (Long ignored : indexMap.keySet()) { - n++; - } - return n; - } - - static void cleanupEmptyInnerMaps() { - // isEmpty() may return incorrect result; see Guava MapMaker javadoc - MAP.values().removeIf(e -> !e.entrySet().iterator().hasNext()); - } - - static int dump(Consumer out) { - out.accept("TermIndex Cache:\n"); - int emptyCount = 0; - for (Map.Entry> entry : MAP.entrySet()) { - final long term = entry.getKey(); - final ConcurrentMap indexMap = entry.getValue(); - final int count = indexCount(term); - if (count == 0) { - emptyCount++; - } - - out.accept(" term=" + term); - out.accept(indexMap.keySet()); - out.accept(" count=" + indexCount(term)); - out.accept(", size=" + indexMap.size()); - out.accept("\n"); - } - return emptyCount; - } - } - - static TermIndex valueOf(long term, long index) { - return Cache.getOrCreate(term, index); - } - - private final long term; - private final long index; - - TermIndexImpl(long term, long index) { - this.term = term; - this.index = index; - OBJECT_COUNT.incrementAndGet(); - } - - @Override - public long getTerm() { - return term; - } - - @Override - public long getIndex() { - return index; - } - - @Override - public boolean equals(Object obj) { - if (obj == this) { - return true; - } else if (!(obj instanceof TermIndexImpl)) { - return false; - } - - final TermIndexImpl that = (TermIndexImpl) obj; - return this.getTerm() == that.getTerm() - && this.getIndex() == that.getIndex(); - } - - @Override - public int hashCode() { - return Long.hashCode(term) ^ Long.hashCode(index); - } - - private String longToString(long n) { - return n >= 0L ? String.valueOf(n) : "~"; - } - - @Override - public String toString() { - return String.format("(t:%s, i:%s)", longToString(term), longToString(index)); - } -} \ No newline at end of file diff --git a/ratis-test/src/test/java/org/apache/ratis/server/protocol/ProtocolTestUtils.java b/ratis-test/src/test/java/org/apache/ratis/server/protocol/ProtocolTestUtils.java new file mode 100644 index 0000000000..dee3f224c4 --- /dev/null +++ b/ratis-test/src/test/java/org/apache/ratis/server/protocol/ProtocolTestUtils.java @@ -0,0 +1,26 @@ +/* + * 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.ratis.server.protocol; + +import org.apache.ratis.util.BiWeakValueCache; + +public interface ProtocolTestUtils { + static BiWeakValueCache getTermIndexCache() { + return TermIndex.Impl.getCache(); + } +} \ No newline at end of file diff --git a/ratis-test/src/test/java/org/apache/ratis/server/protocol/TestTermIndexImpl.java b/ratis-test/src/test/java/org/apache/ratis/util/TestTermIndex.java similarity index 86% rename from ratis-test/src/test/java/org/apache/ratis/server/protocol/TestTermIndexImpl.java rename to ratis-test/src/test/java/org/apache/ratis/util/TestTermIndex.java index 48cd00ebe4..678d7afe68 100644 --- a/ratis-test/src/test/java/org/apache/ratis/server/protocol/TestTermIndexImpl.java +++ b/ratis-test/src/test/java/org/apache/ratis/util/TestTermIndex.java @@ -15,19 +15,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.apache.ratis.server.protocol; +package org.apache.ratis.util; import org.apache.ratis.BaseTest; import org.apache.ratis.RaftTestUtil; -import org.apache.ratis.util.JavaUtils; +import org.apache.ratis.server.protocol.ProtocolTestUtils; +import org.apache.ratis.server.protocol.TermIndex; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; -/** Testing {@link TermIndexImpl}. */ -public class TestTermIndexImpl extends BaseTest { +/** Testing {@link BiWeakValueCache}. */ +public class TestTermIndex extends BaseTest { + static BiWeakValueCache CACHE = ProtocolTestUtils.getTermIndexCache(); + static void dumpCache(Integer expectedEmptyCount) { - final int computed = TermIndexImpl.Cache.dump(System.out::print); + final int computed = CACHE.dump(System.out::print); if (expectedEmptyCount != null) { assertEquals(expectedEmptyCount, computed); } @@ -35,7 +38,7 @@ static void dumpCache(Integer expectedEmptyCount) { } static void assertCacheSize(int expectedSize, long term) { - final int computed = TermIndexImpl.Cache.indexCount(term); + final int computed = CACHE.count(term); if (computed != expectedSize) { dumpCache(null); } @@ -91,7 +94,7 @@ public void testCaching() throws Exception { assertCacheSizeWithGC(0, terms[1]); dumpCache(1); - TermIndexImpl.Cache.cleanupEmptyInnerMaps(); + CACHE.cleanupEmptyInnerMaps(); dumpCache(0); } } From 2e62a3de22a48e78278dc2dfc11133fb35d84add Mon Sep 17 00:00:00 2001 From: Tsz-Wo Nicholas Sze Date: Tue, 18 Mar 2025 15:07:19 -0700 Subject: [PATCH 3/3] Fix license and checkstyle --- .../org/apache/ratis/util/BiWeakValueCache.java | 17 +++++++++++++++++ .../apache/ratis/server/protocol/TermIndex.java | 4 +++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java b/ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java index 85f6e6b0f0..c1aa6bcd5a 100644 --- a/ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java +++ b/ratis-common/src/main/java/org/apache/ratis/util/BiWeakValueCache.java @@ -1,3 +1,20 @@ +/* + * 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.ratis.util; import org.apache.ratis.thirdparty.com.google.common.collect.MapMaker; diff --git a/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java b/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java index 79d1d582c2..6115bccadf 100644 --- a/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java +++ b/ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java @@ -81,7 +81,9 @@ static TermIndex valueOf(long term, long index) { * An implementation for private use. * Note that this is not a public API, although this is public class. */ - class Impl { + final class Impl { + private Impl() { } + private static final BiWeakValueCache CACHE = new BiWeakValueCache<>("term", "index", Impl::newTermIndex);