/* * 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.junit.Assert.*; import java.io.Serializable; import java.util.*; import org.junit.Test; import org.junit.experimental.categories.Category; import org.apache.geode.CancelException; import org.apache.geode.LogWriter; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.Cache; import org.apache.geode.cache.DataPolicy; import org.apache.geode.cache.PartitionAttributes; import org.apache.geode.cache.PartitionAttributesFactory; import org.apache.geode.cache.Region; import org.apache.geode.distributed.*; import org.apache.geode.distributed.internal.DistributionManager; import org.apache.geode.distributed.internal.DistributionMessage; import org.apache.geode.distributed.internal.DistributionMessageObserver; import org.apache.geode.distributed.internal.InternalDistributedSystem; import org.apache.geode.distributed.internal.membership.InternalDistributedMember; import org.apache.geode.internal.cache.ForceReattemptException; import org.apache.geode.internal.cache.PartitionedRegion; import org.apache.geode.internal.cache.PartitionedRegionDataStore; import org.apache.geode.test.dunit.Host; 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.cache.internal.JUnit4CacheTestCase; import org.apache.geode.test.junit.categories.DistributedTest; @Category(DistributedTest.class) public class Bug39356DUnitTest extends JUnit4CacheTestCase { protected static final String REGION_NAME = "myregion"; @Override public Properties getDistributedSystemProperties() { Properties result = super.getDistributedSystemProperties(); result.put(ConfigurationProperties.ENABLE_NETWORK_PARTITION_DETECTION, "false"); return result; } /** * This tests the case where the VM forcing other VMs to create a bucket crashes while creating * the bucket. */ @Test public void testCrashWhileCreatingABucket() { Host host = Host.getHost(0); final VM vm0 = host.getVM(0); final VM vm1 = host.getVM(1); final VM vm2 = host.getVM(2); SerializableRunnable createParReg = new SerializableRunnable("Create parReg") { public void run() { DistributionMessageObserver.setInstance(new MyRegionObserver(vm0)); Cache cache = getCache(); AttributesFactory af = new AttributesFactory(); PartitionAttributesFactory pf = new PartitionAttributesFactory(); pf.setRedundantCopies(1); pf.setRecoveryDelay(0); af.setDataPolicy(DataPolicy.PARTITION); af.setPartitionAttributes(pf.create()); cache.createRegion(REGION_NAME, af.create()); } }; vm1.invoke(createParReg); vm2.invoke(createParReg); SerializableRunnable createParRegAccessor = new SerializableRunnable("Create parReg") { public void run() { Cache cache = getCache(); AttributesFactory af = new AttributesFactory(); PartitionAttributesFactory pf = new PartitionAttributesFactory(); pf.setRedundantCopies(1); pf.setLocalMaxMemory(0); af.setDataPolicy(DataPolicy.PARTITION); af.setPartitionAttributes(pf.create()); Region r = cache.createRegion(REGION_NAME, af.create()); // trigger the creation of a bucket, which should trigger the destruction of this VM. try { r.put("ping", "pong"); fail("Should have gotten a CancelException"); } catch (CancelException e) { // this is ok, we expect our observer to close this cache. } } }; vm0.invoke(createParRegAccessor); SerializableRunnable verifyBuckets = new SerializableRunnable("Verify buckets") { public void run() { LogWriter log = org.apache.geode.test.dunit.LogWriterUtils.getLogWriter(); Cache cache = getCache(); PartitionedRegion r = (PartitionedRegion) cache.getRegion(REGION_NAME); for (int i = 0; i < r.getAttributes().getPartitionAttributes().getTotalNumBuckets(); i++) { List owners = null; while (owners == null) { try { owners = r.getBucketOwnersForValidation(i); } catch (ForceReattemptException e) { log.info( Bug39356DUnitTest.class + " verify buckets Caught a ForceReattemptException"); Wait.pause(1000); } } if (owners.isEmpty()) { log.info("skipping bucket " + i + " because it has no data"); continue; } assertEquals("Expecting bucket " + i + " to have two copies", 2, owners.size()); log.info("bucket " + i + " had two copies"); } } }; vm1.invoke(verifyBuckets); vm2.invoke(verifyBuckets); } protected final class MyRegionObserver extends DistributionMessageObserver implements Serializable { private final VM vm0; /** * @param vm0 */ MyRegionObserver(VM vm0) { this.vm0 = vm0; } public void afterProcessMessage(DistributionManager dm, DistributionMessage message) {} public void beforeProcessMessage(DistributionManager dm, DistributionMessage message) { if (message instanceof ManageBucketMessage) { vm0.invoke(new SerializableRunnable("Disconnect VM 0") { public void run() { disconnectFromDS(); try { Thread.sleep(10000); } catch (InterruptedException e) { fail("interrupted"); } } }); } } } /** * A test to make sure that we cannot move a bucket to a member which already hosts the bucket, * thereby reducing our redundancy. */ @Test public void testMoveBucketToHostThatHasTheBucketAlready() { Host host = Host.getHost(0); VM vm0 = host.getVM(0); VM vm1 = host.getVM(1); SerializableRunnable createPrRegion = new SerializableRunnable("createRegion") { public void run() { Cache cache = getCache(); AttributesFactory attr = new AttributesFactory(); PartitionAttributesFactory paf = new PartitionAttributesFactory(); paf.setRedundantCopies(1); paf.setRecoveryDelay(-1); paf.setStartupRecoveryDelay(-1); PartitionAttributes prAttr = paf.create(); attr.setPartitionAttributes(prAttr); cache.createRegion("region1", attr.create()); } }; vm0.invoke(createPrRegion); vm1.invoke(createPrRegion); // Create a bucket vm0.invoke(new SerializableRunnable("createSomeBuckets") { public void run() { Cache cache = getCache(); Region region = cache.getRegion("region1"); region.put(Integer.valueOf(0), "A"); } }); final InternalDistributedMember vm1MemberId = (InternalDistributedMember) vm1.invoke(new SerializableCallable() { public Object call() throws Exception { return InternalDistributedSystem.getAnyInstance().getDistributedMember(); } }); // Move the bucket vm0.invoke(new SerializableRunnable("moveBucket") { public void run() { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion("region1"); Set<InternalDistributedMember> owners = region.getRegionAdvisor().getBucketOwners(0); assertEquals(2, owners.size()); PartitionedRegionDataStore ds = region.getDataStore(); assertTrue(ds.isManagingBucket(0)); // try to move the bucket from the other member to this one. This should // fail because we already have the bucket assertFalse(ds.moveBucket(0, vm1MemberId, true)); assertEquals(owners, region.getRegionAdvisor().getBucketOwners(0)); } }); } }