| /*========================================================================= |
| * Copyright (c) 2010-2014 Pivotal Software, Inc. All Rights Reserved. |
| * This product is protected by U.S. and international copyright |
| * and intellectual property laws. Pivotal products are covered by |
| * one or more patents listed at http://www.pivotal.io/patents. |
| *========================================================================= |
| */ |
| package com.gemstone.gemfire.internal.cache; |
| |
| import java.net.InetAddress; |
| import java.util.Properties; |
| |
| import org.junit.After; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.experimental.categories.Category; |
| import org.junit.rules.TestName; |
| |
| import com.gemstone.gemfire.cache.CacheFactory; |
| import com.gemstone.gemfire.cache.EntryNotFoundException; |
| import com.gemstone.gemfire.cache.Operation; |
| import com.gemstone.gemfire.cache.RegionFactory; |
| import com.gemstone.gemfire.cache.RegionShortcut; |
| import com.gemstone.gemfire.distributed.DistributedSystem; |
| import com.gemstone.gemfire.distributed.internal.DistributionConfig; |
| import com.gemstone.gemfire.distributed.internal.InternalDistributedSystem; |
| import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember; |
| import com.gemstone.gemfire.internal.Assert; |
| import com.gemstone.gemfire.internal.cache.versions.VersionTag; |
| import com.gemstone.gemfire.test.junit.categories.IntegrationTest; |
| |
| @Category(IntegrationTest.class) |
| public class TombstoneCreationJUnitTest { |
| @Rule |
| public TestName nameRule = new TestName(); |
| |
| @After |
| public void tearDown() { |
| InternalDistributedSystem system = InternalDistributedSystem.getConnectedInstance(); |
| if (system != null) { |
| system.disconnect(); |
| } |
| } |
| |
| @Test |
| public void testDestroyCreatesTombstone() throws Exception { |
| String name = nameRule.getMethodName(); |
| Properties props = new Properties(); |
| props.put(DistributionConfig.LOCATORS_NAME, ""); |
| props.put(DistributionConfig.MCAST_PORT_NAME, "0"); |
| props.put(DistributionConfig.LOG_LEVEL_NAME, "config"); |
| GemFireCacheImpl cache = (GemFireCacheImpl)CacheFactory.create(DistributedSystem.connect(props)); |
| RegionFactory f = cache.createRegionFactory(RegionShortcut.REPLICATE); |
| DistributedRegion region = (DistributedRegion)f.create(name); |
| |
| EntryEventImpl ev = EntryEventImpl.create(region, |
| Operation.DESTROY, "myDestroyedKey", null, null, true, new InternalDistributedMember(InetAddress.getLocalHost(), 1234)); |
| VersionTag tag = VersionTag.create((InternalDistributedMember)ev.getDistributedMember()); |
| tag.setIsRemoteForTesting(); |
| tag.setEntryVersion(2); |
| tag.setRegionVersion(12345); |
| tag.setVersionTimeStamp(System.currentTimeMillis()); |
| tag.setDistributedSystemId(1); |
| ev.setVersionTag(tag); |
| cache.getLogger().info("destroyThread is trying to destroy the entry: " + region.getRegionEntry("myDestroyedKey")); |
| region.basicDestroy(ev, |
| false, |
| null); // expectedOldValue not supported on |
| RegionEntry entry = region.getRegionEntry("myDestroyedKey"); |
| Assert.assertTrue(entry != null, "expected to find a region entry for myDestroyedKey"); |
| Assert.assertTrue(entry.isTombstone(), "expected entry to be found and be a tombstone but it is " + entry); |
| |
| } |
| |
| /** |
| * In bug #47868 a thread puts a REMOVED_PHASE1 entry in the map but is |
| * unable to lock the entry before a Destroy thread gets it. The Destroy |
| * thread did not apply its operation but threw an EntryNotFoundException. |
| * It is supposed to create a Tombstone. |
| * @throws Exception |
| */ |
| @Test |
| public void testConcurrentCreateAndDestroy() throws Exception { |
| String name = nameRule.getMethodName(); |
| Properties props = new Properties(); |
| props.put(DistributionConfig.LOCATORS_NAME, ""); |
| props.put(DistributionConfig.MCAST_PORT_NAME, "0"); |
| props.put(DistributionConfig.LOG_LEVEL_NAME, "config"); |
| final GemFireCacheImpl cache = (GemFireCacheImpl)CacheFactory.create(DistributedSystem.connect(props)); |
| RegionFactory f = cache.createRegionFactory(RegionShortcut.REPLICATE); |
| final DistributedRegion region = (DistributedRegion)f.create(name); |
| |
| // simulate a put() getting into AbstractRegionMap.basicPut() and creating an entry |
| // that has not yet been initialized with values. Then do a destroy that will encounter |
| // the entry |
| String key = "destroyedKey1"; |
| VersionedThinRegionEntryHeap entry = new VersionedThinRegionEntryHeapObjectKey(region, key, Token.REMOVED_PHASE1); |
| ((AbstractRegionMap)region.getRegionMap()).putEntryIfAbsentForTest(entry); |
| cache.getLogger().info("entry inserted into cache: " + entry); |
| |
| EntryEventImpl ev = EntryEventImpl.create(region, |
| Operation.DESTROY, key, null, null, true, new InternalDistributedMember(InetAddress.getLocalHost(), 1234)); |
| VersionTag tag = VersionTag.create((InternalDistributedMember)ev.getDistributedMember()); |
| tag.setIsRemoteForTesting(); |
| tag.setEntryVersion(2); |
| tag.setRegionVersion(12345); |
| tag.setVersionTimeStamp(System.currentTimeMillis()); |
| tag.setDistributedSystemId(1); |
| ev.setVersionTag(tag); |
| cache.getLogger().info("destroyThread is trying to destroy the entry: " + region.getRegionEntry(key)); |
| region.basicDestroy(ev, |
| false, |
| null); // expectedOldValue not supported on |
| entry = (VersionedThinRegionEntryHeap)region.getRegionEntry(key); |
| region.dumpBackingMap(); |
| Assert.assertTrue(entry != null, "expected to find a region entry for " + key); |
| Assert.assertTrue(entry.isTombstone(), "expected entry to be found and be a tombstone but it is " + entry); |
| Assert.assertTrue(entry.getVersionStamp().getEntryVersion() == tag.getEntryVersion(), |
| "expected " + tag.getEntryVersion() + |
| " but found " + entry.getVersionStamp().getEntryVersion()); |
| |
| |
| RegionMap map = region.getRegionMap(); |
| tag = entry.asVersionTag(); |
| map.removeTombstone(entry, tag, false, true); |
| |
| // now do an op that has local origin |
| entry = new VersionedThinRegionEntryHeapObjectKey(region, key, Token.REMOVED_PHASE1); |
| ((AbstractRegionMap)region.getRegionMap()).putEntryIfAbsentForTest(entry); |
| cache.getLogger().info("entry inserted into cache: " + entry); |
| |
| ev = EntryEventImpl.create(region, |
| Operation.DESTROY, key, null, null, false, cache.getMyId()); |
| tag = VersionTag.create((InternalDistributedMember)ev.getDistributedMember()); |
| tag.setEntryVersion(2); |
| tag.setRegionVersion(12345); |
| tag.setVersionTimeStamp(System.currentTimeMillis()); |
| tag.setDistributedSystemId(1); |
| ev.setVersionTag(tag); |
| cache.getLogger().info("destroyThread is trying to destroy the entry: " + region.getRegionEntry(key)); |
| boolean caught = false; |
| try { |
| region.basicDestroy(ev, |
| false, |
| null); // expectedOldValue not supported on |
| } catch (EntryNotFoundException e) { |
| caught = true; |
| } |
| Assert.assertTrue(caught, "expected an EntryNotFoundException for origin=local destroy operation"); |
| } |
| |
| |
| |
| // bug #51184 - cache accepts an event to overwrite an expired |
| // tombstone but the tombstone is actually more recent than |
| // the event |
| @Test |
| public void testOlderEventIgnoredEvenIfTombstoneHasExpired() throws Exception { |
| String name = nameRule.getMethodName(); |
| Properties props = new Properties(); |
| props.put(DistributionConfig.LOCATORS_NAME, ""); |
| props.put(DistributionConfig.MCAST_PORT_NAME, "0"); |
| props.put(DistributionConfig.LOG_LEVEL_NAME, "config"); |
| final GemFireCacheImpl cache = (GemFireCacheImpl)CacheFactory.create(DistributedSystem.connect(props)); |
| RegionFactory f = cache.createRegionFactory(RegionShortcut.REPLICATE); |
| final DistributedRegion region = (DistributedRegion)f.create(name); |
| |
| // simulate a put() getting into AbstractRegionMap.basicPut() and creating an entry |
| // that has not yet been initialized with values. Then do a destroy that will encounter |
| // the entry |
| String key = "destroyedKey1"; |
| VersionedThinRegionEntryHeap entry = new VersionedThinRegionEntryHeapObjectKey(region, key, Token.REMOVED_PHASE1); |
| entry.setLastModified(System.currentTimeMillis() - (2 * TombstoneService.REPLICATED_TOMBSTONE_TIMEOUT)); |
| ((AbstractRegionMap)region.getRegionMap()).putEntryIfAbsentForTest(entry); |
| cache.getLogger().info("entry inserted into cache: " + entry); |
| |
| EntryEventImpl ev = EntryEventImpl.create(region, |
| Operation.DESTROY, key, null, null, true, new InternalDistributedMember(InetAddress.getLocalHost(), 1234)); |
| VersionTag tag = VersionTag.create((InternalDistributedMember)ev.getDistributedMember()); |
| tag.setIsRemoteForTesting(); |
| tag.setEntryVersion(3); |
| tag.setRegionVersion(12345); |
| tag.setVersionTimeStamp(System.currentTimeMillis() - TombstoneService.REPLICATED_TOMBSTONE_TIMEOUT); |
| tag.setDistributedSystemId(1); |
| ev.setVersionTag(tag); |
| cache.getLogger().info("trying to destroy the entry: " + region.getRegionEntry(key)); |
| region.basicDestroy(ev, |
| false, |
| null); // expectedOldValue not supported on |
| entry = (VersionedThinRegionEntryHeap)region.getRegionEntry(key); |
| region.dumpBackingMap(); |
| Assert.assertTrue(entry != null, "expected to find a region entry for " + key); |
| Assert.assertTrue(entry.isTombstone(), "expected entry to be found and be a tombstone but it is " + entry); |
| Assert.assertTrue(entry.getVersionStamp().getEntryVersion() == tag.getEntryVersion(), |
| "expected " + tag.getEntryVersion() + |
| " but found " + entry.getVersionStamp().getEntryVersion()); |
| |
| // pause to let the clock change |
| Thread.sleep(100); |
| |
| ev = EntryEventImpl.create(region, |
| Operation.UPDATE, key, null, null, true, new InternalDistributedMember(InetAddress.getLocalHost(), 1234)); |
| tag = VersionTag.create((InternalDistributedMember)ev.getDistributedMember()); |
| tag.setIsRemoteForTesting(); |
| tag.setEntryVersion(1); |
| tag.setRegionVersion(12340); |
| tag.setVersionTimeStamp(System.currentTimeMillis() - TombstoneService.REPLICATED_TOMBSTONE_TIMEOUT - 10000); |
| tag.setDistributedSystemId(1); |
| ev.setVersionTag(tag); |
| cache.getLogger().info("trying to update the entry with an older event: " + region.getRegionEntry(key)); |
| region.virtualPut(ev, false, true, null, false, tag.getVersionTimeStamp(), true); |
| entry = (VersionedThinRegionEntryHeap)region.getRegionEntry(key); |
| region.dumpBackingMap(); |
| Assert.assertTrue(entry != null, "expected to find a region entry for " + key); |
| Assert.assertTrue(entry.isTombstone(), "expected entry to be found and be a tombstone but it is " + entry); |
| Assert.assertTrue(entry.getVersionStamp().getEntryVersion() == 3, |
| "expected 3" + |
| " but found " + entry.getVersionStamp().getEntryVersion()); |
| |
| } |
| |
| } |