-
Notifications
You must be signed in to change notification settings - Fork 25.1k
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
Prune only gc deletes below the local checkpoint #28790
Changes from 10 commits
bfd2574
6bc190c
f38cbbb
f9c930d
cd3d5d2
a1efe07
8737b52
0c5f601
c2a62b6
2bc3785
d342e85
cc4bafe
6cbde7a
0bf6ffa
388a983
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 |
---|---|---|
|
@@ -77,6 +77,7 @@ | |
import org.elasticsearch.cluster.routing.TestShardRouting; | ||
import org.elasticsearch.common.Randomness; | ||
import org.elasticsearch.common.Strings; | ||
import org.elasticsearch.common.UUIDs; | ||
import org.elasticsearch.common.bytes.BytesArray; | ||
import org.elasticsearch.common.bytes.BytesReference; | ||
import org.elasticsearch.common.collect.Tuple; | ||
|
@@ -165,6 +166,8 @@ | |
import static org.hamcrest.CoreMatchers.instanceOf; | ||
import static org.hamcrest.CoreMatchers.sameInstance; | ||
import static org.hamcrest.Matchers.contains; | ||
import static org.hamcrest.Matchers.containsInAnyOrder; | ||
import static org.hamcrest.Matchers.empty; | ||
import static org.hamcrest.Matchers.equalTo; | ||
import static org.hamcrest.Matchers.everyItem; | ||
import static org.hamcrest.Matchers.greaterThan; | ||
|
@@ -175,6 +178,8 @@ | |
import static org.hamcrest.Matchers.not; | ||
import static org.hamcrest.Matchers.notNullValue; | ||
import static org.hamcrest.Matchers.nullValue; | ||
import static org.mockito.Mockito.spy; | ||
import static org.mockito.Mockito.when; | ||
|
||
public class InternalEngineTests extends EngineTestCase { | ||
|
||
|
@@ -4572,4 +4577,67 @@ public void testStressUpdateSameDocWhileGettingIt() throws IOException, Interrup | |
} | ||
} | ||
} | ||
|
||
public void testPruneOnlyDeletesAtMostLocalCheckpoint() throws Exception { | ||
IOUtils.close(engine, store); | ||
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. can we try not to mess up with the class's engine ? I rather have a local one enclosed in a try with resources 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. Done |
||
final AtomicLong clock = new AtomicLong(0); | ||
threadPool = spy(threadPool); | ||
when(threadPool.relativeTimeInMillis()).thenAnswer(invocation -> clock.get()); | ||
this.store = createStore(); | ||
final EngineConfig config = engine.config(); | ||
final EngineConfig newConfig = new EngineConfig( | ||
config.getOpenMode(), config.getShardId(), config.getAllocationId(), threadPool, config.getIndexSettings(), config.getWarmer(), | ||
store, config.getMergePolicy(), config.getAnalyzer(), config.getSimilarity(), new CodecService(null, logger), | ||
config.getEventListener(), config.getQueryCache(), config.getQueryCachingPolicy(), config.getForceNewHistoryUUID(), | ||
config.getTranslogConfig(), config.getFlushMergesAfter(), config.getExternalRefreshListener(), Collections.emptyList(), | ||
config.getIndexSort(), config.getTranslogRecoveryRunner(), config.getCircuitBreakerService(), | ||
config.getGlobalCheckpointSupplier()); | ||
newConfig.setEnableGcDeletes(false); | ||
// We can control the clock of this engine. | ||
this.engine = new InternalEngine(newConfig); | ||
final long gcInterval = randomIntBetween(0, 10); | ||
final IndexSettings indexSettings = engine.config().getIndexSettings(); | ||
final IndexMetaData indexMetaData = IndexMetaData.builder(indexSettings.getIndexMetaData()) | ||
.settings(Settings.builder().put(indexSettings.getSettings()) | ||
.put(IndexSettings.INDEX_GC_DELETES_SETTING.getKey(), TimeValue.timeValueMillis(gcInterval).getStringRep())).build(); | ||
indexSettings.updateIndexMetaData(indexMetaData); | ||
for (int i = 0, docs = scaledRandomIntBetween(0, 10); i < docs; i++) { | ||
index(this.engine, i); | ||
} | ||
final long deleteBatch = between(10, 20); | ||
final long gapSeqNo = randomLongBetween( | ||
engine.getLocalCheckpointTracker().getMaxSeqNo() + 1, engine.getLocalCheckpointTracker().getMaxSeqNo() + deleteBatch); | ||
for (int i = 0; i < deleteBatch; i++) { | ||
final long seqno = engine.getLocalCheckpointTracker().generateSeqNo(); | ||
if (seqno != gapSeqNo) { | ||
if (randomBoolean()) { | ||
clock.incrementAndGet(); | ||
} | ||
engine.delete(replicaDeleteForDoc(UUIDs.randomBase64UUID(), 1, seqno, threadPool.relativeTimeInMillis())); | ||
} | ||
} | ||
List<DeleteVersionValue> tombstones = new ArrayList<>(engine.getDeletedTombstones()); | ||
engine.config().setEnableGcDeletes(true); | ||
// Prune tombstones whose seqno < gap_seqno and timestamp < clock-gcInterval. | ||
clock.set(randomLongBetween(gcInterval, deleteBatch + gcInterval)); | ||
engine.refresh("test"); | ||
tombstones.removeIf(v -> v.seqNo < gapSeqNo && v.time < clock.get() - gcInterval); | ||
assertThat(engine.getDeletedTombstones(), containsInAnyOrder(tombstones.toArray())); | ||
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. don't we need to check the size too? 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. We don't have to as |
||
// Prune tombstones whose seqno at most the local checkpoint (eg. seqno < gap_seqno). | ||
clock.set(randomLongBetween(deleteBatch + gcInterval * 4/3, 100)); // Need a margin for gcInterval/4. | ||
engine.refresh("test"); | ||
tombstones.removeIf(v -> v.seqNo < gapSeqNo); | ||
assertThat(engine.getDeletedTombstones(), containsInAnyOrder(tombstones.toArray())); | ||
// Fill the seqno gap - should prune all tombstones. | ||
clock.set(between(0, 100)); | ||
if (randomBoolean()) { | ||
engine.index(replicaIndexForDoc(testParsedDocument("d", null, testDocumentWithTextField(), SOURCE, null), 1, gapSeqNo, false)); | ||
} else { | ||
engine.delete(replicaDeleteForDoc(UUIDs.randomBase64UUID(), Versions.MATCH_ANY, gapSeqNo, threadPool.relativeTimeInMillis())); | ||
} | ||
clock.set(randomLongBetween(100 + gcInterval * 4/3, Long.MAX_VALUE)); // Need a margin for gcInterval/4. | ||
engine.refresh("test"); | ||
assertThat(engine.getDeletedTombstones(), empty()); | ||
} | ||
|
||
} |
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.
Why not
<=
?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.
Because we would like to keep delete tombstones for at least one GC cycle.