19
19
20
20
package org .elasticsearch .index .seqno ;
21
21
22
+ import com .carrotsearch .hppc .LongObjectHashMap ;
22
23
import org .apache .lucene .util .FixedBitSet ;
23
24
import org .elasticsearch .common .SuppressForbidden ;
24
- import org .elasticsearch .common .settings .Setting ;
25
- import org .elasticsearch .index .IndexSettings ;
26
-
27
- import java .util .LinkedList ;
28
25
29
26
/**
30
27
* This class generates sequences numbers and keeps track of the so-called "local checkpoint" which is the highest number for which all
33
30
public class LocalCheckpointTracker {
34
31
35
32
/**
36
- * We keep a bit for each sequence number that is still pending. To optimize allocation, we do so in multiple arrays allocating them on
37
- * demand and cleaning up while completed. This constant controls the size of the arrays .
33
+ * We keep a bit for each sequence number that is still pending. To optimize allocation, we do so in multiple sets allocating them on
34
+ * demand and cleaning up while completed. This constant controls the size of the sets .
38
35
*/
39
- static final int BIT_ARRAYS_SIZE = 1024 ;
36
+ static final int BIT_SET_SIZE = 1024 ;
40
37
41
38
/**
42
- * An ordered list of bit arrays representing pending sequence numbers. The list is "anchored" in {@link #firstProcessedSeqNo} which
43
- * marks the sequence number the fist bit in the first array corresponds to .
39
+ * A collection of bit sets representing pending sequence numbers. Each sequence number is mapped to a bit set by dividing by the
40
+ * bit set size .
44
41
*/
45
- final LinkedList <FixedBitSet > processedSeqNo = new LinkedList <>();
46
-
47
- /**
48
- * The sequence number that the first bit in the first array corresponds to.
49
- */
50
- long firstProcessedSeqNo ;
42
+ final LongObjectHashMap <FixedBitSet > processedSeqNo = new LongObjectHashMap <>();
51
43
52
44
/**
53
45
* The current local checkpoint, i.e., all sequence numbers no more than this number have been completed.
@@ -77,7 +69,6 @@ public LocalCheckpointTracker(final long maxSeqNo, final long localCheckpoint) {
77
69
throw new IllegalArgumentException (
78
70
"max seq. no. must be non-negative or [" + SequenceNumbers .NO_OPS_PERFORMED + "] but was [" + maxSeqNo + "]" );
79
71
}
80
- firstProcessedSeqNo = localCheckpoint == SequenceNumbers .NO_OPS_PERFORMED ? 0 : localCheckpoint + 1 ;
81
72
nextSeqNo = maxSeqNo == SequenceNumbers .NO_OPS_PERFORMED ? 0 : maxSeqNo + 1 ;
82
73
checkpoint = localCheckpoint ;
83
74
}
@@ -122,7 +113,6 @@ synchronized void resetCheckpoint(final long checkpoint) {
122
113
assert checkpoint != SequenceNumbers .UNASSIGNED_SEQ_NO ;
123
114
assert checkpoint <= this .checkpoint ;
124
115
processedSeqNo .clear ();
125
- firstProcessedSeqNo = checkpoint + 1 ;
126
116
this .checkpoint = checkpoint ;
127
117
}
128
118
@@ -175,24 +165,28 @@ synchronized void waitForOpsToComplete(final long seqNo) throws InterruptedExcep
175
165
@ SuppressForbidden (reason = "Object#notifyAll" )
176
166
private void updateCheckpoint () {
177
167
assert Thread .holdsLock (this );
178
- assert checkpoint < firstProcessedSeqNo + BIT_ARRAYS_SIZE - 1 :
179
- "checkpoint should be below the end of the first bit set (o.w. current bit set is completed and shouldn't be there)" ;
180
- assert getBitSetForSeqNo (checkpoint + 1 ) == processedSeqNo .getFirst () :
181
- "checkpoint + 1 doesn't point to the first bit set (o.w. current bit set is completed and shouldn't be there)" ;
182
168
assert getBitSetForSeqNo (checkpoint + 1 ).get (seqNoToBitSetOffset (checkpoint + 1 )) :
183
169
"updateCheckpoint is called but the bit following the checkpoint is not set" ;
184
170
try {
185
171
// keep it simple for now, get the checkpoint one by one; in the future we can optimize and read words
186
- FixedBitSet current = processedSeqNo .getFirst ();
172
+ long bitSetKey = getBitSetKey (checkpoint );
173
+ FixedBitSet current = processedSeqNo .get (bitSetKey );
174
+ if (current == null ) {
175
+ // the bit set corresponding to the checkpoint has already been removed, set ourselves up for the next bit set
176
+ assert checkpoint % BIT_SET_SIZE == BIT_SET_SIZE - 1 ;
177
+ current = processedSeqNo .get (++bitSetKey );
178
+ }
187
179
do {
188
180
checkpoint ++;
189
- // the checkpoint always falls in the first bit set or just before. If it falls
190
- // on the last bit of the current bit set, we can clean it.
191
- if (checkpoint == firstProcessedSeqNo + BIT_ARRAYS_SIZE - 1 ) {
192
- processedSeqNo .removeFirst ();
193
- firstProcessedSeqNo += BIT_ARRAYS_SIZE ;
194
- assert checkpoint - firstProcessedSeqNo < BIT_ARRAYS_SIZE ;
195
- current = processedSeqNo .peekFirst ();
181
+ /*
182
+ * The checkpoint always falls in the current bit set or we have already cleaned it; if it falls on the last bit of the
183
+ * current bit set, we can clean it.
184
+ */
185
+ if (checkpoint == lastSeqNoInBitSet (bitSetKey )) {
186
+ assert current != null ;
187
+ final FixedBitSet removed = processedSeqNo .remove (bitSetKey );
188
+ assert removed == current ;
189
+ current = processedSeqNo .get (++bitSetKey );
196
190
}
197
191
} while (current != null && current .get (seqNoToBitSetOffset (checkpoint + 1 )));
198
192
} finally {
@@ -201,37 +195,45 @@ assert getBitSetForSeqNo(checkpoint + 1).get(seqNoToBitSetOffset(checkpoint + 1)
201
195
}
202
196
}
203
197
198
+ private long lastSeqNoInBitSet (final long bitSetKey ) {
199
+ return (1 + bitSetKey ) * BIT_SET_SIZE - 1 ;
200
+ }
201
+
204
202
/**
205
- * Return the bit array for the provided sequence number, possibly allocating a new array if needed.
203
+ * Return the bit set for the provided sequence number, possibly allocating a new set if needed.
206
204
*
207
- * @param seqNo the sequence number to obtain the bit array for
208
- * @return the bit array corresponding to the provided sequence number
205
+ * @param seqNo the sequence number to obtain the bit set for
206
+ * @return the bit set corresponding to the provided sequence number
209
207
*/
208
+ private long getBitSetKey (final long seqNo ) {
209
+ assert Thread .holdsLock (this );
210
+ return seqNo / BIT_SET_SIZE ;
211
+ }
212
+
210
213
private FixedBitSet getBitSetForSeqNo (final long seqNo ) {
211
214
assert Thread .holdsLock (this );
212
- assert seqNo >= firstProcessedSeqNo : " seqNo: " + seqNo + " firstProcessedSeqNo: " + firstProcessedSeqNo ;
213
- final long bitSetOffset = ( seqNo - firstProcessedSeqNo ) / BIT_ARRAYS_SIZE ;
214
- if ( bitSetOffset > Integer . MAX_VALUE ) {
215
- throw new IndexOutOfBoundsException (
216
- "sequence number too high; got [" + seqNo + "], firstProcessedSeqNo [" + firstProcessedSeqNo + "]" );
217
- }
218
- while ( bitSetOffset >= processedSeqNo . size ()) {
219
- processedSeqNo .add ( new FixedBitSet ( BIT_ARRAYS_SIZE ) );
215
+ final long bitSetKey = getBitSetKey ( seqNo ) ;
216
+ final int index = processedSeqNo . indexOf ( bitSetKey ) ;
217
+ final FixedBitSet bitSet ;
218
+ if ( processedSeqNo . indexExists ( index )) {
219
+ bitSet = processedSeqNo . indexGet ( index );
220
+ } else {
221
+ bitSet = new FixedBitSet ( BIT_SET_SIZE );
222
+ processedSeqNo .indexInsert ( index , bitSetKey , bitSet );
220
223
}
221
- return processedSeqNo . get (( int ) bitSetOffset ) ;
224
+ return bitSet ;
222
225
}
223
226
224
227
/**
225
- * Obtain the position in the bit array corresponding to the provided sequence number. The bit array corresponding to the sequence
226
- * number can be obtained via {@link #getBitSetForSeqNo(long)}.
228
+ * Obtain the position in the bit set corresponding to the provided sequence number. The bit set corresponding to the sequence number
229
+ * can be obtained via {@link #getBitSetForSeqNo(long)}.
227
230
*
228
231
* @param seqNo the sequence number to obtain the position for
229
- * @return the position in the bit array corresponding to the provided sequence number
232
+ * @return the position in the bit set corresponding to the provided sequence number
230
233
*/
231
234
private int seqNoToBitSetOffset (final long seqNo ) {
232
235
assert Thread .holdsLock (this );
233
- assert seqNo >= firstProcessedSeqNo ;
234
- return ((int ) (seqNo - firstProcessedSeqNo )) % BIT_ARRAYS_SIZE ;
236
+ return Math .toIntExact (seqNo % BIT_SET_SIZE );
235
237
}
236
238
237
239
}
0 commit comments