-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Persist sequence number checkpoints #18949
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
9799d71
689618b
f61b571
c851db6
a9d5f5e
a5b99c6
6e8f11a
5032fa7
a40112f
3eb20ef
331fceb
f223f19
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -22,7 +22,13 @@ | |
| import org.apache.lucene.document.Field; | ||
| import org.apache.lucene.document.NumericDocValuesField; | ||
| import org.apache.lucene.index.DocValuesType; | ||
| import org.apache.lucene.index.IndexReader; | ||
| import org.apache.lucene.index.LeafReader; | ||
| import org.apache.lucene.index.LeafReaderContext; | ||
| import org.apache.lucene.index.NumericDocValues; | ||
| import org.apache.lucene.search.Query; | ||
| import org.apache.lucene.util.Bits; | ||
| import org.elasticsearch.action.fieldstats.FieldStats; | ||
| import org.elasticsearch.common.Nullable; | ||
| import org.elasticsearch.common.settings.Settings; | ||
| import org.elasticsearch.common.xcontent.XContentBuilder; | ||
|
|
@@ -108,6 +114,36 @@ public Query termQuery(Object value, @Nullable QueryShardContext context) { | |
| throw new QueryShardException(context, "SeqNoField field [" + name() + "] is not searchable"); | ||
| } | ||
|
|
||
| @Override | ||
| public FieldStats stats(IndexReader reader) throws IOException { | ||
| // nocommit remove implementation when late-binding commits | ||
| // are possible | ||
| final List<LeafReaderContext> leaves = reader.leaves(); | ||
| if (leaves.isEmpty()) { | ||
| return null; | ||
| } | ||
|
|
||
| long currentMin = Long.MAX_VALUE; | ||
| long currentMax = Long.MIN_VALUE; | ||
| boolean found = false; | ||
| for (int i = 0; i < leaves.size(); i++) { | ||
| final LeafReader leaf = leaves.get(i).reader(); | ||
| final NumericDocValues values = leaf.getNumericDocValues(name()); | ||
| if (values == null) continue; | ||
| final Bits bits = leaf.getLiveDocs(); | ||
| for (int docID = 0; docID < leaf.maxDoc(); docID++) { | ||
| if (bits == null || bits.get(docID)) { | ||
| found = true; | ||
| final long value = values.get(docID); | ||
| currentMin = Math.min(currentMin, value); | ||
| currentMax = Math.max(currentMax, value); | ||
| } | ||
| } | ||
|
||
| } | ||
|
|
||
| return found ? new FieldStats.Long(reader.maxDoc(), 0, -1, -1, false, true, currentMin, currentMax) : null; | ||
| } | ||
|
|
||
| } | ||
|
|
||
| public SeqNoFieldMapper(Settings indexSettings) { | ||
|
|
@@ -129,7 +165,7 @@ protected void parseCreateField(ParseContext context, List<Field> fields) throws | |
|
|
||
| @Override | ||
| public Mapper parse(ParseContext context) throws IOException { | ||
| // _seqno added in preparse | ||
| // _seq_no added in pre-parse | ||
| return null; | ||
| } | ||
|
|
||
|
|
@@ -157,4 +193,5 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws | |
| protected void doMerge(Mapper mergeWith, boolean updateAllTypes) { | ||
| // nothing to do | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ | |
| import org.elasticsearch.index.IndexSettings; | ||
| import org.elasticsearch.index.shard.AbstractIndexShardComponent; | ||
| import org.elasticsearch.index.shard.ShardId; | ||
| import org.elasticsearch.index.shard.SnapshotStatus; | ||
|
|
||
| import java.util.LinkedList; | ||
|
|
||
|
|
@@ -39,39 +40,64 @@ public class LocalCheckpointService extends AbstractIndexShardComponent { | |
| public static Setting<Integer> SETTINGS_BIT_ARRAYS_SIZE = Setting.intSetting("index.seq_no.checkpoint.bit_arrays_size", 1024, | ||
| 4, Setting.Property.IndexScope); | ||
|
|
||
|
|
||
| /** | ||
| * an ordered list of bit arrays representing pending seq nos. The list is "anchored" in {@link #firstProcessedSeqNo} | ||
| * which marks the seqNo the fist bit in the first array corresponds to. | ||
| */ | ||
| final LinkedList<FixedBitSet> processedSeqNo; | ||
| final int bitArraysSize; | ||
| long firstProcessedSeqNo = 0; | ||
| private final int bitArraysSize; | ||
| long firstProcessedSeqNo; | ||
|
|
||
| /** the current local checkpoint, i.e., all seqNo lower (<=) than this number have been completed */ | ||
| volatile long checkpoint = SequenceNumbersService.NO_OPS_PERFORMED; | ||
| volatile long checkpoint; | ||
|
|
||
| /** the next available seqNo - used for seqNo generation */ | ||
| volatile long nextSeqNo = 0; | ||
|
|
||
| private volatile long nextSeqNo; | ||
|
|
||
| public LocalCheckpointService(ShardId shardId, IndexSettings indexSettings) { | ||
| /** | ||
| * Initialize the local checkpoint service. The {@code maxSeqNo} should be | ||
| * set to the last sequence number assigned by this shard, or | ||
| * {@link SequenceNumbersService#NO_OPS_PERFORMED} and | ||
| * {@code localCheckpoint} should be set to the last known local checkpoint | ||
| * for this shard, or {@link SequenceNumbersService#NO_OPS_PERFORMED}. | ||
| * | ||
| * @param shardId the shard this service is providing tracking | ||
| * local checkpoints for | ||
| * @param indexSettings the index settings | ||
| * @param maxSeqNo the last sequence number assigned by this shard, or | ||
| * {@link SequenceNumbersService#NO_OPS_PERFORMED} | ||
| * @param localCheckpoint the last known local checkpoint for this shard, or | ||
| * {@link SequenceNumbersService#NO_OPS_PERFORMED} | ||
| */ | ||
| LocalCheckpointService(final ShardId shardId, final IndexSettings indexSettings, final long maxSeqNo, final long localCheckpoint) { | ||
| super(shardId, indexSettings); | ||
| if (localCheckpoint < 0 && localCheckpoint != SequenceNumbersService.NO_OPS_PERFORMED) { | ||
| throw new IllegalArgumentException( | ||
| "local checkpoint must be non-negative or [" + SequenceNumbersService.NO_OPS_PERFORMED + "] " | ||
| + "but was [" + localCheckpoint + "]"); | ||
| } | ||
| if (maxSeqNo < 0 && maxSeqNo != SequenceNumbersService.NO_OPS_PERFORMED) { | ||
| throw new IllegalArgumentException( | ||
| "max seq. no. must be non-negative or [" + SequenceNumbersService.NO_OPS_PERFORMED + "] but was [" + maxSeqNo + "]"); | ||
| } | ||
| bitArraysSize = SETTINGS_BIT_ARRAYS_SIZE.get(indexSettings.getSettings()); | ||
| processedSeqNo = new LinkedList<>(); | ||
| firstProcessedSeqNo = localCheckpoint == SequenceNumbersService.NO_OPS_PERFORMED ? 0 : localCheckpoint + 1; | ||
| this.nextSeqNo = maxSeqNo == SequenceNumbersService.NO_OPS_PERFORMED ? 0 : maxSeqNo + 1; | ||
| this.checkpoint = localCheckpoint; | ||
| } | ||
|
|
||
| /** | ||
| * issue the next sequence number | ||
| **/ | ||
| public synchronized long generateSeqNo() { | ||
| synchronized long generateSeqNo() { | ||
| return nextSeqNo++; | ||
| } | ||
|
|
||
| /** | ||
| * marks the processing of the given seqNo have been completed | ||
| **/ | ||
| public synchronized void markSeqNoAsCompleted(long seqNo) { | ||
| synchronized void markSeqNoAsCompleted(long seqNo) { | ||
| // make sure we track highest seen seqNo | ||
| if (seqNo >= nextSeqNo) { | ||
| nextSeqNo = seqNo + 1; | ||
|
|
@@ -94,7 +120,7 @@ public long getCheckpoint() { | |
| } | ||
|
|
||
| /** gets the maximum seqno seen so far */ | ||
| public long getMaxSeqNo() { | ||
| long getMaxSeqNo() { | ||
| return nextSeqNo - 1; | ||
| } | ||
|
|
||
|
|
@@ -130,19 +156,19 @@ assert getBitSetForSeqNo(checkpoint + 1).get(seqNoToBitSetOffset(checkpoint + 1) | |
| */ | ||
| private FixedBitSet getBitSetForSeqNo(long seqNo) { | ||
| assert Thread.holdsLock(this); | ||
| assert seqNo >= firstProcessedSeqNo; | ||
| assert seqNo >= firstProcessedSeqNo : "seqNo: " + seqNo + " firstProcessedSeqNo: " + firstProcessedSeqNo; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the traces of a happy debugging session :)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Guilty as charged. 😄 |
||
| int bitSetOffset = ((int) (seqNo - firstProcessedSeqNo)) / bitArraysSize; | ||
| while (bitSetOffset >= processedSeqNo.size()) { | ||
| processedSeqNo.add(new FixedBitSet(bitArraysSize)); | ||
| } | ||
| return processedSeqNo.get(bitSetOffset); | ||
| } | ||
|
|
||
|
|
||
| /** maps the given seqNo to a position in the bit set returned by {@link #getBitSetForSeqNo} */ | ||
| private int seqNoToBitSetOffset(long seqNo) { | ||
| assert Thread.holdsLock(this); | ||
| assert seqNo >= firstProcessedSeqNo; | ||
| return ((int) (seqNo - firstProcessedSeqNo)) % bitArraysSize; | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
can we add a nocommit on this implementation, so it will be removed?
PS. Although we do this temporarily to achieve other goals, we will end up adding a different stats implementation once seq no is properly indexed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I pushed 689618b.