Skip to content

Commit bc47b30

Browse files
jvareninaMario Kevo
authored andcommitted
GEODE-10412: Clear expired tombstones during region destroy (#7838)
* GEODE-10412: Clear expired tombstones during region destroy The issue: During region destroy operation, the expired tombstones aren't cleared when non-expired ones are available. Later, these expired tombstones prevent all other regions' tombstones from being cleared from memory, causing many issues (memory and disk exhaustion). The solution: When a region is destroyed, it must clear all the related expired and non-expired tombstones from memory. * Add distributed test that reproduce the issue * Update after review
1 parent 62e60b5 commit bc47b30

3 files changed

Lines changed: 86 additions & 8 deletions

File tree

geode-core/src/distributedTest/java/org/apache/geode/internal/cache/versions/TombstoneDUnitTest.java

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,13 @@
1414
*/
1515
package org.apache.geode.internal.cache.versions;
1616

17+
import static org.apache.geode.cache.RegionShortcut.PARTITION_PERSISTENT;
1718
import static org.apache.geode.cache.RegionShortcut.REPLICATE;
1819
import static org.apache.geode.cache.RegionShortcut.REPLICATE_PERSISTENT;
1920
import static org.apache.geode.internal.AvailablePortHelper.getRandomAvailableTCPPort;
2021
import static org.apache.geode.internal.cache.InitialImageOperation.GIITestHookType.DuringApplyDelta;
2122
import static org.apache.geode.internal.cache.InitialImageOperation.resetAllGIITestHooks;
23+
import static org.apache.geode.internal.cache.TombstoneService.EXPIRED_TOMBSTONE_LIMIT;
2224
import static org.apache.geode.test.awaitility.GeodeAwaitility.await;
2325
import static org.assertj.core.api.Assertions.assertThat;
2426
import static org.junit.Assert.assertEquals;
@@ -121,6 +123,35 @@ public void testTombstoneGcMessagesBetweenPersistentAndNonPersistentRegion() {
121123
});
122124
}
123125

126+
@Test
127+
public void testTombstoneExpiredAndNonExpiredAreClearedAfterRegionIsDestroyed() {
128+
VM vm0 = VM.getVM(0);
129+
130+
vm0.invoke(() -> {
131+
// reduce timeout so that tombstone is immediately marked as expired
132+
TombstoneService.REPLICATE_TOMBSTONE_TIMEOUT = 100;
133+
createCacheAndRegion(PARTITION_PERSISTENT);
134+
region.put("K1", "V1");
135+
region.destroy("K1");
136+
});
137+
138+
vm0.invoke(() -> {
139+
waitForScheduledTombstoneCount(0);
140+
// increase timeout so that next tombstone doesn't expire
141+
TombstoneService.REPLICATE_TOMBSTONE_TIMEOUT = 150000;
142+
region.put("K1", "V1");
143+
region.destroy("K1");
144+
145+
region.destroyRegion();
146+
// force expiry of batch - there is only one expired tombstone at this moment
147+
EXPIRED_TOMBSTONE_LIMIT = 1;
148+
});
149+
150+
vm0.invoke(() -> {
151+
createCacheAndRegion(PARTITION_PERSISTENT);
152+
checkExpiredTombstones(0);
153+
});
154+
}
124155

125156
@Test
126157
public void testWhenAnOutOfRangeTimeStampIsSeenWeExpireItInReplicateTombstoneSweeper() {
@@ -562,6 +593,17 @@ private void waitForTombstoneCount(int count) {
562593
}
563594
}
564595

596+
private void waitForScheduledTombstoneCount(int count) {
597+
LocalRegion region = (LocalRegion) cache.getRegion(REGION_NAME);
598+
await().until(() -> ((InternalCache) cache).getTombstoneService().getSweeper(region).tombstones
599+
.size() == count);
600+
}
601+
602+
private void checkExpiredTombstones(int count) {
603+
await().until(
604+
() -> ((InternalCache) cache).getTombstoneService().getScheduledTombstoneCount() == count);
605+
}
606+
565607
private void performGC(int count) throws Exception {
566608
((InternalCache) cache).getTombstoneService().forceBatchExpirationForTests(count);
567609
}

