/* * 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.cache30; 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 org.apache.geode.cache.AttributesFactory; import org.apache.geode.cache.CacheException; import org.apache.geode.cache.ExpirationAction; import org.apache.geode.cache.ExpirationAttributes; import org.apache.geode.cache.Region; import org.apache.geode.cache.RegionAttributes; import org.apache.geode.cache.Scope; import org.apache.geode.test.dunit.Host; import org.apache.geode.test.dunit.LogWriterUtils; import org.apache.geode.test.dunit.VM; import org.apache.geode.test.dunit.Wait; import org.apache.geode.test.dunit.WaitCriterion; /** * Test Region expiration - both time-to-live and idle timeout. * * Note: See LocalRegionTest and MultiVMRegionTestCase for more expiration tests. * * * @since GemFire 3.0 */ @Category(DistributedTest.class) public class RegionExpirationDUnitTest extends JUnit4CacheTestCase { public RegionExpirationDUnitTest() { super(); } // /** // * Returns the attributes of a region to be tested. // */ // private RegionAttributes getRegionAttributes() { // AttributesFactory factory = new AttributesFactory(); // factory.setScope(Scope.LOCAL); // factory.setStatisticsEnabled(true); // return factory.create(); // } /** * Test internal methods that encode & decode time */ /* * The encode and decode time methods are now private in MetaMap * * @Test public void testTimeEncoding() throws CacheException { Region r = * createRegion(getUniqueName(), getRegionAttributes()); long start = * ((InternalDistributedSystem)getCache().getDistributedSystem()).getStartTime(); long timeMs = * System.currentTimeMillis(); assertTrue(timeMs >= start); int encoded = * ((LocalRegion)r).encodeTime(timeMs); long decoded = ((LocalRegion)r).decodeTime(encoded); * getLogWriter().info( "testTimeEncoding: timeMs: " + timeMs + ", ds start: " + start + * ", encoded: " + encoded + ", decoded: " + decoded); assertTrue(Math.abs(timeMs - decoded) <= * 100); } */ @Test public void testRegionTTLLocalDestroy() throws CacheException, InterruptedException { _testRegionTTL(getUniqueName(), ExpirationAction.LOCAL_DESTROY); } @Test public void testRegionTTLDestroy() throws CacheException, InterruptedException { _testRegionTTL(getUniqueName(), ExpirationAction.DESTROY); } @Test public void testRegionTTLLocalInvalidate() throws CacheException, InterruptedException { _testRegionTTL(getUniqueName(), ExpirationAction.LOCAL_INVALIDATE); } @Test public void testRegionTTLInvalidate() throws CacheException, InterruptedException { _testRegionTTL(getUniqueName(), ExpirationAction.INVALIDATE); } @Test public void testRegionTTLAfterMutating() throws InterruptedException, CacheException { String regionName = getUniqueName(); int firstTimeout = 2; int secondTimeout = 6; createWithExpiration(regionName, new ExpirationAttributes(firstTimeout, ExpirationAction.DESTROY), null); long startTime = System.currentTimeMillis(); final Region region = getOrCreateRootRegion().getSubregion(regionName); region.getAttributesMutator() .setRegionTimeToLive(new ExpirationAttributes(secondTimeout, ExpirationAction.DESTROY)); Thread.sleep(firstTimeout * 1000 + 100); if (region.isDestroyed()) { assertTrue(System.currentTimeMillis() >= startTime + secondTimeout * 1000); } Thread.sleep((secondTimeout - firstTimeout) * 1000 + 100); WaitCriterion wc = new WaitCriterion() { public boolean done() { return region.isDestroyed(); } public String description() { return "region never destroyed"; } }; Wait.waitForCriterion(wc, 30 * 1000, 1000, true); } @Test public void testWhenBothTtlAndIdleAreSet() throws InterruptedException, CacheException { String regionName = getUniqueName(); int ttlTimeout = 4; int idleTimeout = 3; createWithExpiration(regionName, new ExpirationAttributes(ttlTimeout, ExpirationAction.DESTROY), new ExpirationAttributes(idleTimeout, ExpirationAction.INVALIDATE)); Region region = getOrCreateRootRegion().getSubregion(regionName); region.create("key", "val"); Thread.sleep(idleTimeout * 1000 + 200); assertFalse(region.isDestroyed()); // Touch the lastAccessedTime assertNull(region.get("key")); // Next action that occurs should be ttl - the destroy Thread.sleep(((ttlTimeout - idleTimeout) * 1000) + 5000); assertTrue(region.isDestroyed()); } /////////////// Utility methods /////////////////////////// private void _testRegionTTL(final String regionName, final ExpirationAction action) throws InterruptedException { final int timeoutSecs = 10; final Object key = "key"; final Object originalValue = "original value"; Host host = Host.getHost(0); VM vm0 = host.getVM(0); VM vm1 = host.getVM(1); LogWriterUtils.getLogWriter().info("vm0 is " + vm0.getPid() + ", vm1 is " + vm1); LogWriterUtils.getLogWriter().info("2: " + regionName + " action is " + action); final long tilt = System.currentTimeMillis() + timeoutSecs * 1000; // In vm0, create the region with ttl, and put value vm0.invoke(new CacheSerializableRunnable("Create Region & Key") { public void run2() throws CacheException { createWithExpiration(regionName, new ExpirationAttributes(timeoutSecs, action), null); Region region = getOrCreateRootRegion().getSubregion(regionName); region.put(key, originalValue); assertEquals(originalValue, region.get(key)); assertTrue(region.containsValueForKey(key)); } }); // In vm1, create the region - no ttl vm1.invoke(new CacheSerializableRunnable("Create Region & Key") { public void run2() throws CacheException { createWithExpiration(regionName, null, null); } }); // In vm1, do get() to cause netsearch vm1.invoke(new CacheSerializableRunnable("Get") { public void run2() throws CacheException { Region region = getOrCreateRootRegion().getSubregion(regionName); Object newVal = region.get(key); if (!originalValue.equals(newVal)) { assertTrue("expected original value but got " + newVal, System.currentTimeMillis() + 1000 >= tilt); } if (!region.containsValueForKey(key)) { assertTrue("Region doesn't hold key", System.currentTimeMillis() + 1000 >= tilt); } } }); // Wait for expiration to occur Thread.sleep(timeoutSecs * 1000 + 2000); // In vm0, region should be absent (for destroy, localDestroy), or // entry invalid vm0.invoke(new CacheSerializableRunnable("Get") { public void run2() throws CacheException { Region region = getRootRegion().getSubregion(regionName); LogWriterUtils.getLogWriter() .info("3: " + regionName + ", " + region + ", action is " + action); if (action.isInvalidate() || action.isLocalInvalidate()) { assertTrue(!region.containsValueForKey(key)); } else { assertTrue(region == null); } } }); // In vm1, region should be absent (for destroy), or entry invalid (invalidate). // For LOCAL_DESTROY or LOCAL_INVALIDATE, the value should be intact vm1.invoke(new CacheSerializableRunnable("Get") { public void run2() throws CacheException { Region region = getRootRegion().getSubregion(regionName); if (action.isInvalidate()) { assertTrue(region.containsKey(key)); assertTrue(!region.containsValueForKey(key)); } else if (action.isDestroy()) { assertTrue(region == null); } else { assertTrue(region.containsValueForKey(key)); assertEquals(originalValue, region.get(key)); } } }); } protected void createWithExpiration(String regionName, ExpirationAttributes ttl, ExpirationAttributes idle) throws CacheException { AttributesFactory factory = new AttributesFactory(); factory.setStatisticsEnabled(true); if (ttl != null) factory.setRegionTimeToLive(ttl); if (idle != null) factory.setRegionIdleTimeout(idle); factory.setScope(Scope.DISTRIBUTED_ACK); factory.setEarlyAck(false); RegionAttributes attrs = factory.create(); LogWriterUtils.getLogWriter().info("4: " + regionName + " ttl action is " + ttl); getOrCreateRootRegion().createSubregion(regionName, attrs); } protected Region getOrCreateRootRegion() throws CacheException { Region root = getRootRegion(); if (root == null) { AttributesFactory factory = new AttributesFactory(); factory.setScope(Scope.DISTRIBUTED_ACK); factory.setEarlyAck(false); factory.setStatisticsEnabled(true); root = createRootRegion(factory.create()); } return root; } }