Skip to content

Commit 907a9f8

Browse files
committed
Address review comment and code refactoring.
1 parent 02d5ba9 commit 907a9f8

File tree

5 files changed

+219
-152
lines changed

5 files changed

+219
-152
lines changed
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package org.apache.ratis.util;
2+
3+
import org.apache.ratis.thirdparty.com.google.common.collect.MapMaker;
4+
5+
import java.util.Map;
6+
import java.util.Objects;
7+
import java.util.concurrent.ConcurrentHashMap;
8+
import java.util.concurrent.ConcurrentMap;
9+
import java.util.concurrent.atomic.AtomicInteger;
10+
import java.util.function.BiFunction;
11+
import java.util.function.Consumer;
12+
13+
/**
14+
* Weak Value Cache: ({@link OUTER}, {@link INNER}) -> {@link T}.
15+
* <p>
16+
* Note that the cached values are weakly referenced.
17+
* A cached value could be garage-collected (i.e. evicted from the cache)
18+
* when there are no external (strong) references.
19+
*
20+
* @param <OUTER> the type of the outer keys.
21+
* @param <INNER> the type of the inner keys.
22+
* @param <T> the type to be cached.
23+
*/
24+
public final class BiWeakValueCache<OUTER, INNER, T> {
25+
private static <K, V> ConcurrentMap<K, V> newMap() {
26+
return new MapMaker().weakValues().makeMap();
27+
}
28+
29+
private final String outerName;
30+
private final String innerName;
31+
private final String name;
32+
33+
/** For constructing {@link T} values from ({@link OUTER}, {@link INNER}) keys. */
34+
private final BiFunction<OUTER, INNER, T> constructor;
35+
/** Count the number of {@link T} values constructed. */
36+
private final AtomicInteger valueCount = new AtomicInteger(0);
37+
38+
/**
39+
* Actual map {@link OUTER} -> ({@link INNER} -> {@link T})
40+
* for the logical view ({@link OUTER}, {@link INNER}) -> {@link T}.
41+
*/
42+
private final ConcurrentMap<OUTER, ConcurrentMap<INNER, T>> map = new ConcurrentHashMap<>();
43+
44+
/**
45+
* Create a cache for mapping ({@link OUTER}, {@link INNER}) keys to {@link T} values.
46+
*
47+
* @param outerName the name of the outer long.
48+
* @param innerName the name of the inner long.
49+
* @param constructor for constructing {@link T} values.
50+
*/
51+
public BiWeakValueCache(String outerName, String innerName, BiFunction<OUTER, INNER, T> constructor) {
52+
this.outerName = outerName;
53+
this.innerName = innerName;
54+
this.name = "(" + outerName + ", " + innerName + ")-cache";
55+
this.constructor = constructor;
56+
}
57+
58+
private T construct(OUTER outer, INNER inner) {
59+
final T constructed = constructor.apply(outer, inner);
60+
Objects.requireNonNull(constructed, "constructed == null");
61+
valueCount.incrementAndGet();
62+
return constructed;
63+
}
64+
65+
/**
66+
* If the key ({@link OUTER}, {@link INNER}) is in the cache, return the cached values.
67+
* Otherwise, create a new value and then return it.
68+
*/
69+
public T getOrCreate(OUTER outer, INNER inner) {
70+
Objects.requireNonNull(outer, () -> outerName + " (outer) == null");
71+
Objects.requireNonNull(inner, () -> innerName + " (inner) == null");
72+
final ConcurrentMap<INNER, T> innerMap = map.computeIfAbsent(outer, k -> newMap());
73+
final T computed = innerMap.computeIfAbsent(inner, i -> construct(outer, i));
74+
if ((valueCount.get() & 0xFFF) == 0) {
75+
cleanupEmptyInnerMaps(); // cleanup empty maps once in a while
76+
}
77+
return computed;
78+
}
79+
80+
/** @return the value count for the given outer key. */
81+
int count(OUTER outer) {
82+
final ConcurrentMap<INNER, T> innerMap = map.get(outer);
83+
if (innerMap == null) {
84+
return 0;
85+
}
86+
87+
// size() may return incorrect result; see Guava MapMaker javadoc
88+
int n = 0;
89+
for (INNER ignored : innerMap.keySet()) {
90+
n++;
91+
}
92+
return n;
93+
}
94+
95+
void cleanupEmptyInnerMaps() {
96+
// isEmpty() may return incorrect result; see Guava MapMaker javadoc
97+
map.values().removeIf(e -> !e.entrySet().iterator().hasNext());
98+
}
99+
100+
@Override
101+
public String toString() {
102+
return name;
103+
}
104+
105+
/** The cache content for debugging. */
106+
int dump(Consumer<String> out) {
107+
out.accept(name + ":\n");
108+
int emptyCount = 0;
109+
for (Map.Entry<OUTER, ConcurrentMap<INNER, T>> entry : map.entrySet()) {
110+
final OUTER outer = entry.getKey();
111+
final ConcurrentMap<INNER, T> innerMap = entry.getValue();
112+
final int count = count(outer);
113+
if (count == 0) {
114+
emptyCount++;
115+
}
116+
117+
out.accept(" " + outerName + ":" + outer);
118+
out.accept(", " + innerName + ":" + innerMap.keySet());
119+
out.accept(", count=" + count);
120+
out.accept(", size=" + innerMap.size());
121+
out.accept("\n");
122+
}
123+
out.accept(" emptyCount=" + emptyCount);
124+
out.accept("\n");
125+
return emptyCount;
126+
}
127+
}

ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndex.java

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.apache.ratis.proto.RaftProtos.LogEntryProto;
2121
import org.apache.ratis.proto.RaftProtos.TermIndexProto;
2222
import org.apache.ratis.server.raftlog.RaftLog;
23+
import org.apache.ratis.util.BiWeakValueCache;
2324

