/* * 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.partitioned; import static org.apache.geode.test.dunit.Assert.*; import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.InetAddress; import java.util.List; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import org.apache.geode.admin.AdminDistributedSystem; import org.apache.geode.admin.AdminDistributedSystemFactory; import org.apache.geode.admin.AdminException; import org.apache.geode.admin.BackupStatus; import org.apache.geode.admin.DistributedSystemConfig; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.Cache; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.DiskStore; import org.apache.geode.cache.PartitionAttributesFactory; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.control.RebalanceFactory; import org.apache.geode.cache.partition.PartitionRegionHelper; import org.apache.geode.cache.partition.PartitionRegionInfo; import org.apache.geode.cache.persistence.PersistentID; import org.apache.geode.distributed.internal.membership.InternalDistributedMember; import org.apache.geode.internal.FileUtil; import org.apache.geode.internal.cache.DiskRegion; import org.apache.geode.internal.cache.GemFireCacheImpl; import org.apache.geode.internal.cache.PartitionedRegion; import org.apache.geode.internal.cache.PartitionedRegionDataStore; import org.apache.geode.internal.cache.control.InternalResourceManager; import org.apache.geode.internal.cache.control.InternalResourceManager.ResourceObserver; import org.apache.geode.internal.cache.persistence.PersistenceAdvisor; import org.apache.geode.internal.cache.persistence.PersistentMemberID; import org.apache.geode.test.dunit.Assert; import org.apache.geode.test.dunit.AsyncInvocation; import org.apache.geode.test.dunit.Invoke; import org.apache.geode.test.dunit.LogWriterUtils; import org.apache.geode.test.dunit.SerializableCallable; import org.apache.geode.test.dunit.SerializableRunnable; import org.apache.geode.test.dunit.VM; import org.apache.geode.test.dunit.Wait; import org.apache.geode.test.dunit.WaitCriterion; import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase; public abstract class PersistentPartitionedRegionTestBase extends JUnit4CacheTestCase { public static String PR_REGION_NAME = "region"; // This must be bigger than the dunit ack-wait-threshold for the revoke // tests. The command line is setting the ack-wait-threshold to be // 60 seconds. private static final int MAX_WAIT = 70 * 1000; /* * (non-Javadoc) Set the region name for this test so that multiple subclasses of this test base * do not conflict with one another during parallel dunit runs * * @see dunit.DistributedTestCase#setUp() */ @Override public final void postSetUp() throws Exception { disconnectAllFromDS(); Invoke.invokeInEveryVM(PersistentPartitionedRegionTestBase.class, "setRegionName", new Object[] {getUniqueName()}); setRegionName(getUniqueName()); postSetUpPersistentPartitionedRegionTestBase(); } protected void postSetUpPersistentPartitionedRegionTestBase() throws Exception {} public static void setRegionName(String testName) { PR_REGION_NAME = testName + "Region"; } protected void checkRecoveredFromDisk(VM vm, final int bucketId, final boolean recoveredLocally) { vm.invoke(new SerializableRunnable("check recovered from disk") { public void run() { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion(PR_REGION_NAME); DiskRegion disk = region.getRegionAdvisor().getBucket(bucketId).getDiskRegion(); if (recoveredLocally) { assertEquals(0, disk.getStats().getRemoteInitializations()); assertEquals(1, disk.getStats().getLocalInitializations()); } else { assertEquals(1, disk.getStats().getRemoteInitializations()); assertEquals(0, disk.getStats().getLocalInitializations()); } } }); } protected void fakeCleanShutdown(VM vm, final int bucketId) { vm.invoke(new SerializableRunnable("mark clean") { public void run() { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion(PR_REGION_NAME); DiskRegion disk = region.getRegionAdvisor().getBucket(bucketId).getDiskRegion(); for (PersistentMemberID id : disk.getOnlineMembers()) { disk.memberOfflineAndEqual(id); } for (PersistentMemberID id : disk.getOfflineMembers()) { disk.memberOfflineAndEqual(id); } cache.close(); } }); } private PersistentMemberID getPersistentID(VM vm, final int bucketId) { Object id = vm.invoke(new SerializableCallable("get bucket persistent id") { public Object call() { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion(PR_REGION_NAME); PersistenceAdvisor advisor = region.getRegionAdvisor().getBucket(bucketId).getPersistenceAdvisor(); return advisor.getPersistentID(); } }); return (PersistentMemberID) id; } private void forceRecovery(VM vm) { vm.invoke(new SerializableRunnable("force recovery") { public void run() { Cache cache = getCache(); RebalanceFactory rf = cache.getResourceManager().createRebalanceFactory(); try { rf.start().getResults(); } catch (Exception e) { Assert.fail("interupted", e); } } }); } protected void checkData(VM vm0, final int startKey, final int endKey, final String value) { checkData(vm0, startKey, endKey, value, PR_REGION_NAME); } protected void checkData(VM vm0, final int startKey, final int endKey, final String value, final String regionName) { SerializableRunnable checkData = new SerializableRunnable("CheckData") { public void run() { Cache cache = getCache(); Region region = cache.getRegion(regionName); for (int i = startKey; i < endKey; i++) { assertEquals("For key " + i, value, region.get(i)); } } }; vm0.invoke(checkData); } protected void removeData(VM vm, final int startKey, final int endKey) { SerializableRunnable createData = new SerializableRunnable() { public void run() { Cache cache = getCache(); Region region = cache.getRegion(PR_REGION_NAME); for (int i = startKey; i < endKey; i++) { region.destroy(i); } } }; vm.invoke(createData); } protected void createData(VM vm, final int startKey, final int endKey, final String value) { LogWriterUtils.getLogWriter().info("createData invoked. PR_REGION_NAME is " + PR_REGION_NAME); createData(vm, startKey, endKey, value, PR_REGION_NAME); } protected void createData(VM vm, final int startKey, final int endKey, final String value, final String regionName) { SerializableRunnable createData = new SerializableRunnable("createData") { public void run() { Cache cache = getCache(); LogWriterUtils.getLogWriter().info("creating data in " + regionName); Region region = cache.getRegion(regionName); for (int i = startKey; i < endKey; i++) { region.put(i, value); } } }; vm.invoke(createData); } protected void closeCache(VM vm0) { SerializableRunnable close = new SerializableRunnable("Close Cache") { public void run() { Cache cache = getCache(); cache.close(); } }; vm0.invoke(close); } protected AsyncInvocation closeCacheAsync(VM vm0) { SerializableRunnable close = new SerializableRunnable() { public void run() { Cache cache = getCache(); cache.close(); } }; return vm0.invokeAsync(close); } protected void closePR(VM vm0) { closePR(vm0, PR_REGION_NAME); } protected void closePR(VM vm0, String regionName) { SerializableRunnable close = new SerializableRunnable("Close PR") { public void run() { Cache cache = getCache(); Region region = cache.getRegion(regionName); region.close(); } }; vm0.invoke(close); } protected void destroyPR(VM vm0) { destroyPR(vm0, PR_REGION_NAME); } protected void destroyPR(VM vm0, String regionName) { SerializableRunnable destroy = new SerializableRunnable("Destroy PR") { public void run() { Cache cache = getCache(); Region region = cache.getRegion(regionName); region.localDestroyRegion(); } }; vm0.invoke(destroy); } protected void localDestroyPR(VM vm0) { SerializableRunnable destroyPR = new SerializableRunnable("destroy pr") { public void run() { Cache cache = getCache(); Region region = cache.getRegion(PR_REGION_NAME); region.localDestroyRegion(); } }; vm0.invoke(destroyPR); } protected void createPR(VM vm0, final int redundancy, final int recoveryDelay, int numBuckets) { SerializableRunnable createPR = getCreatePRRunnable(redundancy, recoveryDelay, numBuckets); vm0.invoke(createPR); } protected void createPR(VM vm0, final int redundancy, final int recoveryDelay, int numBuckets, boolean synchronous) { SerializableRunnable createPR = getCreatePRRunnable(redundancy, recoveryDelay, numBuckets, synchronous); vm0.invoke(createPR); } protected void createPR(VM vm0, final int redundancy, final int recoveryDelay) { SerializableRunnable createPR = getCreatePRRunnable(redundancy, recoveryDelay); vm0.invoke(createPR); } protected void createPR(VM vm0, final int redundancy) { SerializableRunnable createPR = getCreatePRRunnable(redundancy, -1); vm0.invoke(createPR); } protected void createNestedPR(VM vm) { SerializableRunnable createPR = getNestedPRRunnable(); vm.invoke(createPR); } protected AsyncInvocation createNestedPRAsync(VM vm) { SerializableRunnable createPR = getNestedPRRunnable(); return vm.invokeAsync(createPR); } private SerializableRunnable getNestedPRRunnable() { SerializableRunnable createPR = new SerializableRunnable("create pr") { public void run() { Cache cache = getCache(); // Wait for both nested PRs to be created final CountDownLatch recoveryDone = new CountDownLatch(2); ResourceObserver observer = new InternalResourceManager.ResourceObserverAdapter() { @Override public void recoveryFinished(Region region) { recoveryDone.countDown(); } }; InternalResourceManager.setResourceObserver(observer); DiskStore ds = cache.findDiskStore("disk"); if (ds == null) { ds = cache.createDiskStoreFactory().setDiskDirs(getDiskDirs()).create("disk"); } Region parent1; { AttributesFactory af = new AttributesFactory(); af.setDataPolicy(DataPolicy.REPLICATE); parent1 = cache.createRegion("parent1", af.create()); } Region parent2; { AttributesFactory af = new AttributesFactory(); af.setDataPolicy(DataPolicy.REPLICATE); parent2 = cache.createRegion("parent2", af.create()); } { AttributesFactory af = new AttributesFactory(); PartitionAttributesFactory paf = new PartitionAttributesFactory(); paf.setRedundantCopies(1); af.setPartitionAttributes(paf.create()); af.setDataPolicy(DataPolicy.PERSISTENT_PARTITION); af.setDiskStoreName("disk"); parent1.createSubregion(PR_REGION_NAME, af.create()); } { AttributesFactory af = new AttributesFactory(); PartitionAttributesFactory paf = new PartitionAttributesFactory(); paf.setRedundantCopies(1); af.setPartitionAttributes(paf.create()); af.setDataPolicy(DataPolicy.PERSISTENT_PARTITION); af.setDiskStoreName("disk"); parent2.createSubregion(PR_REGION_NAME, af.create()); } try { recoveryDone.await(MAX_WAIT, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Assert.fail("interrupted", e); } } }; return createPR; } private SerializableRunnable getCreatePRRunnable(final int redundancy, final int recoveryDelay) { return getCreatePRRunnable(redundancy, recoveryDelay, 113); } private SerializableRunnable getCreatePRRunnable(final int redundancy, final int recoveryDelay, final int numBuckets) { return getCreatePRRunnable(redundancy, recoveryDelay, numBuckets, true); } private SerializableRunnable getCreatePRRunnable(final int redundancy, final int recoveryDelay, final int numBuckets, final boolean synchronous) { SerializableRunnable createPR = new SerializableRunnable("create pr") { public void run() { final CountDownLatch recoveryDone; if (redundancy > 0) { recoveryDone = new CountDownLatch(1); ResourceObserver observer = new InternalResourceManager.ResourceObserverAdapter() { @Override public void recoveryFinished(Region region) { recoveryDone.countDown(); } }; InternalResourceManager.setResourceObserver(observer); } else { recoveryDone = null; } Cache cache = getCache(); RegionAttributes attr = getPersistentPRAttributes(redundancy, recoveryDelay, cache, numBuckets, synchronous); cache.createRegion(PR_REGION_NAME, attr); if (recoveryDone != null) { try { recoveryDone.await(); } catch (InterruptedException e) { Assert.fail("Interrupted", e); } } } }; return createPR; } protected RegionAttributes getPersistentPRAttributes(final int redundancy, final int recoveryDelay, Cache cache, int numBuckets, boolean synchronous) { DiskStore ds = cache.findDiskStore("disk"); if (ds == null) { ds = cache.createDiskStoreFactory().setDiskDirs(getDiskDirs()).create("disk"); } AttributesFactory af = new AttributesFactory(); PartitionAttributesFactory paf = new PartitionAttributesFactory(); paf.setRedundantCopies(redundancy); paf.setRecoveryDelay(recoveryDelay); paf.setTotalNumBuckets(numBuckets); // Make sure all vms end up with the same local max memory paf.setLocalMaxMemory(500); af.setPartitionAttributes(paf.create()); af.setDataPolicy(DataPolicy.PERSISTENT_PARTITION); af.setDiskStoreName("disk"); af.setDiskSynchronous(synchronous); RegionAttributes attr = af.create(); return attr; } protected AsyncInvocation createPRAsync(VM vm0, final int redundancy, int recoveryDelay, int numBuckets) { SerializableRunnable createPR = getCreatePRRunnable(redundancy, recoveryDelay, numBuckets); return vm0.invokeAsync(createPR); } protected AsyncInvocation createPRAsync(VM vm0, final int redundancy) { SerializableRunnable createPR = getCreatePRRunnable(redundancy, -1); return vm0.invokeAsync(createPR); } protected Set<Integer> getBucketList(VM vm0) { return getBucketList(vm0, PR_REGION_NAME); } protected Set<Integer> getBucketList(VM vm0, final String regionName) { SerializableCallable getBuckets = new SerializableCallable("get buckets") { public Object call() throws Exception { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion(regionName); return new TreeSet<Integer>(region.getDataStore().getAllLocalBucketIds()); } }; return (Set<Integer>) vm0.invoke(getBuckets); } protected void waitForBuckets(VM vm, final Set<Integer> expectedBuckets, final String regionName) { SerializableCallable getBuckets = new SerializableCallable("get buckets") { public Object call() throws Exception { Cache cache = getCache(); final PartitionedRegion region = (PartitionedRegion) cache.getRegion(regionName); Wait.waitForCriterion(new WaitCriterion() { public boolean done() { return expectedBuckets.equals(getActualBuckets()); } public String description() { return "Buckets on vm " + getActualBuckets() + " never became equal to expected " + expectedBuckets; } public TreeSet<Integer> getActualBuckets() { return new TreeSet<Integer>(region.getDataStore().getAllLocalBucketIds()); } }, 30 * 1000, 100, true); return null; } }; vm.invoke(getBuckets); } protected Set<Integer> getPrimaryBucketList(VM vm0) { return getPrimaryBucketList(vm0, PR_REGION_NAME); } protected Set<Integer> getPrimaryBucketList(VM vm0, final String regionName) { SerializableCallable getPrimaryBuckets = new SerializableCallable("get primary buckets") { public Object call() throws Exception { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion(regionName); return new TreeSet<Integer>(region.getDataStore().getAllLocalPrimaryBucketIds()); } }; return (Set<Integer>) vm0.invoke(getPrimaryBuckets); } protected void revokeKnownMissingMembers(VM vm2, final int numExpectedMissing) { vm2.invoke(new SerializableRunnable("Revoke the member") { public void run() { final DistributedSystemConfig config; final AdminDistributedSystem adminDS; try { config = AdminDistributedSystemFactory.defineDistributedSystem(getSystem(), ""); adminDS = AdminDistributedSystemFactory.getDistributedSystem(config); adminDS.connect(); adminDS.waitToBeConnected(MAX_WAIT); try { final WaitCriterion wc = new WaitCriterion() { public boolean done() { try { final Set<PersistentID> missingIds = adminDS.getMissingPersistentMembers(); if (missingIds.size() != numExpectedMissing) { return false; } for (PersistentID missingId : missingIds) { adminDS.revokePersistentMember(missingId.getUUID()); } return true; } catch (AdminException ae) { throw new RuntimeException(ae); } } public String description() { try { return "expected " + numExpectedMissing + " missing members for revocation, current: " + adminDS.getMissingPersistentMembers(); } catch (AdminException ae) { throw new RuntimeException(ae); } } }; Wait.waitForCriterion(wc, MAX_WAIT, 500, true); } finally { adminDS.disconnect(); } } catch (Exception e) { throw new RuntimeException(e); } } }); } protected void revokeAllMembers(VM vm) { vm.invoke(new SerializableRunnable("Revoke the member") { public void run() { GemFireCacheImpl cache = (GemFireCacheImpl) getCache(); DistributedSystemConfig config; AdminDistributedSystem adminDS = null; try { config = AdminDistributedSystemFactory.defineDistributedSystem(getSystem(), ""); adminDS = AdminDistributedSystemFactory.getDistributedSystem(config); adminDS.connect(); adminDS.waitToBeConnected(MAX_WAIT); adminDS.revokePersistentMember(InetAddress.getLocalHost(), null); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } finally { if (adminDS != null) { adminDS.disconnect(); } } } }); } protected void revokeMember(VM vm, final File directory) { vm.invoke(new SerializableRunnable("Revoke the member") { public void run() { GemFireCacheImpl cache = (GemFireCacheImpl) getCache(); DistributedSystemConfig config; AdminDistributedSystem adminDS = null; try { config = AdminDistributedSystemFactory.defineDistributedSystem(getSystem(), ""); adminDS = AdminDistributedSystemFactory.getDistributedSystem(config); adminDS.connect(); adminDS.waitToBeConnected(MAX_WAIT); adminDS.revokePersistentMember(InetAddress.getLocalHost(), directory.getCanonicalPath()); } catch (Exception e) { throw new RuntimeException(e); } finally { if (adminDS != null) { adminDS.disconnect(); } } } }); } protected boolean moveBucket(final int bucketId, VM source, VM target) { SerializableCallable getId = new SerializableCallable("Get Id") { public Object call() throws Exception { Cache cache = getCache(); return cache.getDistributedSystem().getDistributedMember(); } }; final InternalDistributedMember sourceId = (InternalDistributedMember) source.invoke(getId); SerializableCallable move = new SerializableCallable("move bucket") { public Object call() { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion(PR_REGION_NAME); return region.getDataStore().moveBucket(bucketId, sourceId, false); } }; return (Boolean) target.invoke(move); } protected Set<PersistentMemberID> getOfflineMembers(final int bucketId, VM vm) { SerializableCallable getId = new SerializableCallable("Get Id") { public Object call() throws Exception { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion(PR_REGION_NAME); return region.getRegionAdvisor().getProxyBucketArray()[bucketId].getPersistenceAdvisor() .getMembershipView().getOfflineMembers(); } }; return (Set<PersistentMemberID>) vm.invoke(getId); } protected Set<PersistentMemberID> getOnlineMembers(final int bucketId, VM vm) { SerializableCallable getId = new SerializableCallable("Get Id") { public Object call() throws Exception { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion(PR_REGION_NAME); return region.getRegionAdvisor().getProxyBucketArray()[bucketId].getPersistenceAdvisor() .getPersistedOnlineOrEqualMembers(); } }; return (Set<PersistentMemberID>) vm.invoke(getId); } protected void waitForBucketRecovery(final VM vm2, final Set<Integer> lostBuckets) { waitForBucketRecovery(vm2, lostBuckets, PR_REGION_NAME); } protected void waitForBucketRecovery(final VM vm2, final Set<Integer> lostBuckets, final String regionName) { vm2.invoke(new SerializableRunnable() { public void run() { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion(regionName); final PartitionedRegionDataStore dataStore = region.getDataStore(); Wait.waitForCriterion(new WaitCriterion() { public boolean done() { Set<Integer> vm2Buckets = dataStore.getAllLocalBucketIds(); return lostBuckets.equals(vm2Buckets); } public String description() { return "expected to recover " + lostBuckets + " buckets, now have " + dataStore.getAllLocalBucketIds(); } }, MAX_WAIT, 100, true); } }); } protected void waitForRedundancyRecovery(VM vm, final int expectedRedundancy, final String regionName) { vm.invoke(new SerializableRunnable() { public void run() { Cache cache = getCache(); final Region region = cache.getRegion(regionName); Wait.waitForCriterion(new WaitCriterion() { public boolean done() { PartitionRegionInfo info = PartitionRegionHelper.getPartitionRegionInfo(region); return info.getActualRedundantCopies() == expectedRedundancy; } public String description() { PartitionRegionInfo info = PartitionRegionHelper.getPartitionRegionInfo(region); return "Did not reach expected redundancy " + expectedRedundancy + " redundancy info = " + info.getActualRedundantCopies(); } }, 30 * 1000, 100, true); } }); } protected void invalidateData(VM vm, final int startKey, final int endKey) { SerializableRunnable createData = new SerializableRunnable() { public void run() { Cache cache = getCache(); Region region = cache.getRegion(PR_REGION_NAME); for (int i = startKey; i < endKey; i++) { region.destroy(i); region.create(i, null); region.invalidate(i); } } }; vm.invoke(createData); } // used for above test protected BackupStatus backup(VM vm) { return (BackupStatus) vm.invoke(new SerializableCallable("Backup all members") { public Object call() { DistributedSystemConfig config; AdminDistributedSystem adminDS = null; try { config = AdminDistributedSystemFactory.defineDistributedSystem(getSystem(), ""); adminDS = AdminDistributedSystemFactory.getDistributedSystem(config); adminDS.connect(); adminDS.waitToBeConnected(MAX_WAIT); return adminDS.backupAllMembers(getBackupDir()); } catch (Exception e) { throw new RuntimeException(e); } finally { if (adminDS != null) { adminDS.disconnect(); } } } }); } protected void restoreBackup(int expectedNumScripts) throws IOException, InterruptedException { List<File> restoreScripts = FileUtil.findAll(getBackupDir(), ".*restore.*"); assertEquals("Restore scripts " + restoreScripts, expectedNumScripts, restoreScripts.size()); for (File script : restoreScripts) { execute(script); } } private void execute(File script) throws IOException, InterruptedException { ProcessBuilder pb = new ProcessBuilder(script.getAbsolutePath()); pb.redirectErrorStream(true); Process process = pb.start(); InputStream is = process.getInputStream(); byte[] buffer = new byte[1024]; BufferedReader br = new BufferedReader(new InputStreamReader(is)); String line; while ((line = br.readLine()) != null) { LogWriterUtils.getLogWriter().fine("OUTPUT:" + line); // TODO validate output } ; assertEquals(0, process.waitFor()); } protected static File getBackupDir() { File tmpDir = new File(System.getProperty("java.io.tmpdir")); File dir = new File(tmpDir, "backupDir"); dir.mkdirs(); return dir; } }