Skip to content

Commit 27eb1c4

Browse files
committed
Revisit deletion policy after release the last snapshot (#28627)
We currently revisit the index deletion policy whenever the global checkpoint has advanced enough. We should also revisit the deletion policy after releasing the last snapshot of a snapshotting commit. With this change, the old index commits will be cleaned up as soon as possible. Follow-up of #28140 #28140 (comment)
1 parent 4ad361d commit 27eb1c4

File tree

4 files changed

+55
-9
lines changed

4 files changed

+55
-9
lines changed

server/src/main/java/org/elasticsearch/index/engine/CombinedDeletionPolicy.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,8 +168,10 @@ synchronized IndexCommit acquireIndexCommit(boolean acquiringSafeCommit) {
168168

169169
/**
170170
* Releases an index commit that acquired by {@link #acquireIndexCommit(boolean)}.
171+
*
172+
* @return true if the snapshotting commit can be clean up.
171173
*/
172-
synchronized void releaseCommit(final IndexCommit snapshotCommit) {
174+
synchronized boolean releaseCommit(final IndexCommit snapshotCommit) {
173175
final IndexCommit releasingCommit = ((SnapshotIndexCommit) snapshotCommit).delegate;
174176
assert snapshottedCommits.containsKey(releasingCommit) : "Release non-snapshotted commit;" +
175177
"snapshotted commits [" + snapshottedCommits + "], releasing commit [" + releasingCommit + "]";
@@ -178,6 +180,8 @@ synchronized void releaseCommit(final IndexCommit snapshotCommit) {
178180
if (refCount == 0) {
179181
snapshottedCommits.remove(releasingCommit);
180182
}
183+
// The commit can be clean up only if no pending snapshot and it is neither the safe commit nor last commit.
184+
return refCount == 0 && releasingCommit.equals(safeCommit) == false && releasingCommit.equals(lastCommit) == false;
181185
}
182186

183187
/**

server/src/main/java/org/elasticsearch/index/engine/InternalEngine.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1769,13 +1769,21 @@ public IndexCommitRef acquireLastIndexCommit(final boolean flushFirst) throws En
17691769
logger.trace("finish flush for snapshot");
17701770
}
17711771
final IndexCommit lastCommit = combinedDeletionPolicy.acquireIndexCommit(false);
1772-
return new Engine.IndexCommitRef(lastCommit, () -> combinedDeletionPolicy.releaseCommit(lastCommit));
1772+
return new Engine.IndexCommitRef(lastCommit, () -> releaseIndexCommit(lastCommit));
17731773
}
17741774

17751775
@Override
17761776
public IndexCommitRef acquireSafeIndexCommit() throws EngineException {
17771777
final IndexCommit safeCommit = combinedDeletionPolicy.acquireIndexCommit(true);
1778-
return new Engine.IndexCommitRef(safeCommit, () -> combinedDeletionPolicy.releaseCommit(safeCommit));
1778+
return new Engine.IndexCommitRef(safeCommit, () -> releaseIndexCommit(safeCommit));
1779+
}
1780+
1781+
private void releaseIndexCommit(IndexCommit snapshot) throws IOException {
1782+
// Revisit the deletion policy if we can clean up the snapshotting commit.
1783+
if (combinedDeletionPolicy.releaseCommit(snapshot)) {
1784+
ensureOpen();
1785+
indexWriter.deleteUnusedFiles();
1786+
}
17791787
}
17801788

17811789
private boolean failOnTragicEvent(AlreadyClosedException ex) {

server/src/test/java/org/elasticsearch/index/engine/CombinedDeletionPolicyTests.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,15 @@ public void testAcquireIndexCommit() throws Exception {
132132
assertThat(snapshot.getUserData(), equalTo(commitList.get(commitList.size() - 1).getUserData()));
133133
}
134134
}
135-
randomSubsetOf(snapshottingCommits).forEach(snapshot -> {
135+
final List<IndexCommit> releasingSnapshots = randomSubsetOf(snapshottingCommits);
136+
for (IndexCommit snapshot : releasingSnapshots) {
136137
snapshottingCommits.remove(snapshot);
137-
indexPolicy.releaseCommit(snapshot);
138-
});
138+
final long pendingSnapshots = snapshottingCommits.stream().filter(snapshot::equals).count();
139+
final IndexCommit lastCommit = commitList.get(commitList.size() - 1);
140+
final IndexCommit safeCommit = CombinedDeletionPolicy.findSafeCommitPoint(commitList, globalCheckpoint.get());
141+
assertThat(indexPolicy.releaseCommit(snapshot),
142+
equalTo(pendingSnapshots == 0 && snapshot.equals(lastCommit) == false && snapshot.equals(safeCommit) == false));
143+
}
139144
// Snapshotting commits must not be deleted.
140145
snapshottingCommits.forEach(snapshot -> assertThat(snapshot.isDeleted(), equalTo(false)));
141146
// We don't need to retain translog for snapshotting commits.

server/src/test/java/org/elasticsearch/index/engine/InternalEngineTests.java

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2097,9 +2097,9 @@ public void testSeqNoAndCheckpoints() throws IOException {
20972097
// this test writes documents to the engine while concurrently flushing/commit
20982098
// and ensuring that the commit points contain the correct sequence number data
20992099
public void testConcurrentWritesAndCommits() throws Exception {
2100-
List<Engine.IndexCommitRef> commits = new ArrayList<>();
21012100
try (Store store = createStore();
21022101
InternalEngine engine = new InternalEngine(config(defaultSettings, store, createTempDir(), newMergePolicy(), null))) {
2102+
final List<Engine.IndexCommitRef> commits = new ArrayList<>();
21032103

21042104
final int numIndexingThreads = scaledRandomIntBetween(2, 4);
21052105
final int numDocsPerThread = randomIntBetween(500, 1000);
@@ -2184,8 +2184,6 @@ public void testConcurrentWritesAndCommits() throws Exception {
21842184
prevLocalCheckpoint = localCheckpoint;
21852185
prevMaxSeqNo = maxSeqNo;
21862186
}
2187-
} finally {
2188-
IOUtils.close(commits);
21892187
}
21902188
}
21912189

@@ -4474,6 +4472,37 @@ public void testCleanUpCommitsWhenGlobalCheckpointAdvanced() throws Exception {
44744472
}
44754473
}
44764474

4475+
public void testCleanupCommitsWhenReleaseSnapshot() throws Exception {
4476+
IOUtils.close(engine, store);
4477+
store = createStore();
4478+
final AtomicLong globalCheckpoint = new AtomicLong(SequenceNumbers.UNASSIGNED_SEQ_NO);
4479+
try (InternalEngine engine = createEngine(store, createTempDir(), globalCheckpoint::get)) {
4480+
final int numDocs = scaledRandomIntBetween(10, 100);
4481+
for (int docId = 0; docId < numDocs; docId++) {
4482+
index(engine, docId);
4483+
if (frequently()) {
4484+
engine.flush(randomBoolean(), randomBoolean());
4485+
}
4486+
}
4487+
engine.flush(false, randomBoolean());
4488+
int numSnapshots = between(1, 10);
4489+
final List<Engine.IndexCommitRef> snapshots = new ArrayList<>();
4490+
for (int i = 0; i < numSnapshots; i++) {
4491+
snapshots.add(engine.acquireSafeIndexCommit()); // taking snapshots from the safe commit.
4492+
}
4493+
globalCheckpoint.set(engine.getLocalCheckpointTracker().getCheckpoint());
4494+
engine.syncTranslog();
4495+
final List<IndexCommit> commits = DirectoryReader.listCommits(store.directory());
4496+
for (int i = 0; i < numSnapshots - 1; i++) {
4497+
snapshots.get(i).close();
4498+
// pending snapshots - should not release any commit.
4499+
assertThat(DirectoryReader.listCommits(store.directory()), equalTo(commits));
4500+
}
4501+
snapshots.get(numSnapshots - 1).close(); // release the last snapshot - delete all except the last commit
4502+
assertThat(DirectoryReader.listCommits(store.directory()), hasSize(1));
4503+
}
4504+
}
4505+
44774506
public void testShouldPeriodicallyFlush() throws Exception {
44784507
assertThat("Empty engine does not need flushing", engine.shouldPeriodicallyFlush(), equalTo(false));
44794508
int numDocs = between(10, 100);

0 commit comments

Comments
 (0)