| /* |
| * 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.distributed.ConfigurationProperties.SERIALIZABLE_OBJECT_FILTER; |
| import static org.apache.geode.internal.cache.PartitionedRegionGetSomeKeys.getSomeKeys; |
| import static org.apache.geode.test.dunit.Host.getHost; |
| import static org.assertj.core.api.Assertions.assertThat; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import java.io.DataInput; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.io.Serializable; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.Random; |
| import java.util.Set; |
| |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import org.apache.geode.DataSerializable; |
| import org.apache.geode.cache.PartitionAttributesFactory; |
| import org.apache.geode.cache.Region; |
| import org.apache.geode.cache.RegionFactory; |
| import org.apache.geode.cache.RegionShortcut; |
| import org.apache.geode.distributed.DistributedMember; |
| import org.apache.geode.distributed.internal.membership.InternalDistributedMember; |
| import org.apache.geode.test.dunit.Assert; |
| import org.apache.geode.test.dunit.VM; |
| import org.apache.geode.test.dunit.cache.CacheTestCase; |
| |
| /** |
| * Confirm that the utils used for testing work as advertised |
| * |
| * @since GemFire 5.0 |
| */ |
| |
| public class PartitionedRegionTestUtilsDUnitTest extends CacheTestCase { |
| |
| private static final int TOTAL_NUM_BUCKETS = 5; |
| private static final int MAX_KEYS = 50; |
| |
| private String regionName; |
| |
| private VM vm0; |
| private VM vm1; |
| private VM vm2; |
| private VM vm3; |
| |
| @Before |
| public void setUp() { |
| vm0 = getHost(0).getVM(0); |
| vm1 = getHost(0).getVM(1); |
| vm2 = getHost(0).getVM(2); |
| vm3 = getHost(0).getVM(3); |
| |
| regionName = getUniqueName(); |
| } |
| |
| @Override |
| public Properties getDistributedSystemProperties() { |
| Properties config = new Properties(); |
| config.put(SERIALIZABLE_OBJECT_FILTER, TestPRKey.class.getName()); |
| return config; |
| } |
| |
| /** |
| * Test the {@link PartitionedRegionGetSomeKeys#getSomeKeys(PartitionedRegion, Random)} method, |
| * making sure it returns keys when there are keys and {@link java.util.Collections#EMPTY_SET} |
| * when there are none. |
| */ |
| @Test |
| public void testGetKeys() throws Exception { |
| vm0.invoke(() -> createPartitionedRegion()); |
| vm1.invoke(() -> createPartitionedRegion()); |
| vm2.invoke(() -> createPartitionedRegion()); |
| |
| vm0.invoke(() -> { |
| PartitionedRegion partitionedRegion = (PartitionedRegion) getCache().getRegion(regionName); |
| Random random = new Random(123); |
| // Assert that its empty |
| for (int i = 0; i < 5; i++) { |
| Set someKeys = getSomeKeys(partitionedRegion, random); |
| assertNotNull(someKeys); |
| assertTrue(someKeys.isEmpty()); |
| } |
| |
| for (int i = 0; i < MAX_KEYS; i++) { |
| partitionedRegion.put("testKey" + i, i); |
| } |
| |
| // Assert not empty and has value in an acceptable range |
| for (int i = 0; i < 5; i++) { |
| Set someKeys = getSomeKeys(partitionedRegion, random); |
| assertNotNull(someKeys); |
| assertFalse(someKeys.isEmpty()); |
| for (Object key : someKeys) { |
| Integer val = (Integer) partitionedRegion.get(key); |
| assertNotNull(val); |
| assertTrue(val >= 0); |
| assertTrue(val < MAX_KEYS); |
| } |
| } |
| }); |
| } |
| |
| @Test |
| public void testGetNodes() throws Exception { |
| VM validatorVM = vm2; |
| |
| validatorVM.invoke(() -> createPRAndTestGetAllNodes()); |
| |
| validatorVM.invoke(() -> { |
| PartitionedRegion partitionedRegion = (PartitionedRegion) getCache().getRegion(regionName); |
| Set<InternalDistributedMember> allNodes = partitionedRegion.getAllNodes(); |
| assertThat(allNodes).isNotNull().hasSize(1); |
| }); |
| |
| vm0.invoke(() -> createPRAndTestGetAllNodes()); |
| |
| validatorVM.invoke(() -> { |
| PartitionedRegion partitionedRegion = (PartitionedRegion) getCache().getRegion(regionName); |
| Set<InternalDistributedMember> allNodes = partitionedRegion.getAllNodes(); |
| assertThat(allNodes).isNotNull().hasSize(2); |
| }); |
| |
| vm1.invoke(() -> createPRAndTestGetAllNodes()); |
| |
| validatorVM.invoke(() -> { |
| PartitionedRegion partitionedRegion = (PartitionedRegion) getCache().getRegion(regionName); |
| Set<InternalDistributedMember> allNodes = partitionedRegion.getAllNodes(); |
| assertThat(allNodes).isNotNull().hasSize(3); |
| }); |
| } |
| |
| /** |
| * Test the test utilities that allow investigation of a PartitionedRegion's local cache. |
| */ |
| @Test |
| public void testLocalCacheOps() throws Exception { |
| vm0.invoke(() -> { |
| PartitionAttributesFactory paf = new PartitionAttributesFactory(); |
| paf.setTotalNumBuckets(TOTAL_NUM_BUCKETS); |
| paf.setLocalMaxMemory(8); |
| RegionFactory regionFactory = getCache().createRegionFactory(RegionShortcut.PARTITION); |
| regionFactory.setPartitionAttributes(paf.create()); |
| regionFactory.create(regionName); |
| }); |
| |
| vm2.invoke(() -> { |
| PartitionAttributesFactory paf = new PartitionAttributesFactory(); |
| paf.setTotalNumBuckets(TOTAL_NUM_BUCKETS); |
| paf.setLocalMaxMemory(0); |
| RegionFactory regionFactory = getCache().createRegionFactory(RegionShortcut.PARTITION); |
| regionFactory.setPartitionAttributes(paf.create()); |
| PartitionedRegion partitionedRegion = (PartitionedRegion) regionFactory.create(regionName); |
| |
| String key3 = "lcKey3"; |
| String val3 = "lcVal3"; |
| String key4 = "lcKey4"; |
| String val4 = "lcVal4"; |
| |
| // Test localCacheContainsKey |
| assertThat(partitionedRegion.localCacheContainsKey(key3)).isFalse(); |
| assertThat(partitionedRegion.localCacheContainsKey(key4)).isFalse(); |
| partitionedRegion.put(key3, val3); |
| assertThat(partitionedRegion.localCacheContainsKey(key3)).isFalse(); |
| assertThat(partitionedRegion.localCacheContainsKey(key4)).isFalse(); |
| assertThat(partitionedRegion.get(key3)).isEqualTo(val3); |
| assertThat(partitionedRegion.localCacheContainsKey(key3)).isFalse(); |
| assertThat(partitionedRegion.localCacheContainsKey(key4)).isFalse(); |
| |
| // test localCacheKeySet |
| Set localCacheKeySet = partitionedRegion.localCacheKeySet(); |
| assertThat(localCacheKeySet.contains(key3)).isFalse(); |
| assertThat(localCacheKeySet.contains(key4)).isFalse(); |
| |
| // test localCacheGet |
| assertThat(partitionedRegion.localCacheGet(key3)).isNull(); |
| assertThat(partitionedRegion.localCacheGet(key4)).isNull(); |
| partitionedRegion.put(key4, val4); |
| assertThat(partitionedRegion.localCacheGet(key4)).isNull(); |
| assertThat(partitionedRegion.get(key4)).isEqualTo(val4); |
| assertThat(partitionedRegion.localCacheGet(key4)).isNull(); |
| }); |
| } |
| |
| /** |
| * Test the test method PartitionedRegion.getAllNodes Verify that it returns nodes after a value |
| * has been placed into the PartitionedRegion. |
| * |
| * @see PartitionedRegion#getAllNodes() |
| */ |
| @Test |
| public void testGetBucketKeys() throws Exception { |
| vm2.invoke(() -> createPartitionedRegion()); |
| vm3.invoke(() -> createPartitionedRegion()); |
| |
| // Create an accessor |
| PartitionAttributesFactory paf = new PartitionAttributesFactory(); |
| paf.setLocalMaxMemory(0); |
| paf.setTotalNumBuckets(TOTAL_NUM_BUCKETS); |
| |
| RegionFactory regionFactory = getCache().createRegionFactory(RegionShortcut.PARTITION); |
| regionFactory.setPartitionAttributes(paf.create()); |
| |
| PartitionedRegion partitionedRegion = (PartitionedRegion) regionFactory.create(regionName); |
| |
| int numberOfBuckets = partitionedRegion.getTotalNumberOfBuckets(); |
| |
| for (int whichBucket = numberOfBuckets - 1; whichBucket >= 0; whichBucket--) { |
| Set bucketKeys = partitionedRegion.getBucketKeys(whichBucket); |
| assertThat(bucketKeys).isEmpty(); |
| } |
| |
| // Create bucket number of keys, assuming a mod per key hashCode |
| // There should be one key per bucket |
| partitionedRegion.put(new TestPRKey(0, 1), 0); |
| partitionedRegion.put(new TestPRKey(0, 2), 1); |
| partitionedRegion.put(new TestPRKey(0, 3), 2); |
| |
| Set<TestPRKey> bucketKeys = partitionedRegion.getBucketKeys(0); |
| |
| assertThat(bucketKeys).hasSize(3); |
| assertThat(bucketKeys.iterator().next().hashCode()).isEqualTo(0); |
| assertThat(bucketKeys.iterator().next().hashCode()).isEqualTo(0); |
| assertThat(bucketKeys.iterator().next().hashCode()).isEqualTo(0); |
| |
| // Skip bucket zero since we have three keys there, but fill out all the rest with keys |
| for (int whichBucket = numberOfBuckets - 1; whichBucket > 0; whichBucket--) { |
| TestPRKey key = new TestPRKey(whichBucket, 0); |
| partitionedRegion.put(key, whichBucket); |
| } |
| |
| // Assert that the proper number of keys are placed in each bucket |
| for (int whichBucket = 1; whichBucket < numberOfBuckets; whichBucket++) { |
| bucketKeys = partitionedRegion.getBucketKeys(whichBucket); |
| assertThat(bucketKeys).hasSize(1); |
| TestPRKey key = bucketKeys.iterator().next(); |
| assertThat(key.hashCode()).isEqualTo(whichBucket); |
| } |
| } |
| |
| /** |
| * Test the test method {@link PartitionedRegion#getBucketOwnersForValidation(int)} Verify that |
| * the information it discovers is the same as the local advisor. |
| */ |
| @Test |
| public void testGetBucketOwners() throws Exception { |
| String regionName1 = getUniqueName() + "-r0"; |
| String regionName2 = getUniqueName() + "-r1"; |
| String regionName3 = getUniqueName() + "-r2"; |
| String[] regions = {regionName1, regionName2, regionName3}; |
| |
| int numberOfBuckets = 3; |
| |
| VM datastore1VM = vm2; |
| VM datastore2VM = vm3; |
| VM datastore3VM = vm0; |
| VM accessorVM = vm1; |
| |
| datastore1VM |
| .invoke(() -> createPartitionedRegionsWithIncreasingRedundancy(numberOfBuckets, regions)); |
| datastore2VM |
| .invoke(() -> createPartitionedRegionsWithIncreasingRedundancy(numberOfBuckets, regions)); |
| datastore3VM |
| .invoke(() -> createPartitionedRegionsWithIncreasingRedundancy(numberOfBuckets, regions)); |
| |
| accessorVM.invoke(() -> { |
| PartitionAttributesFactory paf = new PartitionAttributesFactory(); |
| paf.setLocalMaxMemory(0); |
| paf.setTotalNumBuckets(numberOfBuckets); |
| |
| RegionFactory regionFactory = getCache().createRegionFactory(RegionShortcut.PARTITION); |
| |
| for (int redundancy = 0; redundancy < regions.length; redundancy++) { |
| paf.setRedundantCopies(redundancy); |
| regionFactory.setPartitionAttributes(paf.create()); |
| Region region = regionFactory.create(regions[redundancy]); |
| assertThat(region.size()).isEqualTo(0); |
| } |
| }); |
| |
| datastore1VM.invoke(() -> validateNoBucketOwners(regions, numberOfBuckets)); |
| datastore2VM.invoke(() -> validateNoBucketOwners(regions, numberOfBuckets)); |
| datastore3VM.invoke(() -> validateNoBucketOwners(regions, numberOfBuckets)); |
| accessorVM.invoke(() -> validateNoBucketOwners(regions, numberOfBuckets)); |
| |
| accessorVM.invoke(() -> { |
| for (String regionName : regions) { |
| PartitionedRegion partitionedRegion = (PartitionedRegion) getCache().getRegion(regionName); |
| assertThat(partitionedRegion.getTotalNumberOfBuckets()).isEqualTo(3); |
| // Create one bucket |
| partitionedRegion.put(0, "zero"); |
| assertThat(partitionedRegion.getRegionAdvisor().getCreatedBucketsCount()).isEqualTo(1); |
| } |
| }); |
| |
| accessorVM.invoke(() -> validateOneBucketPrimary(regions)); |
| datastore1VM.invoke(() -> validateOneBucketPrimary(regions)); |
| datastore2VM.invoke(() -> validateOneBucketPrimary(regions)); |
| datastore3VM.invoke(() -> validateOneBucketPrimary(regions)); |
| } |
| |
| private void validateOneBucketPrimary(String[] regions) { |
| for (String regionName : regions) { |
| PartitionedRegion partitionedRegion = (PartitionedRegion) getCache().getRegion(regionName); |
| try { |
| for (int bucketId : partitionedRegion.getRegionAdvisor().getBucketSet()) { |
| assertThat(partitionedRegion.getRegionAdvisor().getBucketOwners(bucketId)) |
| .hasSize(partitionedRegion.getRedundantCopies() + 1); |
| |
| List primaries = partitionedRegion.getBucketOwnersForValidation(bucketId); |
| assertThat(primaries).hasSize(partitionedRegion.getRedundantCopies() + 1); |
| |
| int primaryCount = 0; |
| for (Object primaryInfo : primaries) { |
| Object[] memberAndBoolean = (Object[]) primaryInfo; |
| assertThat(memberAndBoolean).hasSize(3); // memberId, isPrimary and hostToken(new) |
| assertThat(memberAndBoolean[0]).isInstanceOf(DistributedMember.class); |
| assertThat(memberAndBoolean[1].getClass()).isSameAs(Boolean.class); |
| Boolean isPrimary = (Boolean) memberAndBoolean[1]; |
| if (isPrimary) { |
| primaryCount++; |
| } |
| } |
| assertThat(primaryCount).isEqualTo(1); |
| } |
| } catch (ForceReattemptException noGood) { |
| Assert.fail("Unexpected force retry", noGood); |
| } |
| } |
| } |
| |
| private void validateNoBucketOwners(String[] regions, int numberOfBuckets) |
| throws ForceReattemptException { |
| for (String regionName : regions) { |
| PartitionedRegion partitionedRegion = (PartitionedRegion) getCache().getRegion(regionName); |
| assertThat(partitionedRegion.getTotalNumberOfBuckets()).isEqualTo(numberOfBuckets); |
| |
| for (int whichBucket = 0; whichBucket < partitionedRegion |
| .getTotalNumberOfBuckets(); whichBucket++) { |
| assertThat(partitionedRegion.getRegionAdvisor().getBucketOwners(whichBucket)).isEmpty(); |
| assertThat(partitionedRegion.getBucketOwnersForValidation(whichBucket)).isEmpty(); |
| } |
| } |
| } |
| |
| private void createPartitionedRegionsWithIncreasingRedundancy(int numberOfBuckets, |
| String[] regions) { |
| PartitionAttributesFactory paf = new PartitionAttributesFactory(); |
| paf.setTotalNumBuckets(numberOfBuckets); |
| |
| RegionFactory regionFactory = getCache().createRegionFactory(RegionShortcut.PARTITION); |
| |
| for (int redundancy = 0; redundancy < regions.length; redundancy++) { |
| paf.setRedundantCopies(redundancy); |
| regionFactory.setPartitionAttributes(paf.create()); |
| Region region = regionFactory.create(regions[redundancy]); |
| assertThat(region.size()).isEqualTo(0); |
| } |
| } |
| |
| private void createPartitionedRegion() { |
| PartitionAttributesFactory paf = new PartitionAttributesFactory(); |
| paf.setTotalNumBuckets(TOTAL_NUM_BUCKETS); |
| |
| RegionFactory regionFactory = getCache().createRegionFactory(RegionShortcut.PARTITION); |
| regionFactory.setPartitionAttributes(paf.create()); |
| |
| regionFactory.create(regionName); |
| } |
| |
| private void createPRAndTestGetAllNodes() { |
| PartitionAttributesFactory paf = new PartitionAttributesFactory(); |
| paf.setTotalNumBuckets(TOTAL_NUM_BUCKETS); |
| |
| RegionFactory<Object, Integer> regionFactory = |
| getCache().createRegionFactory(RegionShortcut.PARTITION); |
| regionFactory.setPartitionAttributes(paf.create()); |
| |
| Region<Object, Integer> region = regionFactory.create(regionName); |
| |
| // For each invocation, create a key that has a sequential hashCode. |
| // Putting this key into the PR should force a new bucket allocation on |
| // each new VM (assuming a mod on the hashCode), forcing the number of VMs to increase |
| // when we call getAllNodes each time this method is called. |
| Integer counter = region.get("Counter"); |
| Integer keyHash; |
| if (counter == null) { |
| counter = 0; |
| } else { |
| counter = counter + 1; |
| } |
| keyHash = counter; |
| region.put("Counter", counter); |
| region.put(new TestGetNodesKey(keyHash), counter); |
| |
| Set<InternalDistributedMember> allNodes = ((PartitionedRegion) region).getAllNodes(); |
| assertThat(allNodes).isNotNull().isNotEmpty(); |
| } |
| |
| /** |
| * Test the test method PartitionedRegion.getAllNodes Verify that it returns nodes after a value |
| * has been placed into the PartitionedRegion. |
| * |
| * @see PartitionedRegion#getAllNodes() |
| */ |
| private static class TestGetNodesKey implements DataSerializable { |
| |
| private int hashCode; |
| |
| TestGetNodesKey(int hashCode) { |
| this.hashCode = hashCode; |
| } |
| |
| public TestGetNodesKey() { |
| // nothing |
| } |
| |
| public int hashCode() { |
| return hashCode; |
| } |
| |
| @Override |
| public void toData(DataOutput out) throws IOException { |
| out.writeInt(hashCode); |
| } |
| |
| @Override |
| public void fromData(DataInput in) throws IOException, ClassNotFoundException { |
| hashCode = in.readInt(); |
| } |
| } |
| |
| private static class TestPRKey implements Serializable { |
| |
| private final int hashCode; |
| private final int differentiator; |
| |
| TestPRKey(int hashCode, int differentiator) { |
| this.hashCode = hashCode; |
| this.differentiator = differentiator; |
| } |
| |
| public int hashCode() { |
| return hashCode; |
| } |
| |
| public boolean equals(Object obj) { |
| return obj instanceof TestPRKey && ((TestPRKey) obj).differentiator == differentiator; |
| } |
| |
| public String toString() { |
| return getClass().getSimpleName() + " " + hashCode + " diff " + differentiator; |
| } |
| } |
| } |