You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by ds...@apache.org on 2016/02/05 01:37:08 UTC

incubator-geode git commit: add FORCE_INVALIDATE_EVENT

Repository: incubator-geode
Updated Branches:
  refs/heads/feature/GEODE-915 [created] ad9c9b5cc


add FORCE_INVALIDATE_EVENT


Project: http://git-wip-us.apache.org/repos/asf/incubator-geode/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-geode/commit/ad9c9b5c
Tree: http://git-wip-us.apache.org/repos/asf/incubator-geode/tree/ad9c9b5c
Diff: http://git-wip-us.apache.org/repos/asf/incubator-geode/diff/ad9c9b5c

Branch: refs/heads/feature/GEODE-915
Commit: ad9c9b5ccf9da3641c4f486f86b36d6b07841622
Parents: ab25e41
Author: Darrel Schneider <ds...@pivotal.io>
Authored: Thu Feb 4 16:30:54 2016 -0800
Committer: Darrel Schneider <ds...@pivotal.io>
Committed: Thu Feb 4 16:30:54 2016 -0800

----------------------------------------------------------------------
 .../internal/cache/AbstractRegionMap.java       |  22 ++-
 .../gemfire/internal/cache/LocalRegion.java     |   4 +-
 .../gemfire/cache30/CacheListenerTestCase.java  |  69 +++++++
 .../internal/cache/AbstractRegionMapTest.java   | 186 +++++++++++++++++++
 4 files changed, 278 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ad9c9b5c/gemfire-core/src/main/java/com/gemstone/gemfire/internal/cache/AbstractRegionMap.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/internal/cache/AbstractRegionMap.java b/gemfire-core/src/main/java/com/gemstone/gemfire/internal/cache/AbstractRegionMap.java
index 3679519..6fe60ce 100644
--- a/gemfire-core/src/main/java/com/gemstone/gemfire/internal/cache/AbstractRegionMap.java
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/internal/cache/AbstractRegionMap.java
@@ -105,7 +105,7 @@ import com.gemstone.gemfire.pdx.internal.ConvertableToBytes;
 // which checks for RegionEntry classes of GFE and validates the same with its 
 // own classes.
 
-abstract class AbstractRegionMap implements RegionMap {
+public abstract class AbstractRegionMap implements RegionMap {
 
   private static final Logger logger = LogService.getLogger();
   
@@ -1953,6 +1953,23 @@ abstract class AbstractRegionMap implements RegionMap {
     }
   }
 
+  /**
+   * If true then invalidates that throw EntryNotFoundException
+   * or that are already invalid will first call afterInvalidate on CacheListeners. 
+   * The old value on the event passed to afterInvalidate will be null.
+   */
+  public static boolean FORCE_INVALIDATE_EVENT = Boolean.getBoolean("gemfire.FORCE_INVALIDATE_EVENT");
+
+  /**
+   * If the FORCE_INVALIDATE_EVENT flag is true
+   * then invoke callbacks on the given event.
+   */
+  void forceInvalidateEvent(EntryEventImpl event) {
+    if (FORCE_INVALIDATE_EVENT) {
+      event.invokeCallbacks(_getOwner(), false, false);
+    }
+  }
+  
   public final boolean invalidate(EntryEventImpl event,
       boolean invokeCallbacks, boolean forceNewEntry, boolean forceCallbacks)
       throws EntryNotFoundException
@@ -2019,6 +2036,7 @@ abstract class AbstractRegionMap implements RegionMap {
                         // that's okay - when writing an invalid into a disk, the
                         // region has been cleared (including this token)
                       }
+                      forceInvalidateEvent(event);
                     } else {
                       owner.cacheWriteBeforeInvalidate(event, invokeCallbacks, forceNewEntry);
                       if (owner.concurrencyChecksEnabled && event.noVersionReceivedFromServer()) {
@@ -2247,6 +2265,7 @@ abstract class AbstractRegionMap implements RegionMap {
                   if (event.getVersionTag() != null && owner.getVersionVector() != null) {
                     owner.getVersionVector().recordVersion((InternalDistributedMember) event.getDistributedMember(), event.getVersionTag());
                   }
+                  forceInvalidateEvent(event);
                 }
                 else { // previous value not invalid
                   event.setRegionEntry(re);
@@ -2313,6 +2332,7 @@ abstract class AbstractRegionMap implements RegionMap {
             // is in region, do nothing
           }
           if (!entryExisted) {
+            forceInvalidateEvent(event);
             owner.checkEntryNotFound(event.getKey());
           }
         } // while(retry)

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ad9c9b5c/gemfire-core/src/main/java/com/gemstone/gemfire/internal/cache/LocalRegion.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/main/java/com/gemstone/gemfire/internal/cache/LocalRegion.java b/gemfire-core/src/main/java/com/gemstone/gemfire/internal/cache/LocalRegion.java
index 1c5f321..2092508 100644
--- a/gemfire-core/src/main/java/com/gemstone/gemfire/internal/cache/LocalRegion.java
+++ b/gemfire-core/src/main/java/com/gemstone/gemfire/internal/cache/LocalRegion.java
@@ -804,11 +804,11 @@ public class LocalRegion extends AbstractRegion
     }
   }
 
