public void testOutOfOrderSequenceNumbersWithVersionConflict() throws IOException {
final List operations = new ArrayList<>();
final int numberOfOperations = randomIntBetween(16, 32);
final Document document = testDocumentWithTextField();
final AtomicLong sequenceNumber = new AtomicLong();
final Engine.Operation.Origin origin = randomFrom(LOCAL_TRANSLOG_RECOVERY, PEER_RECOVERY, PRIMARY, REPLICA);
final LongSupplier sequenceNumberSupplier =
origin == PRIMARY ? () -> SequenceNumbersService.UNASSIGNED_SEQ_NO : sequenceNumber::getAndIncrement;
document.add(new Field(SourceFieldMapper.NAME, BytesReference.toBytes(B_1), SourceFieldMapper.Defaults.FIELD_TYPE));
final ParsedDocument doc = testParsedDocument("1", "test", null, document, B_1, null);
final Term uid = newUid(doc);
for (int i = 0; i < numberOfOperations; i++) {
if (randomBoolean()) {
final Engine.Index index = new Engine.Index(
uid,
doc,
sequenceNumberSupplier.getAsLong(),
1,
i,
VersionType.EXTERNAL,
origin,
System.nanoTime(),
IndexRequest.UNSET_AUTO_GENERATED_TIMESTAMP,
false);
operations.add(index);
} else {
final Engine.Delete delete = new Engine.Delete(
"test",
"1",
uid,
sequenceNumberSupplier.getAsLong(),
1,
i,
VersionType.EXTERNAL,
origin,
System.nanoTime());
operations.add(delete);
}
}
final boolean exists = operations.get(operations.size() - 1) instanceof Engine.Index;
Randomness.shuffle(operations);
for (final Engine.Operation operation : operations) {
if (operation instanceof Engine.Index) {
engine.index((Engine.Index) operation);
} else {
engine.delete((Engine.Delete) operation);
}
}
final long expectedLocalCheckpoint;
if (origin == PRIMARY) {
// we can only advance as far as the number of operations that did not conflict
int count = 0;
// each time the version increments as we walk the list, that counts as a successful operation
long version = -1;
for (int i = 0; i < numberOfOperations; i++) {
if (operations.get(i).version() >= version) {
count++;
version = operations.get(i).version();
}
}
// sequence numbers start at zero, so the expected local checkpoint is the number of successful operations minus one
expectedLocalCheckpoint = count - 1;
} else {
expectedLocalCheckpoint = numberOfOperations - 1;
}
assertThat(engine.seqNoService().getLocalCheckpoint(), equalTo(expectedLocalCheckpoint));
try (Engine.GetResult result = engine.get(new Engine.Get(true, uid))) {
assertThat(result.exists(), equalTo(exists));
}
}