/* * 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 org.junit.experimental.categories.Category; import org.junit.Test; import static org.junit.Assert.*; import org.apache.geode.test.dunit.cache.internal.JUnit4CacheTestCase; import org.apache.geode.test.dunit.internal.JUnit4DistributedTestCase; import org.apache.geode.test.junit.categories.DistributedTest; import java.util.concurrent.CancellationException; import org.apache.geode.Statistics; import org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.Cache; import org.apache.geode.cache.CacheException; import org.apache.geode.cache.PartitionAttributes; import org.apache.geode.cache.PartitionAttributesFactory; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.control.RebalanceOperation; import org.apache.geode.cache.control.RebalanceResults; import org.apache.geode.cache30.CacheSerializableRunnable; import org.apache.geode.distributed.DistributedSystem; import org.apache.geode.test.dunit.Assert; import org.apache.geode.test.dunit.Host; 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; /** * */ @Category(DistributedTest.class) public class PartitionedRegionStatsDUnitTest extends PartitionedRegionDUnitTestCase { ////// constructor ////////// public PartitionedRegionStatsDUnitTest() { super(); }// end of constructor public static final String PR_PREFIX = "PR"; static final int cnt = 10; final int PUT = 1; final int GET = 2; final int CONTAINS_KEY = 3; final int CONTAINS_VALUE_FOR_KEY = 4; final int INVALIDATE = 5; final int DESTROY = 6; final int CREATE = 7; final int GET_ENTRY = 8; final int totalNumBuckets = 5; static final int REDUNDANT_COPIES = 1; ////////// test methods //////////////// @Test public void testClose() throws Exception { final Host host = Host.getHost(0); final VM vm0 = host.getVM(0); final VM vm1 = host.getVM(1); final VM vm2 = host.getVM(2); final VM vm3 = host.getVM(3); CacheSerializableRunnable createPR = new CacheSerializableRunnable("createPrRegions") { public void run2() throws CacheException { Cache cache = getCache(); cache.createRegion(PR_PREFIX, createRegionAttributesForPR(REDUNDANT_COPIES, 200)); cache.createRegion(PR_PREFIX + "1", createRegionAttributesForPR(REDUNDANT_COPIES, 200)); } }; /** * This class creates accessors. */ CacheSerializableRunnable createAccessor = new CacheSerializableRunnable("createAccessor") { public void run2() throws CacheException { Cache cache = getCache(); cache.createRegion(PR_PREFIX, createRegionAttributesForPR(REDUNDANT_COPIES, 0)); cache.createRegion(PR_PREFIX + "1", createRegionAttributesForPR(REDUNDANT_COPIES, 0)); } }; /** * This class does put, get, create, destroy, invalidate, containsKey and containsValueForKey * operations on PartitionedRegion. */ CacheSerializableRunnable doRegionOps = new CacheSerializableRunnable("doRegionOps") { public void run2() {} public void doOps(Integer opTypeInteger) throws CacheException { Cache cache = getCache(); int opType = opTypeInteger.intValue(); Region pr = cache.getRegion(Region.SEPARATOR + PR_PREFIX); assertNotNull(pr); doRegionOpsOnPR(pr, opType); pr = cache.getRegion(Region.SEPARATOR + PR_PREFIX + "1"); assertNotNull(pr); doRegionOpsOnPR(pr, opType); } private void doRegionOpsOnPR(Region pr, int opType) throws CacheException { switch (opType) { case PUT: for (int k = 0; k < cnt; k++) { pr.put("" + k, "" + k); } break; case GET: for (int k = 0; k < cnt; k++) { pr.get("" + k); } break; case CONTAINS_KEY: for (int k = 0; k < cnt; k++) { pr.containsKey("" + k); } break; case CONTAINS_VALUE_FOR_KEY: for (int k = 0; k < cnt; k++) { pr.containsValueForKey("" + k); } break; case INVALIDATE: for (int k = 0; k < cnt; k++) { pr.invalidate("" + k); } break; case DESTROY: for (int k = 0; k < cnt; k++) { pr.destroy("" + k); } break; case CREATE: for (int k = 0; k < cnt; k++) { pr.create("1" + k, "1" + k); } break; case GET_ENTRY: for (int k = 0; k < cnt; k++) { pr.getEntry(Integer.toString(k)); } } } }; /** * This class disconnects the VM from DistributedSystem. */ CacheSerializableRunnable disconnectVM = new CacheSerializableRunnable( // TODO bug36296 "disconnectVM") { public void run2() { Cache cache = getCache(); DistributedSystem ds = cache.getDistributedSystem(); ds.disconnect(); } }; /** * This class validates the min, max and avg Redundant Copies PR Statistics before disconnecting * VM. */ CacheSerializableRunnable validateRedundantCopiesStats = new CacheSerializableRunnable("validateStats") { public void run2() throws CacheException { Cache cache = getCache(); PartitionedRegion pr = (PartitionedRegion) cache.getRegion(Region.SEPARATOR + PR_PREFIX); assertNotNull(pr); Statistics stats = pr.getPrStats().getStats(); int minRedundantCopies = stats.get("minRedundantCopies").intValue(); int maxRedundantCopies = stats.get("maxRedundantCopies").intValue(); int avgRedundantCopies = stats.get("avgRedundantCopies").intValue(); assertEquals(minRedundantCopies, 1); assertEquals(maxRedundantCopies, 1); assertEquals(avgRedundantCopies, 1); } }; /** * This class validates the min, max and avg redundant copies PR Statistics after disconnecting * VM. */ CacheSerializableRunnable validateRedundantCopiesStatsAfterDisconnect = new CacheSerializableRunnable("validateRedundantCopiesStatsAfterDisconnect") { public void run2() throws CacheException { Cache cache = getCache(); PartitionedRegion pr = (PartitionedRegion) cache.getRegion(Region.SEPARATOR + PR_PREFIX); assertNotNull(pr); Statistics stats = pr.getPrStats().getStats(); int minRedundantCopies = stats.get("minRedundantCopies").intValue(); int maxRedundantCopies = stats.get("maxRedundantCopies").intValue(); int avgRedundantCopies = stats.get("avgRedundantCopies").intValue(); assertEquals(minRedundantCopies, 1); assertEquals(maxRedundantCopies, 1); assertEquals(avgRedundantCopies, 1); } }; /** * This class validates PartitionedRegion operations related statistics. PRStatistics for put, * get, create, invalidate, containsKey, containsValueForKey and destroys are validated. */ CacheSerializableRunnable validatePartitionedRegionOpsStats = new CacheSerializableRunnable("validatePartitionedRegionOpsStats") { public void run2() throws CacheException { Cache cache = getCache(); PartitionedRegion pr = (PartitionedRegion) cache.getRegion(Region.SEPARATOR + PR_PREFIX); assertNotNull(pr); pr = (PartitionedRegion) cache.getRegion(Region.SEPARATOR + PR_PREFIX); assertNotNull(pr); Statistics stats = pr.getPrStats().getStats(); int putsCompleted = stats.get("putsCompleted").intValue(); int getsCompleted = stats.get("getsCompleted").intValue(); int getEntrysCompleted = stats.get("getEntryCompleted").intValue(); int createsCompleted = stats.get("createsCompleted").intValue(); int containsKeyCompleted = stats.get("containsKeyCompleted").intValue(); int containsValueForKeyCompleted = stats.get("containsValueForKeyCompleted").intValue(); int invalidatesCompleted = stats.get("invalidatesCompleted").intValue(); int destroysCompleted = stats.get("destroysCompleted").intValue(); assertEquals(cnt, putsCompleted); assertEquals(cnt, getsCompleted); assertEquals(cnt, getEntrysCompleted); assertEquals(0, createsCompleted); assertEquals(cnt, containsKeyCompleted); assertEquals(cnt, containsValueForKeyCompleted); assertEquals(cnt, invalidatesCompleted); assertEquals(cnt, destroysCompleted); // TODO: Need to validate that bucket stats add up.... // pr.getDataStore().getCachePerfStats().getGets(); // etc... } }; // Create PRs on 3 VMs and accessors on 1 VM vm0.invoke(createPR); vm1.invoke(createPR); vm2.invoke(createPR); vm3.invoke(createAccessor); // Do Region operations. Object[] put = {new Integer(PUT)}; vm0.invoke(doRegionOps, "doOps", put); Object[] get = {new Integer(GET)}; vm0.invoke(doRegionOps, "doOps", get); Object[] getEntry = {new Integer(GET_ENTRY)}; vm0.invoke(doRegionOps, "doOps", getEntry); Object[] containsKey = {new Integer(CONTAINS_KEY)}; vm0.invoke(doRegionOps, "doOps", containsKey); Object[] containsValueForKey = {new Integer(CONTAINS_VALUE_FOR_KEY)}; vm0.invoke(doRegionOps, "doOps", containsValueForKey); Object[] invalidate = {new Integer(INVALIDATE)}; vm0.invoke(doRegionOps, "doOps", invalidate); Object[] destroy = {new Integer(DESTROY)}; vm0.invoke(doRegionOps, "doOps", destroy); vm0.invoke(validatePartitionedRegionOpsStats); CacheSerializableRunnable destroyRegion = new CacheSerializableRunnable("destroyRegion") { public void run2() throws CacheException { Cache cache = getCache(); PartitionedRegion pr = (PartitionedRegion) cache.getRegion(Region.SEPARATOR + PR_PREFIX + "1"); assertNotNull("Null region is " + pr.getName(), pr); pr.destroyRegion(); pr = (PartitionedRegion) cache.getRegion(Region.SEPARATOR + PR_PREFIX); assertNotNull("Null region is " + pr.getName(), pr); pr.destroyRegion(); } }; vm0.invoke(destroyRegion); /* * Redundant Copies related statistics validation vm0.invoke(validateRedundantCopiesStats); * * vm1.invoke(disconnectVM); * * Thread.sleep(20000); * * vm0.invoke(validateRedundantCopiesStatsAfterDisconnect); */ } @Test public void testDataStoreEntryCountWithRebalance() throws InterruptedException { // Ok, first problem, GC'd tombstone is counted as an entry // To test // - modifying a tombstone // - modifying and doing tombstone GC? final Host host = Host.getHost(0); final VM vm0 = host.getVM(0); final 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(0); PartitionAttributes prAttr = paf.create(); attr.setPartitionAttributes(prAttr); cache.createRegion("region1", attr.create()); RebalanceOperation op = cache.getResourceManager().createRebalanceFactory().start(); try { RebalanceResults results = op.getResults(); } catch (Exception e) { Assert.fail("ex", e); } } }; vm0.invoke(createPrRegion); vm0.invoke(new SerializableRunnable("Put some data") { public void run() { Cache cache = getCache(); Region region = cache.getRegion("region1"); region.put(Long.valueOf(0), "A"); region.put(Long.valueOf(1), "A"); region.put(Long.valueOf(113), "A"); region.put(Long.valueOf(114), "A"); region.destroy(Long.valueOf(0)); region.destroy(Long.valueOf(1)); } }); vm1.invoke(createPrRegion); validateEntryCount(vm0, 1); validateEntryCount(vm1, 1); } @Test public void testDataStoreEntryCount2WithRebalance() throws InterruptedException { final Host host = Host.getHost(0); final VM vm0 = host.getVM(0); final 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(0); PartitionAttributes prAttr = paf.create(); attr.setPartitionAttributes(prAttr); cache.createRegion("region1", attr.create()); RebalanceOperation op = cache.getResourceManager().createRebalanceFactory().start(); try { RebalanceResults results = op.getResults(); } catch (Exception e) { Assert.fail("ex", e); } } }; vm0.invoke(createPrRegion); vm0.invoke(new SerializableRunnable("Put some data") { public void run() { Cache cache = getCache(); Region region = cache.getRegion("region1"); region.put(Long.valueOf(0), "A"); region.put(Long.valueOf(1), "A"); region.put(Long.valueOf(2), "A"); region.put(Long.valueOf(3), "A"); region.put(Long.valueOf(4), "A"); region.put(Long.valueOf(5), "A"); } }); vm1.invoke(createPrRegion); validateEntryCount(vm0, 3); validateEntryCount(vm1, 3); } /** * Test to make sure the datastore entry count is accurate. * * @throws InterruptedException */ @Test public void testDataStoreEntryCount() throws InterruptedException { // Ok, first problem, GC'd tombstone is counted as an entry // To test // - modifying a tombstone // - modifying and doing tombstone GC? final Host host = Host.getHost(0); final VM vm0 = host.getVM(0); final VM vm1 = host.getVM(1); final VM vm2 = host.getVM(2); SerializableRunnable createPrRegion = new SerializableRunnable("createRegion") { public void run() { Cache cache = getCache(); AttributesFactory attr = new AttributesFactory(); PartitionAttributesFactory paf = new PartitionAttributesFactory(); paf.setRedundantCopies(2); PartitionAttributes prAttr = paf.create(); attr.setPartitionAttributes(prAttr); cache.createRegion("region1", attr.create()); } }; vm0.invoke(createPrRegion); vm1.invoke(createPrRegion); vm0.invoke(new SerializableRunnable("Put some data") { public void run() { Cache cache = getCache(); Region region = cache.getRegion("region1"); region.put(Long.valueOf(0), "A"); region.put(Long.valueOf(1), "A"); region.put(Long.valueOf(113), "A"); region.put(Long.valueOf(226), "A"); } }); validateEntryCount(vm0, 4); validateEntryCount(vm1, 4); // Do a destroy vm0.invoke(new SerializableRunnable("Put some data") { public void run() { Cache cache = getCache(); Region region = cache.getRegion("region1"); region.destroy(Long.valueOf(0)); } }); // We expect the tombstone won't be recorded as part of // the entry count validateEntryCount(vm0, 3); validateEntryCount(vm1, 3); // Destroy and modify a tombstone vm0.invoke(new SerializableRunnable("Put some data") { public void run() { Cache cache = getCache(); Region region = cache.getRegion("region1"); region.destroy(Long.valueOf(113)); region.put(Long.valueOf(113), "B"); } }); validateEntryCount(vm0, 3); validateEntryCount(vm1, 3); // After GII (which might include the tombstone), a new members // should still see only 2 live entries. vm2.invoke(createPrRegion); // Wait for redundancy to be restored. Once it is the entry count should be // 2 vm2.invoke(new SerializableRunnable("validate stats") { public void run() { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion("region1"); final PartitionedRegionStats stats = region.getPrStats(); Wait.waitForCriterion(new WaitCriterion() { @Override public boolean done() { return stats.getLowRedundancyBucketCount() == 0; } @Override public String description() { return "Redundancy was not satisfied " + stats.getLowRedundancyBucketCount(); } }, 20000, 100, true); } }); validateEntryCount(vm2, 3); // A tombstone GC shouldn't affect the count. vm0.invoke(new SerializableRunnable("Put some data") { public void run() { GemFireCacheImpl cache = (GemFireCacheImpl) getCache(); TombstoneService tombstoneService = cache.getTombstoneService(); try { tombstoneService.forceBatchExpirationForTests(1); } catch (InterruptedException e) { Assert.fail("interrupted", e); } } }); validateEntryCount(vm0, 3); validateEntryCount(vm1, 3); validateEntryCount(vm2, 3); } @Test public void testTotalNumBuckets() { final Host host = Host.getHost(0); final VM vm0 = host.getVM(0); final 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(2); PartitionAttributes prAttr = paf.create(); attr.setPartitionAttributes(prAttr); cache.createRegion("region1", attr.create()); } }; vm0.invoke(createPrRegion); vm1.invoke(createPrRegion); SerializableRunnable putDataInRegion = new SerializableRunnable("Put some data") { public void run() { Cache cache = getCache(); Region region = cache.getRegion("region1"); region.put(Long.valueOf(0), "A"); region.put(Long.valueOf(1), "A"); region.put(Long.valueOf(113), "A"); region.put(Long.valueOf(226), "A"); } }; vm0.invoke(putDataInRegion); vm1.invoke(putDataInRegion); int expectedBucketCount = 113; validateTotalNumBucketsCount(vm0, expectedBucketCount); validateTotalNumBucketsCount(vm1, expectedBucketCount); } private void validateEntryCount(final VM vm0, final long expectedCount) { SerializableRunnable validateStats = new SerializableRunnable("validate stats") { public void run() { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion("region1"); PartitionedRegionStats stats = region.getPrStats(); CachePerfStats cachePerfStats = region.getCachePerfStats(); assertEquals(expectedCount, stats.getDataStoreEntryCount()); assertEquals(expectedCount, cachePerfStats.getEntries()); } }; vm0.invoke(validateStats); } private void validateTotalNumBucketsCount(final VM vm0, final long expectedCount) { SerializableRunnable validateStats = new SerializableRunnable("validate stats") { public void run() { Cache cache = getCache(); PartitionedRegion region = (PartitionedRegion) cache.getRegion("region1"); PartitionedRegionStats stats = region.getPrStats(); CachePerfStats cachePerfStats = region.getCachePerfStats(); assertEquals(expectedCount, stats.getTotalNumBuckets()); } }; vm0.invoke(validateStats); } /** * This private methods sets the passed attributes and returns RegionAttribute object, which is * used in create region * * @param redundancy * @param localMaxMem * * @return */ protected RegionAttributes createRegionAttributesForPR(int redundancy, int localMaxMem) { AttributesFactory attr = new AttributesFactory(); PartitionAttributesFactory paf = new PartitionAttributesFactory(); // Properties globalProps = new Properties(); PartitionAttributes prAttr = paf.setRedundantCopies(redundancy).setLocalMaxMemory(localMaxMem) .setTotalNumBuckets(totalNumBuckets).create(); attr.setPartitionAttributes(prAttr); return attr.create(); } }