This is an automated email from the ASF dual-hosted git repository.

zhouxj pushed a commit to branch support/1.12
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/support/1.12 by this push:
     new 27d60c9e7b GEODE-10290: GII requester should remove departed members 
(#7670)
27d60c9e7b is described below

commit 27d60c9e7b41583775b0724ac44c15b09946ef44
Author: Xiaojian Zhou <[email protected]>
AuthorDate: Wed May 18 11:55:15 2022 -0700

    GEODE-10290: GII requester should remove departed members (#7670)
    
    (cherry picked from commit 3d6354cb6b182d54531a8103a357f03754cf5165)
---
 ...PartitionedRegionRestartRebalanceDUnitTest.java | 133 +++++++++++++++++
 .../internal/cache/InitialImageOperation.java      |  48 ++++--
 .../cache/versions/RegionVersionHolder.java        |   2 +
 .../cache/versions/RegionVersionVector.java        |  16 +-
 .../internal/cache/InitialImageOperationTest.java  | 165 +++++++++++++++++++++
 5 files changed, 343 insertions(+), 21 deletions(-)

diff --git 
a/geode-core/src/distributedTest/java/org/apache/geode/internal/cache/PartitionedRegionRestartRebalanceDUnitTest.java
 
b/geode-core/src/distributedTest/java/org/apache/geode/internal/cache/PartitionedRegionRestartRebalanceDUnitTest.java
new file mode 100644
index 0000000000..8f8a37758b
--- /dev/null
+++ 
b/geode-core/src/distributedTest/java/org/apache/geode/internal/cache/PartitionedRegionRestartRebalanceDUnitTest.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more 
contributor license
+ * agreements. See the NOTICE file distributed with this work for additional 
information regarding
+ * copyright ownership. The ASF licenses this file to You under the Apache 
License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the 
License. You may obtain a
+ * copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software 
distributed under the License
+ * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 
KIND, either express
+ * or implied. See the License for the specific language governing permissions 
and limitations under
+ * the License.
+ */
+package org.apache.geode.internal.cache;
+
+import static org.apache.geode.test.dunit.VM.getVM;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import java.io.Serializable;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import org.apache.geode.cache.DataPolicy;
+import org.apache.geode.cache.PartitionAttributesFactory;
+import org.apache.geode.cache.RegionFactory;
+import org.apache.geode.cache.control.RebalanceOperation;
+import org.apache.geode.cache.control.RebalanceResults;
+import org.apache.geode.internal.cache.versions.VersionSource;
+import org.apache.geode.test.dunit.VM;
+import org.apache.geode.test.dunit.rules.CacheRule;
+import org.apache.geode.test.dunit.rules.DistributedRule;
+
+public class PartitionedRegionRestartRebalanceDUnitTest implements 
Serializable {
+  private static final int REDUNDANT_COPIES = 2;
+  private static final int TOTAL_NUM_BUCKETS = 12;
+  private static final Logger logger = LogManager.getLogger();
+
+  private String REGION_NAME = getClass().getSimpleName();;
+  private VM[] datastores;
+
+  @Rule
+  public DistributedRule distributedRule = new DistributedRule();
+
+  @Rule
+  public CacheRule cacheRule = new CacheRule();
+
+  @Before
+  public void setUp() throws Exception {
+    datastores = new VM[4];
+    for (int i = 0; i < datastores.length; i++) {
+      datastores[i] = getVM(i);
+      datastores[i].invoke(() -> cacheRule.createCache());
+      datastores[i].invoke(() -> createRegion());
+    }
+    datastores[0].invoke(() -> feedData());
+  }
+
+  private void createRegion() {
+    PartitionAttributesFactory<String, Integer> paf = new 
PartitionAttributesFactory();
+    paf.setRedundantCopies(REDUNDANT_COPIES);
+    paf.setTotalNumBuckets(TOTAL_NUM_BUCKETS);
+
+    RegionFactory<String, Integer> rf = 
cacheRule.getCache().createRegionFactory();
+    rf.setDataPolicy(DataPolicy.PARTITION);
+    rf.setPartitionAttributes(paf.create());
+    LocalRegion region = (LocalRegion) rf.create(REGION_NAME);
+  }
+
+  private void feedData() throws InterruptedException {
+    PartitionedRegion pr = (PartitionedRegion) 
cacheRule.getCache().getRegion(REGION_NAME);
+    for (int i = 0; i < TOTAL_NUM_BUCKETS * 2; i++) {
+      pr.put(i, "VALUE-" + i);
+      if (i < TOTAL_NUM_BUCKETS) {
+        pr.destroy(i);
+      }
+    }
+    
cacheRule.getCache().getTombstoneService().forceBatchExpirationForTests(TOTAL_NUM_BUCKETS);
+  }
+
+  private void rebalance() throws InterruptedException {
+    RebalanceOperation op =
+        
cacheRule.getCache().getResourceManager().createRebalanceFactory().start();
+    RebalanceResults results = op.getResults();
+    logger.info("Rebalance total time is " + results.getTotalTime());
+  }
+
+  private void verify() {
+    PartitionedRegion pr = (PartitionedRegion) 
cacheRule.getCache().getRegion(REGION_NAME);
+    for (BucketRegion br : pr.getDataStore().getAllLocalBucketRegions()) {
+      Set<VersionSource> departedMemberSet = 
br.getVersionVector().getDepartedMembersSet();
+      for (Object key : br.getRegionKeysForIteration()) {
+        RegionEntry entry = br.getRegionEntry(key);
+        departedMemberSet.remove(entry.getVersionStamp().getMemberID());
+        if (departedMemberSet.isEmpty()) {
+          break;
+        }
+      }
+      Map map = br.getVersionVector().getMemberToVersion();
+      for (Object key : br.getVersionVector().getMemberToVersion().keySet()) {
+        logger.info(br.getFullPath() + ":" + key + ":"
+            + br.getVersionVector().getMemberToVersion().get(key));
+      }
+      // The test proved that departedMemberSet is not growing
+      
assertThat(departedMemberSet.size()).isLessThanOrEqualTo(datastores.length);
+    }
+  }
+
+  @Test
+  public void restartAndRebalanceShouldNotIncreaseMemberToVersionMap() {
+    for (int i = 0; i < datastores.length * 10; i++) {
+      datastores[i % datastores.length].invoke(() -> {
+        cacheRule.getCache().close();
+      });
+      datastores[(i + 1) % datastores.length].invoke(() -> {
+        rebalance();
+        verify();
+      });
+      datastores[i % datastores.length].invoke(() -> {
+        cacheRule.createCache();
+        createRegion();
+        rebalance();
+        verify();
+      });
+    }
+  }
+}
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/cache/InitialImageOperation.java
 
b/geode-core/src/main/java/org/apache/geode/internal/cache/InitialImageOperation.java
index 199a678914..38bf3bd18b 100644
--- 
a/geode-core/src/main/java/org/apache/geode/internal/cache/InitialImageOperation.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/cache/InitialImageOperation.java
@@ -364,7 +364,7 @@ public class InitialImageOperation {
         // remote_rvv will be filled with the versions of unfinished keys
         // then if recoveredRVV is still newer than the filled remote_rvv, do 
fullGII
         remote_rvv = received_rvv.getCloneForTransmission();
-        keysOfUnfinishedOps = processReceivedRVV(remote_rvv, recoveredRVV);
+        keysOfUnfinishedOps = processReceivedRVV(remote_rvv, recoveredRVV, 
received_rvv);
         if (internalAfterCalculatedUnfinishedOps != null
             && 
internalAfterCalculatedUnfinishedOps.getRegionName().equals(this.region.getName()))
 {
           internalAfterCalculatedUnfinishedOps.run();
@@ -1069,21 +1069,32 @@ public class InitialImageOperation {
   /**
    * Compare the received RVV with local RVV and return a set of keys for 
unfinished operations.
    *
-   * @param remoteRVV RVV from provider
+   * @param remoteRVV RVV from provider to be filled with unfinished operations
    * @param localRVV RVV recovered from disk
+   * @param receivedRVV original RVV from provider to remove departed members
    * @return set for keys of unfinished operations.
    */
-  protected Set processReceivedRVV(RegionVersionVector remoteRVV, 
RegionVersionVector localRVV) {
+  protected Set<Object> processReceivedRVV(RegionVersionVector remoteRVV,
+      RegionVersionVector localRVV, RegionVersionVector receivedRVV) {
     if (remoteRVV == null) {
       return null;
     }
     // calculate keys for unfinished ops
-    HashSet keys = new HashSet();
-    if (this.region.getDataPolicy().withPersistence()
-        && localRVV.isNewerThanOrCanFillExceptionsFor(remoteRVV)) {
-      // only search for unfinished keys when localRVV has something newer
-      // and the region is persistent region
-      Iterator it = this.region.getBestIterator(false);
+    HashSet<Object> keys = new HashSet<>();
+    Set<VersionSource> departedMemberSet = receivedRVV.getDepartedMembersSet();
+    boolean isPersistentRegion = region.getDataPolicy().withPersistence();
+    Set<VersionSource> foundIds;
+    if (!isPersistentRegion) {
+      foundIds = new HashSet<>();
+    } else {
+      foundIds = Collections.emptySet();
+    }
+    if ((isPersistentRegion && 
localRVV.isNewerThanOrCanFillExceptionsFor(remoteRVV))
+        || !departedMemberSet.isEmpty()) {
+      // Only search for unfinished keys when localRVV has something newer
+      // and the region is persistent region.
+      // Search for departed members if region is not persistent region
+      Iterator<RegionEntry> it = region.getBestIterator(false);
       int count = 0;
       VersionSource<?> myId = this.region.getVersionMember();
       while (it.hasNext()) {
@@ -1093,7 +1104,9 @@ public class InitialImageOperation {
         if (id == null) {
           id = myId;
         }
-        if (!remoteRVV.contains(id, stamp.getRegionVersion())) {
+        if (!isPersistentRegion) {
+          foundIds.add(id);
+        } else if (!remoteRVV.contains(id, stamp.getRegionVersion())) {
           // found an unfinished operation
           keys.add(mapEntry.getKey());
           remoteRVV.recordVersion(id, stamp.getRegionVersion());
@@ -1116,6 +1129,13 @@ public class InitialImageOperation {
         }
       }
     }
+    if (!departedMemberSet.isEmpty()) {
+      if (localRVV != null) {
+        localRVV.removeOldMembers(foundIds);
+      }
+      receivedRVV.removeOldMembers(foundIds);
+      remoteRVV.removeOldMembers(foundIds);
+    }
     return keys;
   }
 
@@ -2082,11 +2102,9 @@ public class InitialImageOperation {
           // if this region is destroyed while we are sending data, then abort.
         } while (keepGoing && it.hasNext());
 
-        if (foundIds.size() > 0) {
-          RegionVersionVector vv = rgn.getVersionVector();
-          if (vv != null) {
-            vv.removeOldMembers(foundIds);
-          }
+        RegionVersionVector vv = rgn.getVersionVector();
+        if (vv != null) {
+          vv.removeOldMembers(foundIds);
         }
         // return false if we were told to abort
         return sentLastChunk;
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/cache/versions/RegionVersionHolder.java
 
b/geode-core/src/main/java/org/apache/geode/internal/cache/versions/RegionVersionHolder.java
index 75439ddcf3..105e71b656 100755
--- 
a/geode-core/src/main/java/org/apache/geode/internal/cache/versions/RegionVersionHolder.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/cache/versions/RegionVersionHolder.java
@@ -207,6 +207,8 @@ public class RegionVersionHolder<T> implements Cloneable, 
DataSerializable {
       sb.append(this.exceptions.toString());
     }
     sb.append("}");
+    sb.append("id=" + id);
+    sb.append(",departed?" + isDepartedMember);
     return sb.toString();
   }
 
diff --git 
a/geode-core/src/main/java/org/apache/geode/internal/cache/versions/RegionVersionVector.java
 
b/geode-core/src/main/java/org/apache/geode/internal/cache/versions/RegionVersionVector.java
index 575cc0b3d9..36cf9ac602 100644
--- 
a/geode-core/src/main/java/org/apache/geode/internal/cache/versions/RegionVersionVector.java
+++ 
b/geode-core/src/main/java/org/apache/geode/internal/cache/versions/RegionVersionVector.java
@@ -192,11 +192,12 @@ public abstract class RegionVersionVector<T extends 
VersionSource<?>>
 
   @VisibleForTesting
   RegionVersionVector(T ownerId, LocalRegion owner, long version) {
-    this.myId = ownerId;
-    this.isLiveVector = true;
-    this.region = owner;
-    this.localExceptions = new RegionVersionHolder<T>(0);
-    this.memberToVersion =
+    myId = ownerId;
+    isLiveVector = true;
+    region = owner;
+    localExceptions = new RegionVersionHolder<>(0);
+    localExceptions.id = myId;
+    memberToVersion =
         new ConcurrentHashMap<>(INITIAL_CAPACITY, LOAD_FACTOR, 
CONCURRENCY_LEVEL);
     this.memberToGCVersion =
         new ConcurrentHashMap<>(INITIAL_CAPACITY, LOAD_FACTOR, 
CONCURRENCY_LEVEL);
@@ -609,7 +610,10 @@ public abstract class RegionVersionVector<T extends 
VersionSource<?>>
       if (!mbr.equals(this.myId)) {
         h = otherHolder.clone();
         h.makeReadyForRecording();
-        this.memberToVersion.put(mbr, h);
+        if (h.id == null) {
+          h.id = mbr;
+        }
+        memberToVersion.put(mbr, h);
       } else {
         RegionVersionHolder<T> vh = otherHolder;
         long version = vh.version;
diff --git 
a/geode-core/src/test/java/org/apache/geode/internal/cache/InitialImageOperationTest.java
 
b/geode-core/src/test/java/org/apache/geode/internal/cache/InitialImageOperationTest.java
index 61bed57db8..ffad8b416e 100644
--- 
a/geode-core/src/test/java/org/apache/geode/internal/cache/InitialImageOperationTest.java
+++ 
b/geode-core/src/test/java/org/apache/geode/internal/cache/InitialImageOperationTest.java
@@ -15,6 +15,7 @@
 package org.apache.geode.internal.cache;
 
 import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -22,14 +23,23 @@ import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
+import java.util.ArrayList;
+import java.util.Iterator;
+
 import org.junit.Before;
 import org.junit.Test;
 
 import org.apache.geode.cache.CacheClosedException;
+import org.apache.geode.cache.DataPolicy;
 import org.apache.geode.cache.Scope;
 import org.apache.geode.distributed.internal.ClusterDistributionManager;
 import 
org.apache.geode.distributed.internal.membership.InternalDistributedMember;
+import org.apache.geode.internal.cache.persistence.DiskStoreID;
+import org.apache.geode.internal.cache.versions.DiskRegionVersionVector;
+import org.apache.geode.internal.cache.versions.RegionVersionVector;
+import org.apache.geode.internal.cache.versions.VMRegionVersionVector;
 import org.apache.geode.internal.cache.versions.VersionSource;
+import org.apache.geode.internal.cache.versions.VersionStamp;
 
 public class InitialImageOperationTest {
 
@@ -95,4 +105,159 @@ public class InitialImageOperationTest {
     verify(distributedRegion, 
never()).scheduleSynchronizeForLostMember(lostMember, versionSource,
         0);
   }
+
+  @Test
+  public void shouldRemoveDepartedMembersFromRVVForNonPersistentRegion() {
+    InternalDistributedMember server1 = new InternalDistributedMember("host1", 
101);
+    InternalDistributedMember server2 = new InternalDistributedMember("host2", 
102);
+    InternalDistributedMember server3 = new InternalDistributedMember("host3", 
103);
+    InternalDistributedMember server4 = new InternalDistributedMember("host4", 
104);
+    when(distributedRegion.getDataPolicy()).thenReturn(DataPolicy.REPLICATE);
+    when(distributedRegion.getVersionMember()).thenReturn(server1);
+
+    RegionEntry re1 = mock(RegionEntry.class);
+    RegionEntry re2 = mock(RegionEntry.class);
+    RegionEntry re3 = mock(RegionEntry.class);
+    ArrayList<RegionEntry> entries = new ArrayList<>();
+    entries.add(re1);
+    entries.add(re2);
+    entries.add(re3);
+    Iterator<RegionEntry> iterator = entries.iterator();
+    when(distributedRegion.getBestIterator(false)).thenReturn(iterator);
+    VersionStamp stamp1 = mock(VersionStamp.class);
+    VersionStamp stamp2 = mock(VersionStamp.class);
+    VersionStamp stamp3 = mock(VersionStamp.class);
+    when(re1.getVersionStamp()).thenReturn(stamp1);
+    when(re2.getVersionStamp()).thenReturn(stamp2);
+    when(re3.getVersionStamp()).thenReturn(stamp3);
+    when(stamp1.getMemberID()).thenReturn(server1);
+    when(stamp2.getMemberID()).thenReturn(server2);
+    when(stamp3.getMemberID()).thenReturn(server3);
+
+    RegionMap regionMap = mock(RegionMap.class);
+    InitialImageOperation operation = spy(new 
InitialImageOperation(distributedRegion, regionMap));
+
+    RegionVersionVector recoveredRVV = new VMRegionVersionVector(server1);
+    recoveredRVV.recordVersion(server1, 1);
+    recoveredRVV.recordVersion(server2, 1);
+    recoveredRVV.recordVersion(server3, 1);
+    recoveredRVV.recordVersion(server4, 1);
+    recoveredRVV.recordGCVersion(server2, 1);
+    recoveredRVV.recordGCVersion(server3, 1);
+    recoveredRVV.recordGCVersion(server4, 1);
+    recoveredRVV.memberDeparted(null, server3, true);
+    recoveredRVV.memberDeparted(null, server4, true);
+    assertThat(recoveredRVV.isDepartedMember(server3)).isTrue();
+    assertThat(recoveredRVV.isDepartedMember(server4)).isTrue();
+    assertThat(recoveredRVV.getMemberToVersion().size()).isEqualTo(4);
+    assertThat(recoveredRVV.getMemberToGCVersion().size()).isEqualTo(3);
+
+    RegionVersionVector receivedRVV = new VMRegionVersionVector(server2);
+    receivedRVV.recordVersion(server1, 1);
+    receivedRVV.recordVersion(server2, 1);
+    receivedRVV.recordVersion(server2, 2);
+    receivedRVV.recordVersion(server3, 1);
+    receivedRVV.recordVersion(server4, 1);
+    receivedRVV.recordGCVersion(server2, 1);
+    receivedRVV.recordGCVersion(server3, 1);
+    receivedRVV.recordGCVersion(server4, 1);
+    receivedRVV.memberDeparted(null, server3, true);
+    receivedRVV.memberDeparted(null, server4, true);
+    assertThat(receivedRVV.isDepartedMember(server3)).isTrue();
+    assertThat(receivedRVV.isDepartedMember(server4)).isTrue();
+    assertThat(receivedRVV.getMemberToVersion().size()).isEqualTo(4);
+    assertThat(receivedRVV.getMemberToGCVersion().size()).isEqualTo(3);
+
+    RegionVersionVector remoteRVV = receivedRVV.getCloneForTransmission();
+
+    operation.processReceivedRVV(remoteRVV, recoveredRVV, receivedRVV);
+    assertThat(receivedRVV.getMemberToVersion().size()).isEqualTo(3);
+    assertThat(receivedRVV.getMemberToGCVersion().size()).isEqualTo(2);
+    assertThat(recoveredRVV.getMemberToVersion().size()).isEqualTo(3);
+    assertThat(recoveredRVV.getMemberToGCVersion().size()).isEqualTo(2);
+    assertThat(remoteRVV.getMemberToVersion().size()).isEqualTo(3);
+    assertThat(remoteRVV.getMemberToGCVersion().size()).isEqualTo(2);
+    
assertThat(recoveredRVV.getMemberToVersion().containsKey(server3)).isTrue();
+    
assertThat(recoveredRVV.getMemberToVersion().containsKey(server4)).isFalse();
+    
assertThat(recoveredRVV.getMemberToGCVersion().containsKey(server3)).isTrue();
+    
assertThat(recoveredRVV.getMemberToGCVersion().containsKey(server4)).isFalse();
+    assertThat(receivedRVV.getMemberToVersion().containsKey(server3)).isTrue();
+    
assertThat(receivedRVV.getMemberToVersion().containsKey(server4)).isFalse();
+    
assertThat(receivedRVV.getMemberToGCVersion().containsKey(server3)).isTrue();
+    
assertThat(receivedRVV.getMemberToGCVersion().containsKey(server4)).isFalse();
+  }
+
+  @Test
+  public void shouldNotRemoveDepartedMembersFromRVVForPersistentRegion() {
+    InternalDistributedMember idm = new InternalDistributedMember("host1", 
101);
+    DiskStoreID server1 = new DiskStoreID(0, 0);
+    DiskStoreID server2 = new DiskStoreID(0, 1);
+    DiskStoreID server3 = new DiskStoreID(0, 2);
+    DiskStoreID server4 = new DiskStoreID(0, 3);
+    
when(distributedRegion.getDataPolicy()).thenReturn(DataPolicy.PERSISTENT_REPLICATE);
+    when(distributedRegion.getVersionMember()).thenReturn(server1);
+
+    RegionEntry re1 = mock(RegionEntry.class);
+    RegionEntry re2 = mock(RegionEntry.class);
+    RegionEntry re3 = mock(RegionEntry.class);
+    ArrayList<RegionEntry> entries = new ArrayList<>();
+    entries.add(re1);
+    entries.add(re2);
+    entries.add(re3);
+    Iterator<RegionEntry> iterator = entries.iterator();
+    when(distributedRegion.getBestIterator(false)).thenReturn(iterator);
+    VersionStamp stamp1 = mock(VersionStamp.class);
+    VersionStamp stamp2 = mock(VersionStamp.class);
+    VersionStamp stamp3 = mock(VersionStamp.class);
+    when(re1.getVersionStamp()).thenReturn(stamp1);
+    when(re2.getVersionStamp()).thenReturn(stamp2);
+    when(re3.getVersionStamp()).thenReturn(stamp3);
+    when(stamp1.getMemberID()).thenReturn(server1);
+    when(stamp2.getMemberID()).thenReturn(server2);
+    when(stamp3.getMemberID()).thenReturn(server3);
+
+    RegionMap regionMap = mock(RegionMap.class);
+    InitialImageOperation operation = spy(new 
InitialImageOperation(distributedRegion, regionMap));
+
+    RegionVersionVector recoveredRVV = new DiskRegionVersionVector(server1);
+    recoveredRVV.recordVersion(server1, 1);
+    recoveredRVV.recordVersion(server2, 1);
+    recoveredRVV.recordVersion(server3, 1);
+    recoveredRVV.recordVersion(server4, 1);
+    recoveredRVV.recordGCVersion(server2, 1);
+    recoveredRVV.recordGCVersion(server3, 1);
+    recoveredRVV.recordGCVersion(server4, 1);
+    recoveredRVV.memberDeparted(null, idm, true);
+    assertThat(recoveredRVV.getMemberToVersion().size()).isEqualTo(4);
+    assertThat(recoveredRVV.getMemberToGCVersion().size()).isEqualTo(3);
+
+    RegionVersionVector receivedRVV = new DiskRegionVersionVector(server2);
+    receivedRVV.recordVersion(server1, 1);
+    receivedRVV.recordVersion(server2, 1);
+    receivedRVV.recordVersion(server2, 2);
+    receivedRVV.recordVersion(server3, 1);
+    receivedRVV.recordVersion(server4, 1);
+    receivedRVV.recordGCVersion(server2, 1);
+    receivedRVV.recordGCVersion(server3, 1);
+    receivedRVV.recordGCVersion(server4, 1);
+    receivedRVV.memberDeparted(null, idm, true);
+    assertThat(receivedRVV.getMemberToVersion().size()).isEqualTo(4);
+    assertThat(receivedRVV.getMemberToGCVersion().size()).isEqualTo(3);
+
+    RegionVersionVector remoteRVV = receivedRVV.getCloneForTransmission();
+    receivedRVV = spy(receivedRVV);
+    recoveredRVV = spy(recoveredRVV);
+    remoteRVV = spy(remoteRVV);
+
+    operation.processReceivedRVV(remoteRVV, recoveredRVV, receivedRVV);
+    assertThat(receivedRVV.getMemberToVersion().size()).isEqualTo(4);
+    assertThat(receivedRVV.getMemberToGCVersion().size()).isEqualTo(3);
+    assertThat(recoveredRVV.getMemberToVersion().size()).isEqualTo(4);
+    assertThat(recoveredRVV.getMemberToGCVersion().size()).isEqualTo(3);
+    assertThat(remoteRVV.getMemberToVersion().size()).isEqualTo(4);
+    assertThat(remoteRVV.getMemberToGCVersion().size()).isEqualTo(3);
+    verify(receivedRVV, never()).removeOldMembers(any());
+    verify(recoveredRVV, never()).removeOldMembers(any());
+    verify(remoteRVV, never()).removeOldMembers(any());
+  }
 }

Reply via email to