You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@geode.apache.org by es...@apache.org on 2019/01/09 23:47:52 UTC

[geode] branch feature/GEODE-6261 updated: GEODE-6261: Do not break repeatable read by incorrectly cleaning up TXEntries

This is an automated email from the ASF dual-hosted git repository.

eshu11 pushed a commit to branch feature/GEODE-6261
in repository https://gitbox.apache.org/repos/asf/geode.git


The following commit(s) were added to refs/heads/feature/GEODE-6261 by this push:
     new 309bd7b  GEODE-6261: Do not break repeatable read by incorrectly cleaning up TXEntries
309bd7b is described below

commit 309bd7b6bc4a1b111ee9b5cbbb189edc1da1f138
Author: eshu <es...@pivotal.io>
AuthorDate: Wed Jan 9 15:27:37 2019 -0800

    GEODE-6261: Do not break repeatable read by incorrectly cleaning up TXEntries
    
     * Do not cleanup existing TXEntries if finding an entry does not have expected value.
---
 .../java/org/apache/geode/TXJUnitTest.java         | 47 ++++++++++++++++++++++
 .../org/apache/geode/internal/cache/TXState.java   |  1 -
 .../apache/geode/internal/cache/TXStateTest.java   | 28 ++++++++++++-
 3 files changed, 74 insertions(+), 2 deletions(-)

diff --git a/geode-core/src/integrationTest/java/org/apache/geode/TXJUnitTest.java b/geode-core/src/integrationTest/java/org/apache/geode/TXJUnitTest.java
index 408cc6b..545baf2 100644
--- a/geode-core/src/integrationTest/java/org/apache/geode/TXJUnitTest.java
+++ b/geode-core/src/integrationTest/java/org/apache/geode/TXJUnitTest.java
@@ -70,6 +70,8 @@ import org.apache.geode.cache.PartitionAttributes;
 import org.apache.geode.cache.PartitionAttributesFactory;
 import org.apache.geode.cache.Region;
 import org.apache.geode.cache.RegionEvent;
+import org.apache.geode.cache.RegionFactory;
+import org.apache.geode.cache.RegionShortcut;
 import org.apache.geode.cache.Scope;
 import org.apache.geode.cache.TimeoutException;
 import org.apache.geode.cache.TransactionEvent;
@@ -7208,4 +7210,49 @@ public class TXJUnitTest {
     assertTrue(!ev.getOperation().isNetSearch());
   }
 
+  private enum OperationType {
+    REPLACE, REMOVE
+  }
+
+  @Test
+  public void removeShouldNotCleanupRepeatableReadTXEntriesIfEntryNotFound() {
+    repeatableReadTXEntriesShouldNotBeCleanedUpIfEntryNotFound(OperationType.REMOVE);
+  }
+
+  @Test
+  public void replaceShouldNotCleanupRepeatableReadTXEntriesIfEntryNotFound() {
+    repeatableReadTXEntriesShouldNotBeCleanedUpIfEntryNotFound(OperationType.REPLACE);
+  }
+
+  private void repeatableReadTXEntriesShouldNotBeCleanedUpIfEntryNotFound(OperationType type) {
+    RegionFactory regionFactory = cache.createRegionFactory(RegionShortcut.REPLICATE);
+    Region<Integer, String> region = regionFactory.create(getUniqueName());
+    region.put(1, "value1");
+    region.put(2, "value2");
+    txMgr.begin();
+    region.put(1, "newValue1");
+    TransactionId transaction1 = txMgr.suspend();
+
+    txMgr.begin();
+    region.get(1); // a repeatable read operation
+    switch (type) {
+      case REMOVE:
+        assertThat(region.remove(2, "nonExistingValue")).isFalse();
+        break;
+      case REPLACE:
+        assertThat(region.replace(2, "newValue", "nonExistingValue")).isFalse();
+        break;
+      default:
+        throw new RuntimeException("Unknown operation");
+    };
+    TransactionId transaction2 = txMgr.suspend();
+
+    txMgr.resume(transaction1);
+    txMgr.commit();
+
+    txMgr.resume(transaction2);
+    region.put(1, "anotherValue");
+    assertThatThrownBy(() -> txMgr.commit()).isInstanceOf(CommitConflictException.class);
+  }
+
 }