2425
import java.util.Comparator;
2526
import java.util.Optional;
@@ -73,6 +74,60 @@ static TermIndex valueOf(LogEntryProto proto) {
7374

7475
/** @return a {@link TermIndex} object. */
7576
static TermIndex valueOf(long term, long index) {
76-
return TermIndexImpl.valueOf(term, index);
77+
return Impl.getCache().getOrCreate(term, index);
78+
}
79+
80+
/**
81+
* An implementation for private use.
82+
* Note that this is not a public API, although this is public class.
83+
*/
84+
class Impl {
85+
private static final BiWeakValueCache<Long, Long, TermIndex> CACHE
86+
= new BiWeakValueCache<>("term", "index", Impl::newTermIndex);
87+
88+
static BiWeakValueCache<Long, Long, TermIndex> getCache() {
89+
return CACHE;
90+
}
91+
92+
private static TermIndex newTermIndex(long term, long index) {
93+
return new TermIndex() {
94+
@Override
95+
public long getTerm() {
96+
return term;
97+
}
98+
99+
@Override
100+
public long getIndex() {
101+
return index;
102+
}
103+
104+
@Override
105+
public boolean equals(Object obj) {
106+
if (obj == this) {
107+
return true;
108+
} else if (!(obj instanceof TermIndex)) {
109+
return false;
110+
}
111+
112+
final TermIndex that = (TermIndex) obj;
113+
return this.getTerm() == that.getTerm()
114+
&& this.getIndex() == that.getIndex();
115+
}
116+
117+
@Override
118+
public int hashCode() {
119+
return Long.hashCode(term) ^ Long.hashCode(index);
120+
}
121+
122+
private String longToString(long n) {
123+
return n >= 0L ? String.valueOf(n) : "~";
124+
}
125+
126+
@Override
127+
public String toString() {
128+
return String.format("(t:%s, i:%s)", longToString(term), longToString(index));
129+
}
130+
};
131+
}
77132
}
78133
}

ratis-server-api/src/main/java/org/apache/ratis/server/protocol/TermIndexImpl.java

Lines changed: 0 additions & 144 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
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, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.ratis.server.protocol;
19+
20+
import org.apache.ratis.util.BiWeakValueCache;
21+
22+
public interface ProtocolTestUtils {
23+
static BiWeakValueCache<Long, Long, TermIndex> getTermIndexCache() {
24+
return TermIndex.Impl.getCache();
25+
}
26+
}

0 commit comments

Comments
 (0)