-  public final IndexUpdater getIndexUpdater() {
+  public IndexUpdater getIndexUpdater() {
     return this.entries.getIndexUpdater();
   }
 
-  final boolean isCacheClosing()
+  boolean isCacheClosing()
   {
     return this.cache.isClosed();
   }

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ad9c9b5c/gemfire-core/src/test/java/com/gemstone/gemfire/cache30/CacheListenerTestCase.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/cache30/CacheListenerTestCase.java b/gemfire-core/src/test/java/com/gemstone/gemfire/cache30/CacheListenerTestCase.java
index 9a725e7..4e4147f 100644
--- a/gemfire-core/src/test/java/com/gemstone/gemfire/cache30/CacheListenerTestCase.java
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/cache30/CacheListenerTestCase.java
@@ -19,6 +19,7 @@ package com.gemstone.gemfire.cache30;
 import com.gemstone.gemfire.cache.*;
 //import com.gemstone.gemfire.cache.util.*;
 //import java.util.*;
+import com.gemstone.gemfire.internal.cache.AbstractRegionMap;
 
 /**
  * An abstract class whose test methods test the functionality of
@@ -234,11 +235,79 @@ public abstract class CacheListenerTestCase
     Region region =
       createRegion(name, factory.create());
 
+    // Does not exist so should not invoke listener
+    try {
+      region.invalidate(key);
+      fail("expected EntryNotFoundException");
+    } catch (EntryNotFoundException expected) {
+    }
+    assertFalse(listener.wasInvoked());
+
     region.create(key, value);
     assertTrue(listener.wasInvoked());
     region.invalidate(key);
     assertTrue(listener.wasInvoked());
+
+    // already invalid so should not invoke listener
+    region.invalidate(key);
+    assertFalse(listener.wasInvoked());
   }
+  
+  public void testCacheListenerAfterInvalidateWithForce() throws CacheException {
+    AbstractRegionMap.FORCE_INVALIDATE_EVENT = true;
+    try {
+      String name = this.getUniqueName();
+      final Object key = this.getUniqueName();
+      final Object value = new Integer(42);
+
+      TestCacheListener listener = new TestCacheListener() {
+          int invalidateCount = 0;
+          public void afterCreate2(EntryEvent event) {
+            // This method will get invoked when the region is populated
+          }
+
+          public void afterInvalidate2(EntryEvent event) {
+            invalidateCount++;
+            assertEquals(key, event.getKey());
+            if (invalidateCount == 2) {
+              assertEquals(value, event.getOldValue());
+            } else {
+              assertNull(event.getOldValue());
+            }
+            assertNull(event.getNewValue());
+            assertFalse(event.isLoad());
+            assertFalse(event.isLocalLoad());
+            assertFalse(event.isNetLoad());
+            assertFalse(event.isNetSearch());
+          }
+        };
+
+      AttributesFactory factory =
+        new AttributesFactory(getRegionAttributes());
+      factory.setCacheListener(listener);
+      Region region =
+        createRegion(name, factory.create());
+
+      // Does not exist but should still invoke listener
+      try {
+        region.invalidate(key);
+        fail("expected EntryNotFoundException");
+      } catch (EntryNotFoundException expected) {
+      }
+      assertTrue(listener.wasInvoked());
+
+      region.create(key, value);
+      assertTrue(listener.wasInvoked());
+      region.invalidate(key);
+      assertTrue(listener.wasInvoked());
+      // already invalid but should still invoke listener
+      region.invalidate(key);
+      assertTrue(listener.wasInvoked());
+    } finally {
+      AbstractRegionMap.FORCE_INVALIDATE_EVENT = false;
+    }
+  }
+
 
   /**
    * Tests that the <code>CacheListener</code> is called after a region

http://git-wip-us.apache.org/repos/asf/incubator-geode/blob/ad9c9b5c/gemfire-core/src/test/java/com/gemstone/gemfire/internal/cache/AbstractRegionMapTest.java
----------------------------------------------------------------------
diff --git a/gemfire-core/src/test/java/com/gemstone/gemfire/internal/cache/AbstractRegionMapTest.java b/gemfire-core/src/test/java/com/gemstone/gemfire/internal/cache/AbstractRegionMapTest.java
new file mode 100644
index 0000000..b54bbe2
--- /dev/null
+++ b/gemfire-core/src/test/java/com/gemstone/gemfire/internal/cache/AbstractRegionMapTest.java
@@ -0,0 +1,186 @@
+/*
+ * 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 com.gemstone.gemfire.internal.cache;
+
+import static org.junit.Assert.*;
+import static org.mockito.Mockito.*;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.gemstone.gemfire.cache.DataPolicy;
+import com.gemstone.gemfire.cache.EntryNotFoundException;
+import com.gemstone.gemfire.cache.Operation;
+import com.gemstone.gemfire.test.junit.categories.UnitTest;
+
+@Category(UnitTest.class)
+public class AbstractRegionMapTest {
+
+  @BeforeClass
+  public static void setUpBeforeClass() throws Exception {
+  }
+
+  @AfterClass
+  public static void tearDownAfterClass() throws Exception {
+  }
+
+  @Before
+  public void setUp() throws Exception {
+  }
+
+  @After
+  public void tearDown() throws Exception {
+  }
+
+  @Test
+  public void invalidateOfNonExistentRegionThrowsEntryNotFound() {
+    TestableAbstractRegionMap arm = new TestableAbstractRegionMap();
+    EntryEventImpl event = createEventForInvalidate(arm.owner);
+    when(arm.owner.isInitialized()).thenReturn(true);
+
+    try {
+      arm.invalidate(event, true, false, false);
+      fail("expected EntryNotFoundException");
+    } catch (EntryNotFoundException expected) {
+    }
+    verify(arm.owner, never()).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean());
+    verify(arm.owner, never()).invokeInvalidateCallbacks(any(), any(), anyBoolean());
+  }
+  
+  @Test
+  public void invalidateOfNonExistentRegionThrowsEntryNotFoundWithForce() {
+    AbstractRegionMap.FORCE_INVALIDATE_EVENT = true;
+    try {
+      TestableAbstractRegionMap arm = new TestableAbstractRegionMap();
+      EntryEventImpl event = createEventForInvalidate(arm.owner);
+      when(arm.owner.isInitialized()).thenReturn(true);
+
+      try {
+        arm.invalidate(event, true, false, false);
+        fail("expected EntryNotFoundException");
+      } catch (EntryNotFoundException expected) {
+      }
+      verify(arm.owner, never()).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean());
+      verify(arm.owner, times(1)).invokeInvalidateCallbacks(any(), any(), anyBoolean());
+    } finally {
+      AbstractRegionMap.FORCE_INVALIDATE_EVENT = false;
+    }
+  }
+  
+  @Test
+  public void invalidateOfAlreadyInvalidEntryReturnsFalse() {
+    TestableAbstractRegionMap arm = new TestableAbstractRegionMap();
+    EntryEventImpl event = createEventForInvalidate(arm.owner);
+    
+    // invalidate on region that is not initialized should create
+    // entry in map as invalid.
+    try {
+      arm.invalidate(event, true, false, false);
+      fail("expected EntryNotFoundException");
+    } catch (EntryNotFoundException expected) {
+    }
+    
+    when(arm.owner.isInitialized()).thenReturn(true);
+    assertFalse(arm.invalidate(event, true, false, false));
+    verify(arm.owner, never()).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean());
+    verify(arm.owner, never()).invokeInvalidateCallbacks(any(), any(), anyBoolean());
+  }
+
+  @Test
+  public void invalidateOfAlreadyInvalidEntryReturnsFalseWithForce() {
+    AbstractRegionMap.FORCE_INVALIDATE_EVENT = true;
+    try {
+      TestableAbstractRegionMap arm = new TestableAbstractRegionMap();
+      EntryEventImpl event = createEventForInvalidate(arm.owner);
+
+      // invalidate on region that is not initialized should create
+      // entry in map as invalid.
+      try {
+        arm.invalidate(event, true, false, false);
+        fail("expected EntryNotFoundException");
+      } catch (EntryNotFoundException expected) {
+      }
+
+      when(arm.owner.isInitialized()).thenReturn(true);
+      assertFalse(arm.invalidate(event, true, false, false));
+      verify(arm.owner, never()).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean());
+      verify(arm.owner, times(1)).invokeInvalidateCallbacks(any(), any(), anyBoolean());
+    } finally {
+      AbstractRegionMap.FORCE_INVALIDATE_EVENT = false;
+    }
+  }
+
+  private EntryEventImpl createEventForInvalidate(LocalRegion lr) {
+    Object key = "key";
+    when(lr.getKeyInfo(key)).thenReturn(new KeyInfo(key, null, null));
+    return EntryEventImpl.create(lr, Operation.INVALIDATE, key, false, null, true, false);
+  }
+  
+  @Test
+  public void invalidateForceNewEntryOfAlreadyInvalidEntryReturnsFalse() {
+    TestableAbstractRegionMap arm = new TestableAbstractRegionMap();
+    EntryEventImpl event = createEventForInvalidate(arm.owner);
+
+    // invalidate on region that is not initialized should create
+    // entry in map as invalid.
+    assertTrue(arm.invalidate(event, true, true, false));
+    verify(arm.owner, times(1)).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean());
+    
+    when(arm.owner.isInitialized()).thenReturn(true);
+    assertFalse(arm.invalidate(event, true, true, false));
+    verify(arm.owner, times(1)).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean());
+    verify(arm.owner, never()).invokeInvalidateCallbacks(any(), any(), anyBoolean());
+  }
+
+  @Test
+  public void invalidateForceNewEntryOfAlreadyInvalidEntryReturnsFalseWithForce() {
+    AbstractRegionMap.FORCE_INVALIDATE_EVENT = true;
+    try {
+      TestableAbstractRegionMap arm = new TestableAbstractRegionMap();
+      EntryEventImpl event = createEventForInvalidate(arm.owner);
+
+      // invalidate on region that is not initialized should create
+      // entry in map as invalid.
+      assertTrue(arm.invalidate(event, true, true, false));
+      verify(arm.owner, times(1)).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean());
+      verify(arm.owner, never()).invokeInvalidateCallbacks(any(), any(), anyBoolean());
+
+      when(arm.owner.isInitialized()).thenReturn(true);
+      assertFalse(arm.invalidate(event, true, true, false));
+      verify(arm.owner, times(1)).basicInvalidatePart2(any(), any(), anyBoolean(), anyBoolean());
+      verify(arm.owner, times(1)).invokeInvalidateCallbacks(any(), any(), anyBoolean());
+    } finally {
+      AbstractRegionMap.FORCE_INVALIDATE_EVENT = false;
+    }
+  }
+
+  public static class TestableAbstractRegionMap extends AbstractRegionMap {
+    public LocalRegion owner;
+
+    protected TestableAbstractRegionMap() {
+      super(null);
+      this.owner = mock(LocalRegion.class);
+      when(this.owner.getDataPolicy()).thenReturn(DataPolicy.REPLICATE);
+      doThrow(EntryNotFoundException.class).when(this.owner).checkEntryNotFound(any());
+      initialize(owner, new Attributes(), null, false);
+    }
+  }
+}