Skip to content

Commit 8c088d5

Browse files
authored
[test] Track index commits internally acquired by the commits listener in CombinedDeletionPolicy (#112507)
After finishing an integration test, we run some checks against the test cluster, among others we assert there are no leaky acquired index commits left in `InternalTestCluster#beforeIndexDeletion`. The issue is that while we check the test cluster before we shut it down, we can't guarantee that there wouldn't be a new commit triggered by a background merge which will acquire an index commit. But we actually don't care about these commits acquired internally as part of `CombinedDeletionPolicy#commitsListener` callbacks. We just want to make sure that all index commits that have been acquired explicitly are also released. So, we make an explicit distinction between external and internal index commits that are tracked in `CombinedDeletionPolicy`. ES-8407 <!-- Please don't remove the machine data below, even if you remove the linking label --> <!--es-delivery-machine-data {"linkedServerlessPr":2694,"linkedStatefulPr":112507} es-delivery-machine-data-->
1 parent 9081a95 commit 8c088d5

File tree

5 files changed

+42
-15
lines changed

5 files changed

+42
-15
lines changed

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

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ public class CombinedDeletionPolicy extends IndexDeletionPolicy {
4343
private final SoftDeletesPolicy softDeletesPolicy;
4444
private final LongSupplier globalCheckpointSupplier;
4545
private final Map<IndexCommit, Integer> acquiredIndexCommits; // Number of references held against each commit point.
46+
// Index commits internally acquired by the commits listener. We want to track them separately to be able to disregard them
47+
// when checking for externally acquired index commits that haven't been released
48+
private final Set<IndexCommit> internallyAcquiredIndexCommits;
4649

4750
interface CommitsListener {
4851

@@ -72,6 +75,7 @@ interface CommitsListener {
7275
this.globalCheckpointSupplier = globalCheckpointSupplier;
7376
this.commitsListener = commitsListener;
7477
this.acquiredIndexCommits = new HashMap<>();
78+
this.internallyAcquiredIndexCommits = new HashSet<>();
7579
}
7680

7781
@Override
@@ -114,7 +118,7 @@ public void onCommit(List<? extends IndexCommit> commits) throws IOException {
114118
this.maxSeqNoOfNextSafeCommit = Long.parseLong(commits.get(keptPosition + 1).getUserData().get(SequenceNumbers.MAX_SEQ_NO));
115119
}
116120
if (commitsListener != null && previousLastCommit != this.lastCommit) {
117-
newCommit = acquireIndexCommit(false);
121+
newCommit = acquireIndexCommit(false, true);
118122
} else {
119123
newCommit = null;
120124
}
@@ -210,15 +214,25 @@ SafeCommitInfo getSafeCommitInfo() {
210214
* @param acquiringSafeCommit captures the most recent safe commit point if true; otherwise captures the most recent commit point.
211215
*/
212216
synchronized IndexCommit acquireIndexCommit(boolean acquiringSafeCommit) {
217+
return acquireIndexCommit(acquiringSafeCommit, false);
218+
}
219+
220+
private synchronized IndexCommit acquireIndexCommit(boolean acquiringSafeCommit, boolean acquiredInternally) {
213221
assert safeCommit != null : "Safe commit is not initialized yet";
214222
assert lastCommit != null : "Last commit is not initialized yet";
215223
final IndexCommit snapshotting = acquiringSafeCommit ? safeCommit : lastCommit;
216224
acquiredIndexCommits.merge(snapshotting, 1, Integer::sum); // increase refCount
217-
return wrapCommit(snapshotting);
225+
assert acquiredInternally == false || internallyAcquiredIndexCommits.add(snapshotting)
226+
: "commit [" + snapshotting + "] already added";
227+
return wrapCommit(snapshotting, acquiredInternally);
218228
}
219229

220230
protected IndexCommit wrapCommit(IndexCommit indexCommit) {
221-
return new SnapshotIndexCommit(indexCommit);
231+
return wrapCommit(indexCommit, false);
232+
}
233+
234+
protected IndexCommit wrapCommit(IndexCommit indexCommit, boolean acquiredInternally) {
235+
return new SnapshotIndexCommit(indexCommit, acquiredInternally);
222236
}
223237

224238
/**
@@ -227,7 +241,8 @@ protected IndexCommit wrapCommit(IndexCommit indexCommit) {
227241
* @return true if the acquired commit can be clean up.
228242
*/
229243
synchronized boolean releaseCommit(final IndexCommit acquiredCommit) {
230-
final IndexCommit releasingCommit = ((SnapshotIndexCommit) acquiredCommit).getIndexCommit();
244+
final SnapshotIndexCommit snapshotIndexCommit = (SnapshotIndexCommit) acquiredCommit;
245+
final IndexCommit releasingCommit = snapshotIndexCommit.getIndexCommit();
231246
assert acquiredIndexCommits.containsKey(releasingCommit)
232247
: "Release non-acquired commit;"
233248
+ "acquired commits ["
@@ -242,6 +257,8 @@ synchronized boolean releaseCommit(final IndexCommit acquiredCommit) {
242257
}
243258
return count - 1;
244259
});
260+
assert snapshotIndexCommit.acquiredInternally == false || internallyAcquiredIndexCommits.remove(releasingCommit)
261+
: "Trying to release a commit [" + releasingCommit + "] that hasn't been previously acquired internally";
245262

246263
assert refCount == null || refCount > 0 : "Number of references for acquired commit can not be negative [" + refCount + "]";
247264
// The commit can be clean up only if no refCount and it is neither the safe commit nor last commit.
@@ -296,10 +313,16 @@ private static Set<String> listOfNewFileNames(IndexCommit previous, IndexCommit
296313
}
297314

298315
/**
299-
* Checks whether the deletion policy is holding on to acquired index commits
316+
* Checks whether the deletion policy is holding on to externally acquired index commits
300317
*/
301-
synchronized boolean hasAcquiredIndexCommits() {
302-
return acquiredIndexCommits.isEmpty() == false;
318+
synchronized boolean hasAcquiredIndexCommitsForTesting() {
319+
// We explicitly check only external commits and disregard internal commits acquired by the commits listener
320+
for (var e : acquiredIndexCommits.entrySet()) {
321+
if (internallyAcquiredIndexCommits.contains(e.getKey()) == false || e.getValue() > 1) {
322+
return true;
323+
}
324+
}
325+
return false;
303326
}
304327

305328
/**
@@ -320,8 +343,12 @@ public static String commitDescription(IndexCommit commit) throws IOException {
320343
* A wrapper of an index commit that prevents it from being deleted.
321344
*/
322345
private static class SnapshotIndexCommit extends FilterIndexCommit {
323-
SnapshotIndexCommit(IndexCommit delegate) {
346+
347+
private final boolean acquiredInternally;
348+
349+
SnapshotIndexCommit(IndexCommit delegate, boolean acquiredInternally) {
324350
super(delegate);
351+
this.acquiredInternally = acquiredInternally;
325352
}
326353

327354
@Override

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -669,8 +669,8 @@ Translog getTranslog() {
669669
}
670670

671671
// Package private for testing purposes only
672-
boolean hasAcquiredIndexCommits() {
673-
return combinedDeletionPolicy.hasAcquiredIndexCommits();
672+
boolean hasAcquiredIndexCommitsForTesting() {
673+
return combinedDeletionPolicy.hasAcquiredIndexCommitsForTesting();
674674
}
675675

676676
@Override

test/framework/src/main/java/org/elasticsearch/index/engine/EngineTestCase.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1440,10 +1440,10 @@ public static void waitForOpsToComplete(InternalEngine engine, long seqNo) throw
14401440
assertBusy(() -> assertThat(engine.getLocalCheckpointTracker().getProcessedCheckpoint(), greaterThanOrEqualTo(seqNo)));
14411441
}
14421442

1443-
public static boolean hasAcquiredIndexCommits(Engine engine) {
1443+
public static boolean hasAcquiredIndexCommitsForTesting(Engine engine) {
14441444
assert engine instanceof InternalEngine : "only InternalEngines have snapshotted commits, got: " + engine.getClass();
14451445
InternalEngine internalEngine = (InternalEngine) engine;
1446-
return internalEngine.hasAcquiredIndexCommits();
1446+
return internalEngine.hasAcquiredIndexCommitsForTesting();
14471447
}
14481448

14491449
public static final class PrimaryTermSupplier implements LongSupplier {

test/framework/src/main/java/org/elasticsearch/test/InternalTestCluster.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1369,7 +1369,7 @@ private void assertNoAcquiredIndexCommit() throws Exception {
13691369
if (engine instanceof InternalEngine) {
13701370
assertFalse(
13711371
indexShard.routingEntry().toString() + " has unreleased snapshotted index commits",
1372-
EngineTestCase.hasAcquiredIndexCommits(engine)
1372+
EngineTestCase.hasAcquiredIndexCommitsForTesting(engine)
13731373
);
13741374
}
13751375
} catch (AlreadyClosedException ignored) {

x-pack/plugin/ccr/src/test/java/org/elasticsearch/xpack/ccr/repository/CcrRestoreSourceServiceTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,9 @@ public void testGetSessionDoesNotLeakFileIfClosed() throws IOException {
215215
sessionReader.readFileBytes(files.get(1).name(), MockBigArrays.NON_RECYCLING_INSTANCE.newByteArray(10, false));
216216
}
217217

218-
assertTrue(EngineTestCase.hasAcquiredIndexCommits(IndexShardTestCase.getEngine(indexShard)));
218+
assertTrue(EngineTestCase.hasAcquiredIndexCommitsForTesting(IndexShardTestCase.getEngine(indexShard)));
219219
restoreSourceService.closeSession(sessionUUID);
220-
assertFalse(EngineTestCase.hasAcquiredIndexCommits(IndexShardTestCase.getEngine(indexShard)));
220+
assertFalse(EngineTestCase.hasAcquiredIndexCommitsForTesting(IndexShardTestCase.getEngine(indexShard)));
221221

222222
closeShards(indexShard);
223223
// Exception will be thrown if file is not closed.

0 commit comments

Comments
 (0)