Eugene Nedzvetsky created GEODE-5486:
----------------------------------------
Summary: Two transactions with only read operations produce
CommitConflictException if property gemfire.detectReadConflict=true
Key: GEODE-5486
URL: https://issues.apache.org/jira/browse/GEODE-5486
Project: Geode
Issue Type: Bug
Components: transactions
Reporter: Eugene Nedzvetsky
Application was started with system property gemfire.detectReadConflict = true.
Some transactions with only read operations(region#get, ....)were broken after
that with an exception CommitConflictException:
{code}
java.util.concurrent.ExecutionException:
org.apache.geode.cache.CommitConflictException: The key key1 in region
/TXJReadConflictTest was being modified by another transaction locally.
at java.util.concurrent.FutureTask.report(FutureTask.java:122)
at java.util.concurrent.FutureTask.get(FutureTask.java:192)
at
org.apache.geode.TXJReadConflictTest.testDetectReadConflict(TXJReadConflictTest.java:131)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at
org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at
org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at
org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at
org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at
org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at
org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at
org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at
com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at
com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at
com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.apache.geode.cache.CommitConflictException: The key key1 in
region /TXJReadConflictTest was being modified by another transaction locally.
at
org.apache.geode.internal.cache.TXReservationMgr.checkSetForConflict(TXReservationMgr.java:107)
at
org.apache.geode.internal.cache.TXReservationMgr.checkForConflict(TXReservationMgr.java:77)
at
org.apache.geode.internal.cache.TXReservationMgr.makeReservation(TXReservationMgr.java:56)
at
org.apache.geode.internal.cache.TXLockRequest.txLocalLock(TXLockRequest.java:151)
at
org.apache.geode.internal.cache.TXLockRequest.obtain(TXLockRequest.java:84)
at org.apache.geode.internal.cache.TXState.reserveAndCheck(TXState.java:340)
at org.apache.geode.internal.cache.TXState.commit(TXState.java:411)
at
org.apache.geode.internal.cache.TXStateProxyImpl.commit(TXStateProxyImpl.java:210)
at
org.apache.geode.internal.cache.TXManagerImpl.commit(TXManagerImpl.java:413)
at
org.apache.geode.TXJReadConflictTest.lambda$testDetectReadConflict$1(TXJReadConflictTest.java:126)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
{code}
I reproduced this issue and created Integration test:
{code}
package org.apache.geode;
/*
* 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.
*/
import static org.apache.geode.distributed.ConfigurationProperties.MCAST_PORT;
import static org.junit.Assert.fail;
import java.util.Properties;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.locks.StampedLock;
import org.apache.geode.cache.AttributesFactory;
import org.apache.geode.cache.Cache;
import org.apache.geode.cache.CacheException;
import org.apache.geode.cache.CacheFactory;
import org.apache.geode.cache.CacheTransactionManager;
import org.apache.geode.cache.Region;
import org.apache.geode.cache.Scope;
import org.apache.geode.distributed.DistributedSystem;
import org.apache.geode.internal.cache.GemFireCacheImpl;
import org.apache.geode.internal.cache.TXManagerImpl;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
* Tests basic detectReadConflict mode transaction functionality
*/
public class TXJReadConflictTest {
private CacheTransactionManager txMgr;
protected GemFireCacheImpl cache;
protected Region<String, String> region;
@Before
public void setUpTXJUnitTest() throws Exception {
System.setProperty("gemfire.detectReadConflicts", "true");
createCache();
}
@After
public void tearDownTXJUnitTest() {
closeCache();
}
private void closeCache() {
if (this.cache != null) {
if (this.txMgr != null) {
try {
this.txMgr.rollback();
} catch (IllegalStateException ignore) {
}
}
this.region = null;
this.txMgr = null;
Cache c = this.cache;
this.cache = null;
c.close();
}
}
private void createCache() {
Properties properties = new Properties();
properties.setProperty(MCAST_PORT, "0"); // loner
this.cache = (GemFireCacheImpl)
CacheFactory.create(DistributedSystem.connect(properties));
createRegion();
this.txMgr = this.cache.getCacheTransactionManager();
}
private void createRegion() {
AttributesFactory<String, String> attributesFactory = new
AttributesFactory<>();
attributesFactory.setScope(Scope.DISTRIBUTED_NO_ACK);
attributesFactory.setConcurrencyChecksEnabled(false); // test
validation expects this behavior
attributesFactory.setIndexMaintenanceSynchronous(true);
this.region = this.cache.createRegion(getClass().getSimpleName(),
attributesFactory.create());
}
/**
* Test that two transactions with only read operations don't produce
CommitConflictException
* This test fill some initial value on startup and after that create
different threads with region#get operations
* Sync two different threads commit time through lock
*/
@Test(/* no exception expected */)
public void testDetectReadConflict() throws CacheException,
ExecutionException, InterruptedException {
final TXManagerImpl txMgrImpl = (TXManagerImpl) this.txMgr;
//fill some initial key-value on start
txMgrImpl.begin();
this.region.put("key1", "value1"); // non-tx
txMgrImpl.commit();
ExecutorService executor = Executors.newFixedThreadPool(2);
for (int i = 0; i < 20; i++) {
final StampedLock lock = new StampedLock();
lock.asWriteLock().lock();
//
Future<?> future1 = executor.submit(() -> {
CacheTransactionManager txMgr2 =
cache.getCacheTransactionManager();
txMgr2.begin();
region.get("key1"); // non-tx
lock.asReadLock().lock();
txMgr2.commit();
});
Future<?> future2 = executor.submit(() -> {
CacheTransactionManager txMgr3 =
cache.getCacheTransactionManager();
txMgr3.begin();
region.get("key1"); // non-tx
lock.asReadLock().lock();
txMgr3.commit();
});
pause(100);
lock.asWriteLock().unlock();
future1.get();
future2.get();
}
}
private void pause(int msWait) {
try {
Thread.sleep(msWait);
} catch (InterruptedException ignore) {
fail("interrupted");
}
}
}
{code}
--
This message was sent by Atlassian JIRA
(v7.6.3#76005)