geode-core/src/main/java/org/apache/geode/internal/cache/TombstoneService.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -926,7 +926,11 @@ private boolean removeUnexpiredIf(Predicate<Tombstone> predicate) {
926926
* @return true if predicate ever returned true
927927
*/
928928
private boolean removeIf(Predicate<Tombstone> predicate) {
929-
return removeUnexpiredIf(predicate) || removeExpiredIf(predicate);
929+
boolean isTombstoneRemoved = removeUnexpiredIf(predicate);
930+
if (removeExpiredIf(predicate)) {
931+
isTombstoneRemoved = true;
932+
}
933+
return isTombstoneRemoved;
930934
}
931935

932936
synchronized void start() {

geode-core/src/test/java/org/apache/geode/internal/cache/TombstoneServiceTest.java

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
*/
1515
package org.apache.geode.internal.cache;
1616

17+
import static org.assertj.core.api.Assertions.assertThat;
1718
import static org.mockito.Mockito.mock;
1819
import static org.mockito.Mockito.verify;
1920
import static org.mockito.Mockito.when;
@@ -40,7 +41,9 @@ public class TombstoneServiceTest {
4041
DistributedRegion region;
4142
VersionTag destroyedVersion;
4243
private TombstoneService.ReplicateTombstoneSweeper replicateTombstoneSweeper;
43-
private TombstoneService.Tombstone tombstone;
44+
private TombstoneService.Tombstone tombstone1;
45+
46+
private TombstoneService.Tombstone tombstone2;
4447

4548

4649
@Before
@@ -55,18 +58,19 @@ public void setUp() throws Exception {
5558
destroyedVersion = mock(VersionTag.class);
5659
replicateTombstoneSweeper = new TombstoneService.ReplicateTombstoneSweeper(cacheTime, stats,
5760
cancelCriterion, executor);
58-
tombstone = new TombstoneService.Tombstone(entry, region, destroyedVersion);
59-
tombstone.entry = entry;
61+
tombstone1 = new TombstoneService.Tombstone(entry, region, destroyedVersion);
62+
tombstone2 = new TombstoneService.Tombstone(entry, region, destroyedVersion);
63+
tombstone1.entry = entry;
6064
}
6165

6266
@Test
6367
public void validateThatRemoveIsNotCalledOnTombstoneInRegionThatIsNotInitialized() {
6468
when(region.isInitialized()).thenReturn(false);
6569
when(region.getRegionMap()).thenReturn(regionMap);
6670

67-
replicateTombstoneSweeper.expireTombstone(tombstone);
71+
replicateTombstoneSweeper.expireTombstone(tombstone1);
6872
replicateTombstoneSweeper.expireBatch();
69-
verify(regionMap, Mockito.never()).removeTombstone(tombstone.entry, tombstone);
73+
verify(regionMap, Mockito.never()).removeTombstone(tombstone1.entry, tombstone1);
7074
}
7175

7276
@Test
@@ -80,8 +84,36 @@ public void validateThatRemoveIsCalledOnTombstoneInRegionThatIsInitialized() {
8084
when(region.getDiskRegion()).thenReturn(mock(DiskRegion.class));
8185

8286

83-
replicateTombstoneSweeper.expireTombstone(tombstone);
87+
replicateTombstoneSweeper.expireTombstone(tombstone1);
8488
replicateTombstoneSweeper.expireBatch();
85-
verify(regionMap).removeTombstone(tombstone.entry, tombstone);
89+
verify(regionMap).removeTombstone(tombstone1.entry, tombstone1);
90+
}
91+
92+
@Test
93+
public void validateThatTheExpiredTombstonesAreCleared() {
94+
when(region.getRegionMap()).thenReturn(regionMap);
95+
replicateTombstoneSweeper.expireTombstone(tombstone1);
96+
assertThat(replicateTombstoneSweeper.getScheduledTombstoneCount()).isOne();
97+
replicateTombstoneSweeper.unscheduleTombstones(region);
98+
assertThat(replicateTombstoneSweeper.getScheduledTombstoneCount()).isZero();
99+
}
100+
101+
@Test
102+
public void validateThatTheNonExpiredTombstonesAreCleared() {
103+
when(region.getRegionMap()).thenReturn(regionMap);
104+
replicateTombstoneSweeper.scheduleTombstone(tombstone1);
105+
assertThat(replicateTombstoneSweeper.getScheduledTombstoneCount()).isOne();
106+
replicateTombstoneSweeper.unscheduleTombstones(region);
107+
assertThat(replicateTombstoneSweeper.getScheduledTombstoneCount()).isZero();
108+
}
109+
110+
@Test
111+
public void validateThatTheNonExpiredAndExpiredTombstonesAreCleared() {
112+
when(region.getRegionMap()).thenReturn(regionMap);
113+
replicateTombstoneSweeper.scheduleTombstone(tombstone1);
114+
replicateTombstoneSweeper.expireTombstone(tombstone2);
115+
assertThat(replicateTombstoneSweeper.getScheduledTombstoneCount()).isEqualTo(2);
116+
replicateTombstoneSweeper.unscheduleTombstones(region);
117+
assertThat(replicateTombstoneSweeper.getScheduledTombstoneCount()).isZero();
86118
}
87119
}

0 commit comments

Comments
 (0)