diff --git a/geode-core/src/main/java/org/apache/geode/internal/cache/TXState.java b/geode-core/src/main/java/org/apache/geode/internal/cache/TXState.java
index 3474a2d..2af3146 100644
--- a/geode-core/src/main/java/org/apache/geode/internal/cache/TXState.java
+++ b/geode-core/src/main/java/org/apache/geode/internal/cache/TXState.java
@@ -1553,7 +1553,6 @@ public class TXState implements TXStateInterface {
       if (expectedOldValue != null) {
         Object val = result.getNearSidePendingValue();
         if (!AbstractRegionEntry.checkExpectedOldValue(expectedOldValue, val, internalRegion)) {
-          txr.cleanupNonDirtyEntries(internalRegion);
           throw new EntryNotFoundException(
               "The current value was not equal to expected value.");
         }
diff --git a/geode-core/src/test/java/org/apache/geode/internal/cache/TXStateTest.java b/geode-core/src/test/java/org/apache/geode/internal/cache/TXStateTest.java
index 2727ec9..fad1973 100644
--- a/geode-core/src/test/java/org/apache/geode/internal/cache/TXStateTest.java
+++ b/geode-core/src/test/java/org/apache/geode/internal/cache/TXStateTest.java
@@ -34,7 +34,9 @@ import org.junit.Before;
 import org.junit.Test;
 
 import org.apache.geode.cache.CommitConflictException;
+import org.apache.geode.cache.EntryNotFoundException;
 import org.apache.geode.cache.FailedSynchronizationException;
+import org.apache.geode.cache.RegionAttributes;
 import org.apache.geode.cache.SynchronizationCommitConflictException;
 import org.apache.geode.cache.TransactionDataNodeHasDepartedException;
 import org.apache.geode.cache.TransactionException;
@@ -180,7 +182,7 @@ public class TXStateTest {
   }
 
   @Test
-  public void txReadEntryDoesNotCleanupNonDirtyEntriesIfRegionCreateReadEntryReturnsNull() {
+  public void txReadEntryDoesNotCleanupTXEntriesIfRegionCreateReadEntryReturnsNull() {
     TXState txState = spy(new TXState(txStateProxy, true));
     KeyInfo keyInfo = mock(KeyInfo.class);
     Object key = new Object();
@@ -197,4 +199,28 @@ public class TXStateTest {
     verify(txRegionState, never()).cleanupNonDirtyEntries(dataRegion);
   }
 
+  @Test
+  public void txReadEntryDoesNotCleanupTXEntriesIfEntryNotFound() {
+    TXState txState = spy(new TXState(txStateProxy, true));
+    KeyInfo keyInfo = mock(KeyInfo.class);
+    Object key = new Object();
+    Object expectedValue = new Object();
+    InternalRegion internalRegion = mock(InternalRegion.class);
+    InternalRegion dataRegion = mock(InternalRegion.class);
+    TXRegionState txRegionState = mock(TXRegionState.class);
+    TXEntryState txEntryState = mock(TXEntryState.class);
+    when(internalRegion.getDataRegionForWrite(keyInfo)).thenReturn(dataRegion);
+    when(internalRegion.getAttributes()).thenReturn(mock(RegionAttributes.class));
+    when(internalRegion.getCache()).thenReturn(mock(InternalCache.class));
+    when(txState.txReadRegion(dataRegion)).thenReturn(txRegionState);
+    when(keyInfo.getKey()).thenReturn(key);
+    when(txRegionState.readEntry(key)).thenReturn(txEntryState);
+    when(txEntryState.getNearSidePendingValue()).thenReturn("currentVal");
+
+    assertThatThrownBy(
+        () -> txState.txReadEntry(keyInfo, internalRegion, true, expectedValue, false))
+            .isInstanceOf(EntryNotFoundException.class);
+    verify(txRegionState, never()).cleanupNonDirtyEntries(internalRegion);
+  }
+
 }