/* * 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.versions; import static org.junit.Assert.*; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.util.HashSet; import java.util.Map; import java.util.Set; import org.junit.Test; import org.junit.experimental.categories.Category; import org.apache.geode.DataSerializer; import org.apache.geode.distributed.internal.membership.InternalDistributedMember; import org.apache.geode.internal.HeapDataOutputStream; import org.apache.geode.internal.InternalDataSerializer; import org.apache.geode.internal.Version; import org.apache.geode.internal.cache.persistence.DiskStoreID; import org.apache.geode.test.dunit.NetworkUtils; import org.apache.geode.test.junit.categories.UnitTest; @Category(UnitTest.class) public class RegionVersionVectorJUnitTest { @Test public void testExceptionsWithContains() { DiskStoreID ownerId = new DiskStoreID(0, 0); DiskStoreID id1 = new DiskStoreID(0, 1); DiskStoreID id2 = new DiskStoreID(1, 0); DiskRegionVersionVector rvv = new DiskRegionVersionVector(ownerId); doExceptionsWithContains(ownerId, rvv); doExceptionsWithContains(id1, rvv); } @SuppressWarnings({"unchecked", "rawtypes"}) @Test public void testRegionVersionVectors() throws Exception { // this is just a quick set of unit tests for basic RVV functionality final String local = NetworkUtils.getIPLiteral(); InternalDistributedMember server1 = new InternalDistributedMember(local, 101); InternalDistributedMember server2 = new InternalDistributedMember(local, 102); InternalDistributedMember server3 = new InternalDistributedMember(local, 103); InternalDistributedMember server4 = new InternalDistributedMember(local, 104); RegionVersionVector rv1 = null; // (a) Test that an exception is mended when the versions that are missing are // added rv1 = new VMRegionVersionVector(server1); rv1.recordVersion(server2, 1); rv1.recordVersion(server2, 5); rv1.recordVersion(server2, 8); System.out.println("for test (a) formed this RVV: " + rv1.fullToString()); // there should now be two exceptions: 1-5 and 5-8 assertEquals(8, rv1.getVersionForMember(server2)); assertEquals(2, rv1.getExceptionCount(server2)); rv1.recordVersion(server2, 3); System.out.println("for test (a) RVV is now: " + rv1.fullToString()); assertEquals(8, rv1.getVersionForMember(server2)); assertEquals(2, rv1.getExceptionCount(server2)); rv1.recordVersion(server2, 4); rv1.recordVersion(server2, 2); System.out.println("for test (a) RVV is now: " + rv1.fullToString()); assertEquals(1, rv1.getExceptionCount(server2)); rv1.recordVersion(server2, 6); rv1.recordVersion(server2, 7); System.out.println("for test (a) RVV is now: " + rv1.fullToString()); assertEquals(0, rv1.getExceptionCount(server2)); // (b) Test the contains() operation rv1 = new VMRegionVersionVector(server1); rv1.recordVersion(server2, 1); rv1.recordVersion(server2, 5); rv1.recordVersion(server2, 8); rv1.recordVersion(server2, 10); System.out.println("for test (b) formed this RVV: " + rv1.fullToString()); assertTrue(rv1.contains(server2, 1)); assertTrue(rv1.contains(server2, 5)); assertTrue(rv1.contains(server2, 8)); assertTrue(rv1.contains(server2, 10)); assertFalse(rv1.contains(server2, 2)); assertFalse(rv1.contains(server2, 3)); assertFalse(rv1.contains(server2, 4)); assertFalse(rv1.contains(server2, 9)); assertFalse(rv1.contains(server2, 11)); rv1.recordVersion(server2, 3); System.out.println("for test (b) RVV is now: " + rv1.fullToString()); assertTrue(rv1.contains(server2, 3)); rv1.recordVersion(server2, 2); System.out.println("for test (b) RVV is now: " + rv1.fullToString()); assertTrue(rv1.contains(server2, 2)); assertTrue(rv1.contains(server2, 3)); rv1.recordVersion(server2, 4); System.out.println("for test (b) RVV is now: " + rv1.fullToString()); assertTrue(rv1.contains(server2, 1)); assertTrue(rv1.contains(server2, 2)); assertTrue(rv1.contains(server2, 3)); assertTrue(rv1.contains(server2, 4)); assertTrue(rv1.contains(server2, 5)); rv1.recordVersion(server2, 11); System.out.println("for test (b) RVV is now: " + rv1.fullToString()); assertTrue(rv1.contains(server2, 11)); assertTrue(rv1.contains(server2, 10)); System.out.println("for test (b) RVV is now: " + rv1.fullToString()); rv1.recordVersion(server2, 6); assertTrue(rv1.contains(server2, 2)); assertTrue(rv1.contains(server2, 5)); assertTrue(rv1.contains(server2, 6)); assertFalse(rv1.contains(server2, 7)); assertTrue(rv1.contains(server2, 8)); rv1.recordVersion(server2, 7); System.out.println("for test (b) RVV is now: " + rv1.fullToString()); assertTrue(rv1.contains(server2, 7)); rv1.recordVersion(server2, 9); System.out.println("for test (b) RVV is now: " + rv1.fullToString()); assertTrue(rv1.contains(server2, 9)); assertTrue(rv1.getExceptionCount(server2) == 0); assertTrue(rv1.contains(server2, 8)); // Test RVV comparisons for GII Delta rv1 = new VMRegionVersionVector(server1); rv1.recordVersion(server2, 1); rv1.recordVersion(server2, 4); rv1.recordVersion(server2, 8); rv1.recordVersion(server2, 9); rv1.recordVersion(server2, 10); rv1.recordVersion(server2, 11); rv1.recordVersion(server2, 12); rv1.recordVersion(server3, 2); rv1.recordVersion(server3, 3); rv1.recordVersion(server3, 4); rv1.recordVersion(server3, 6); rv1.recordVersion(server3, 7); RegionVersionVector rv2 = rv1.getCloneForTransmission(); System.out.println("rv1 is " + rv1.fullToString()); System.out.println("rv2 is " + rv2.fullToString()); assertFalse(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); assertFalse(rv2.isNewerThanOrCanFillExceptionsFor(rv1)); rv1.recordVersion(server2, 6); assertTrue(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); rv2.recordVersion(server2, 6); assertFalse(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); // fill an exception gap rv1.recordVersion(server2, 5); assertTrue(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); rv2.recordVersion(server2, 5); assertFalse(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); rv1.recordVersion(server2, 7); assertTrue(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); rv2.recordVersion(server2, 7); assertFalse(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); // add a more recent revision rv1.recordVersion(server3, 8); assertTrue(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); rv2.recordVersion(server3, 8); assertFalse(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); // fill another exception gap rv1.recordVersion(server3, 5); assertTrue(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); rv2.recordVersion(server3, 5); assertFalse(rv1.isNewerThanOrCanFillExceptionsFor(rv2)); // test that old members are removed from the vector InternalDistributedMember server5 = new InternalDistributedMember(local, 105); rv1 = new VMRegionVersionVector(server1); rv1.recordVersion(server2, 1); rv1.recordVersion(server3, 1); rv1.recordVersion(server4, 1); rv1.recordVersion(server5, 1); rv1.memberDeparted(server2, false); rv1.memberDeparted(server4, true); assertTrue(rv1.containsMember(server2)); assertTrue(rv1.containsMember(server3)); assertTrue(rv1.containsMember(server4)); Set retain = new HashSet(); retain.add(server2); // still have data from server2 retain.add(server3); // still have data from server3 // no data found from server4 in region retain.add(server5); // still have data from server5 rv1.removeOldMembers(retain); assertFalse(rv1.containsMember(server4)); rv1.memberDeparted(server3, false); // {server2, server3(departed), server5} // Now test that departed members are transferred with GII. We simulate // a new server, server6, doing a GII from server1 InternalDistributedMember server6 = new InternalDistributedMember(local, 106); RegionVersionVector giiReceiverRVV = new VMRegionVersionVector(server6); // the gii request will cause server1 to clone its RVV and send it to server6 rv2 = rv1.getCloneForTransmission(); // serialize/deserialize to mimic sending the rvv in a message ByteArrayOutputStream baos = new ByteArrayOutputStream(1000); DataOutputStream out = new DataOutputStream(baos); DataSerializer.writeObject(rv2, out); ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); DataInputStream in = new DataInputStream(bais); RegionVersionVector transmittedVector = (RegionVersionVector) DataSerializer.readObject(in); // record the provider's rvv in the receiver of the image giiReceiverRVV.recordVersions(transmittedVector); // removedMembers in the receiver should hold {server4, server3}. Simulate // another member departure to kick out server4 assertTrue(giiReceiverRVV.containsMember(server2)); assertTrue(giiReceiverRVV.containsMember(server5)); assertTrue(giiReceiverRVV.containsMember(server3)); assertTrue(giiReceiverRVV.isDepartedMember(server3)); // unit test for bit-set boundary. First boundary is 3/4 of bitset width, // which is the amount dumped to the exceptions list when the bitset becomes full rv1 = new VMRegionVersionVector(server1); long bitSetRollPoint = RegionVersionHolder.BIT_SET_WIDTH + 1; long boundary = RegionVersionHolder.BIT_SET_WIDTH * 3 / 4; for (long i = 1; i < boundary; i++) { rv1.recordVersion(server2, i); assertTrue(rv1.contains(server2, i)); } assertFalse(rv1.contains(server2, boundary + 1)); RegionVersionVector.DEBUG = true; rv1.recordVersion(server2, bitSetRollPoint); rv1.recordVersion(server2, bitSetRollPoint + 1); // bitSet should be rolled at this point RegionVersionHolder h = (RegionVersionHolder) rv1.getMemberToVersion().get(server2); long versionBoundary = h.getBitSetVersionForTesting(); assertEquals("expected holder bitset version to roll to this value", boundary - 1, versionBoundary); assertFalse(rv1.contains(server2, bitSetRollPoint - 1)); assertTrue(rv1.contains(server2, bitSetRollPoint)); assertTrue(rv1.contains(server2, bitSetRollPoint + 1)); assertFalse(rv1.contains(server2, bitSetRollPoint + 2)); assertTrue(rv1.contains(server2, boundary - 1)); assertFalse(rv1.contains(server2, boundary)); assertFalse(rv1.contains(server2, boundary + 1)); // now test the merge System.out.println("testing merge for " + rv1.fullToString()); assertEquals(1, rv1.getExceptionCount(server2)); // one exception from boundary-1 to // bitSetRollPoint assertFalse(rv1.contains(server2, bitSetRollPoint - 1)); assertTrue(rv1.contains(server2, bitSetRollPoint)); assertTrue(rv1.contains(server2, bitSetRollPoint + 1)); assertFalse(rv1.contains(server2, bitSetRollPoint + 2)); } @Test public void testRVVSerialization() throws Exception { DiskStoreID ownerId = new DiskStoreID(0, 0); DiskStoreID id1 = new DiskStoreID(0, 1); DiskStoreID id2 = new DiskStoreID(1, 0); DiskRegionVersionVector rvv = new DiskRegionVersionVector(ownerId); rvv.recordVersion(id1, 5); rvv.recordVersion(id1, 6); rvv.recordVersion(id1, 7); rvv.recordVersion(id1, 9); rvv.recordVersion(id1, 20); rvv.recordVersion(id1, 11); rvv.recordVersion(id1, 12); rvv.recordGCVersion(id2, 5); rvv.recordGCVersion(id1, 3); assertTrue(rvv.sameAs(rvv.getCloneForTransmission())); HeapDataOutputStream out = new HeapDataOutputStream(Version.CURRENT); InternalDataSerializer.writeObject(rvv.getCloneForTransmission(), out); byte[] bytes = out.toByteArray(); DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes)); DiskRegionVersionVector rvv2 = InternalDataSerializer.readObject(dis); assertTrue(rvv.sameAs(rvv2)); } /** * Test that we can copy the member to version map correctly. */ @Test public void testCopyMemberToVersion() { DiskStoreID id0 = new DiskStoreID(0, 0); DiskStoreID id1 = new DiskStoreID(0, 1); DiskStoreID id2 = new DiskStoreID(1, 0); DiskRegionVersionVector rvv0 = new DiskRegionVersionVector(id0); rvv0.getNextVersion(); rvv0.getNextVersion(); rvv0.getNextVersion(); rvv0.recordVersion(id1, 1); rvv0.recordVersion(id1, 3); DiskRegionVersionVector rvv1 = new DiskRegionVersionVector(id1); rvv1.recordVersions(rvv0); assertEquals(3, rvv1.getCurrentVersion()); assertFalse(rvv1.contains(id1, 2)); assertTrue(rvv1.contains(id1, 1)); assertTrue(rvv1.contains(id1, 3)); assertTrue(rvv1.contains(id0, 3)); assertTrue(rvv0.sameAs(rvv1)); rvv1.recordVersion(id1, 2); assertTrue(rvv1.isNewerThanOrCanFillExceptionsFor(rvv0)); assertFalse(rvv0.isNewerThanOrCanFillExceptionsFor(rvv1)); assertTrue(rvv1.dominates(rvv0)); assertFalse(rvv0.dominates(rvv1)); } @Test public void testSpecialException() { DiskStoreID id0 = new DiskStoreID(0, 0); DiskStoreID id1 = new DiskStoreID(0, 1); DiskStoreID id2 = new DiskStoreID(1, 0); DiskRegionVersionVector rvv0 = new DiskRegionVersionVector(id0); rvv0.getNextVersion(); rvv0.getNextVersion(); rvv0.getNextVersion(); rvv0.recordVersion(id1, 1); rvv0.recordVersion(id1, 2); DiskRegionVersionVector rvv1 = new DiskRegionVersionVector(id1); rvv1.recordVersions(rvv0); rvv1.recordVersion(id1, 3); RegionVersionHolder holder_at_rvv1 = rvv1.getLocalExceptions(); RegionVersionHolder holder_at_rvv0 = rvv0.getMemberToVersion().get(id1); holder_at_rvv1.addException(2, 4); assertFalse(rvv1.isNewerThanOrCanFillExceptionsFor(rvv0)); assertFalse(rvv0.isNewerThanOrCanFillExceptionsFor(rvv1)); assertTrue(rvv1.dominates(rvv0)); assertTrue(rvv0.dominates(rvv1)); } @Test public void test48066_1() { DiskStoreID id0 = new DiskStoreID(0, 0); DiskRegionVersionVector rvv0 = new DiskRegionVersionVector(id0); for (int i = 1; i <= 3; i++) { rvv0.recordVersion(id0, i); } System.out.println("rvv0=" + rvv0.fullToString()); DiskRegionVersionVector rvv1 = (DiskRegionVersionVector) rvv0.getCloneForTransmission(); System.out.println("after clone, rvv1=" + rvv1.fullToString()); DiskRegionVersionVector rvv2 = new DiskRegionVersionVector(id0); for (int i = 1; i <= 10; i++) { rvv2.recordVersion(id0, i); } rvv2.recordVersions(rvv1); System.out.println("after init, rvv2=" + rvv2.fullToString()); rvv2.recordVersion(id0, 4); System.out.println("after record 4, rvv2=" + rvv2.fullToString()); assertEquals(4, rvv2.getCurrentVersion()); rvv2.recordVersion(id0, 7); System.out.println("after record 7, rvv2=" + rvv2.fullToString()); assertEquals(7, rvv2.getCurrentVersion()); } @Test public void test48066_2() { DiskStoreID id0 = new DiskStoreID(0, 0); DiskRegionVersionVector rvv0 = new DiskRegionVersionVector(id0); for (int i = 1; i <= 10; i++) { rvv0.recordVersion(id0, i); } DiskRegionVersionVector rvv1 = new DiskRegionVersionVector(id0); rvv0.recordVersions(rvv1); System.out.println("rvv0=" + rvv0.fullToString()); rvv0.recordVersion(id0, 4); System.out.println("after record 4, rvv0=" + rvv0.fullToString()); assertEquals(4, rvv0.getCurrentVersion()); rvv0.recordVersion(id0, 7); System.out.println("after record 7, rvv0=" + rvv0.fullToString()); assertEquals(7, rvv0.getCurrentVersion()); assertFalse(rvv0.contains(id0, 5)); DiskRegionVersionVector rvv2 = (DiskRegionVersionVector) rvv0.getCloneForTransmission(); System.out.println("after clone, rvv2=" + rvv2.fullToString()); assertEquals(11, rvv0.getNextVersion()); assertFalse(rvv2.contains(id0, 5)); assertEquals(11, rvv2.getNextVersion()); } /** * Test for bug 47023. Make sure recordGCVersion works correctly and doesn't generate exceptions * for the local member. */ @Test public void testRecordGCVersion() { DiskStoreID id0 = new DiskStoreID(0, 0); DiskStoreID id1 = new DiskStoreID(0, 1); DiskRegionVersionVector rvv0 = new DiskRegionVersionVector(id0); // generate 3 local versions rvv0.getNextVersion(); rvv0.getNextVersion(); rvv0.getNextVersion(); // record some version from a remote member rvv0.recordVersion(id1, 1); rvv0.recordVersion(id1, 3); rvv0.recordVersion(id1, 5); // Assert that the exceptions are present { Map<DiskStoreID, RegionVersionHolder<DiskStoreID>> memberToVersion = rvv0.getMemberToVersion(); RegionVersionHolder<DiskStoreID> holder1 = memberToVersion.get(id1); // Make sure the exceptions are present assertFalse(holder1.contains(2)); assertFalse(holder1.contains(4)); } // Record some GC versions rvv0.recordGCVersion(id0, 2); rvv0.recordGCVersion(id1, 3); { Map<DiskStoreID, RegionVersionHolder<DiskStoreID>> memberToVersion = rvv0.getMemberToVersion(); RegionVersionHolder<DiskStoreID> holder0 = memberToVersion.get(id0); // Make sure we didn't generate a bogus exception for // the local member by calling record GC version - bug 47023 assertTrue(holder0.getExceptionForTest().isEmpty()); } // Clean up old exceptions rvv0.pruneOldExceptions(); // Make assertions about what exceptions are still present Map<DiskStoreID, RegionVersionHolder<DiskStoreID>> memberToVersion = rvv0.getMemberToVersion(); RegionVersionHolder<DiskStoreID> holder0 = memberToVersion.get(id0); RegionVersionHolder<DiskStoreID> holder1 = memberToVersion.get(id1); assertTrue(holder0.getExceptionForTest().isEmpty()); // exceptions less than the GC version should have been removed assertTrue(holder1.contains(2)); // exceptions greater than the GC version should still be there. assertFalse(holder1.contains(4)); } @Test public void testRemoveOldVersions() { DiskStoreID id0 = new DiskStoreID(0, 0); DiskStoreID id1 = new DiskStoreID(0, 1); DiskStoreID id2 = new DiskStoreID(0, 2); DiskRegionVersionVector rvv = new DiskRegionVersionVector(id0); // generate 3 local versions rvv.getNextVersion(); rvv.getNextVersion(); rvv.getNextVersion(); // record some version from a remote member rvv.recordVersion(id1, 1); rvv.recordVersion(id1, 3); rvv.recordVersion(id1, 5); // record a GC version for that member that is older than its version rvv.recordGCVersion(id1, 3); rvv.recordGCVersion(id2, 4950); rvv.recordVersion(id2, 5000); rvv.recordVersion(id2, 5001); rvv.recordVersion(id2, 5005); rvv.removeOldVersions(); assertEquals("expected gc version to be set to current version for " + rvv.fullToString(), rvv.getCurrentVersion(), rvv.getGCVersion(null)); assertEquals("expected gc version to be set to current version for " + rvv.fullToString(), rvv.getVersionForMember(id1), rvv.getGCVersion(id1)); assertEquals("expected gc version to be set to current version for " + rvv.fullToString(), rvv.getVersionForMember(id2), rvv.getGCVersion(id2)); assertEquals("expected exceptions to be erased for " + rvv.fullToString(), rvv.getExceptionCount(id1), 0); assertEquals("expected exceptions to be erased for " + rvv.fullToString(), rvv.getExceptionCount(id2), 0); } @Test public void testRegionVersionInTags() { VMVersionTag tag = new VMVersionTag(); long version = 0x8080000000L; tag.setRegionVersion(version); assertEquals("failed test for bug #48576", version, tag.getRegionVersion()); } private void doExceptionsWithContains(DiskStoreID id, DiskRegionVersionVector rvv) { rvv.recordVersion(id, 10); // Make sure we have exceptions from 0-10 assertFalse(rvv.contains(id, 5)); rvv.recordVersion(id, 5); assertTrue(rvv.contains(id, 5)); assertFalse(rvv.contains(id, 4)); assertFalse(rvv.contains(id, 6)); for (int i = 0; i < 10; i++) { rvv.recordVersion(id, i); } for (int i = 0; i < 10; i++) { assertTrue(rvv.contains(id, i)); } assertEquals(0, rvv.getExceptionCount(id)); } }