Skip to content

Commit 0b96f39

Browse files
committed
Simplify MultiSnapshot#SeqNoSet
Today, we maintain two sets in a SeqNoSet: ongoing sets and completed sets. We can remove the completed sets by releasing the internal bitset of a CountedBitSet when all its bits are set. Relates #27268
1 parent a4b4e14 commit 0b96f39

File tree

1 file changed

+31
-28
lines changed

1 file changed

+31
-28
lines changed

core/src/main/java/org/elasticsearch/index/translog/MultiSnapshot.java

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,8 @@
1919

2020
package org.elasticsearch.index.translog;
2121

22-
import com.carrotsearch.hppc.LongHashSet;
2322
import com.carrotsearch.hppc.LongObjectHashMap;
24-
import com.carrotsearch.hppc.LongSet;
23+
import com.carrotsearch.hppc.cursors.ObjectCursor;
2524
import org.apache.lucene.util.FixedBitSet;
2625
import org.elasticsearch.index.seqno.SequenceNumbers;
2726

@@ -85,11 +84,13 @@ public void close() throws IOException {
8584
}
8685

8786
/**
88-
* A wrapper of {@link FixedBitSet} but allows to check if all bits are set in O(1).
87+
* A {@link CountedBitSet} wraps a {@link FixedBitSet} but automatically releases the internal bitset
88+
* when all bits are set to reduce memory usage. This structure can work well for sequence numbers
89+
* from translog as these numbers are likely to form contiguous ranges (eg. filling all bits).
8990
*/
9091
private static final class CountedBitSet {
91-
private short onBits;
92-
private final FixedBitSet bitset;
92+
private short onBits; // Number of bits are set.
93+
private FixedBitSet bitset;
9394

9495
CountedBitSet(short numBits) {
9596
assert numBits > 0;
@@ -99,60 +100,62 @@ private static final class CountedBitSet {
99100

100101
boolean getAndSet(int index) {
101102
assert index >= 0;
103+
assert bitset == null || onBits < bitset.length() : "Bitset should be cleared when all bits are set";
104+
105+
// A null bitset means all bits are set.
106+
if (bitset == null) {
107+
return true;
108+
}
109+
102110
boolean wasOn = bitset.getAndSet(index);
103111
if (wasOn == false) {
104112
onBits++;
113+
// Once all bits are set, we can simply just return YES for all indexes.
114+
// This allows us to clear the internal bitset and use null check as the guard.
115+
if (onBits == bitset.length()) {
116+
bitset = null;
117+
}
105118
}
106119
return wasOn;
107120
}
108121

109122
boolean hasAllBitsOn() {
110-
return onBits == bitset.length();
123+
return bitset == null;
111124
}
112125
}
113126

114-
/**
115-
* Sequence numbers from translog are likely to form contiguous ranges,
116-
* thus collapsing a completed bitset into a single entry will reduce memory usage.
117-
*/
118127
static final class SeqNoSet {
119128
static final short BIT_SET_SIZE = 1024;
120-
private final LongSet completedSets = new LongHashSet();
121-
private final LongObjectHashMap<CountedBitSet> ongoingSets = new LongObjectHashMap<>();
129+
private final LongObjectHashMap<CountedBitSet> bitSets = new LongObjectHashMap<>();
122130

123131
/**
124132
* Marks this sequence number and returns <tt>true</tt> if it is seen before.
125133
*/
126134
boolean getAndSet(long value) {
127135
assert value >= 0;
128136
final long key = value / BIT_SET_SIZE;
129-
130-
if (completedSets.contains(key)) {
131-
return true;
132-
}
133-
134-
CountedBitSet bitset = ongoingSets.get(key);
137+
CountedBitSet bitset = bitSets.get(key);
135138
if (bitset == null) {
136139
bitset = new CountedBitSet(BIT_SET_SIZE);
137-
ongoingSets.put(key, bitset);
138-
}
139-
140-
final boolean wasOn = bitset.getAndSet(Math.toIntExact(value % BIT_SET_SIZE));
141-
if (bitset.hasAllBitsOn()) {
142-
ongoingSets.remove(key);
143-
completedSets.add(key);
140+
bitSets.put(key, bitset);
144141
}
145-
return wasOn;
142+
return bitset.getAndSet(Math.toIntExact(value % BIT_SET_SIZE));
146143
}
147144

148145
// For testing
149146
long completeSetsSize() {
150-
return completedSets.size();
147+
int completedBitSets = 0;
148+
for (ObjectCursor<CountedBitSet> bitset : bitSets.values()) {
149+
if (bitset.value.hasAllBitsOn()) {
150+
completedBitSets++;
151+
}
152+
}
153+
return completedBitSets;
151154
}
152155

153156
// For testing
154157
long ongoingSetsSize() {
155-
return ongoingSets.size();
158+
return bitSets.size() - completeSetsSize();
156159
}
157160
}
158161
}

0 commit comments

Comments
 (0)