/*
* JBoss, Home of Professional Open Source
* Copyright 2010 Red Hat Inc. and/or its affiliates and other
* contributors as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a full listing of
* individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.infinispan.tx.dld;
import org.infinispan.config.Configuration;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.test.PerCacheExecutorThread;
import org.infinispan.test.SingleCacheManagerTest;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.infinispan.transaction.LockingMode;
import org.infinispan.util.concurrent.locks.DeadlockDetectedException;
import org.infinispan.util.concurrent.locks.DeadlockDetectingLockManager;
import static org.testng.Assert.assertEquals;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import javax.transaction.RollbackException;
/**
* Tests deadlock detection functionality for local caches.
*
* @author Mircea.Markus@jboss.com
*/
@Test(groups = "functional", testName = "tx.dld.LocalDeadlockDetectionTest")
public class LocalDeadlockDetectionTest extends SingleCacheManagerTest {
private PerCacheExecutorThread t1;
private PerCacheExecutorThread t2;
private DeadlockDetectingLockManager lockManager;
private Object response1;
private Object response2;
protected EmbeddedCacheManager createCacheManager() throws Exception {
cacheManager = TestCacheManagerFactory.createLocalCacheManager(false);
Configuration configuration = createConfig();
configuration.fluent().transaction().lockingMode(LockingMode.PESSIMISTIC);
cacheManager.defineConfiguration("test", configuration);
cache = cacheManager.getCache("test");
lockManager = (DeadlockDetectingLockManager) TestingUtil.extractLockManager(cache);
return cacheManager;
}
protected Configuration createConfig() {
Configuration configuration = getDefaultStandaloneConfig(true);
configuration.setEnableDeadlockDetection(true);
configuration.setUseLockStriping(false);
configuration.setExposeJmxStatistics(true);
return configuration;
}
@BeforeMethod
public void startExecutors() {
t1 = new PerCacheExecutorThread(cache, 0);
t2 = new PerCacheExecutorThread(cache, 1);
lockManager.resetStatistics();
}
@AfterMethod
public void stopExecutors() {
t1.stopThread();
t2.stopThread();
}
public void testDldPutAndPut() {
testLocalVsLocalTxDeadlock(PerCacheExecutorThread.Operations.PUT_KEY_VALUE,
PerCacheExecutorThread.Operations.PUT_KEY_VALUE);
if (response1 instanceof Exception) {
assertEquals("value_1_t2", cache.get("k1"));
assertEquals("value_2_t2", cache.get("k2"));
} else {
assertEquals("value_1_t1", cache.get("k1"));
assertEquals("value_2_t1", cache.get("k2"));
}
}
public void testDldPutAndRemove() {
testLocalVsLocalTxDeadlock(PerCacheExecutorThread.Operations.PUT_KEY_VALUE,
PerCacheExecutorThread.Operations.REMOVE_KEY);
if (response1 instanceof Exception) {
assertEquals(cache.get("k1"), null);
assertEquals("value_2_t2", cache.get("k2"));
} else {
assertEquals("value_1_t1", cache.get("k1"));
assertEquals(null, cache.get("k2"));
}
}
public void testDldRemoveAndPut() {
testLocalVsLocalTxDeadlock(PerCacheExecutorThread.Operations.REMOVE_KEY,
PerCacheExecutorThread.Operations.PUT_KEY_VALUE);
if (response1 instanceof Exception) {
System.out.println("t1 failure");
assertEquals(cache.get("k1"), "value_1_t2");
assertEquals(cache.get("k2"), null);
} else {
System.out.println("t2 failure");
assertEquals(cache.get("k1"), null);
assertEquals(cache.get("k2"), "value_2_t1");
}
}
public void testDldRemoveAndRemove() {
testLocalVsLocalTxDeadlock(PerCacheExecutorThread.Operations.REMOVE_KEY,
PerCacheExecutorThread.Operations.REMOVE_KEY);
if (response1 instanceof Exception) {
System.out.println("t1 failure");
assertEquals(cache.get("k1"), null);
assertEquals(cache.get("k2"), null);
} else {
System.out.println("t2 failure");
assertEquals(cache.get("k1"), null);
assertEquals(cache.get("k2"), null);
}
}
public void testDldPutAndReplace() {
cache.put("k1", "initial_1");
cache.put("k2", "initial_2");
testLocalVsLocalTxDeadlock(PerCacheExecutorThread.Operations.PUT_KEY_VALUE,
PerCacheExecutorThread.Operations.REPLACE_KEY_VALUE);
if (response1 instanceof Exception) {
System.out.println("t1 failure");
assertEquals(cache.get("k1"), "value_1_t2");
assertEquals(cache.get("k2"), "value_2_t2");
} else {
System.out.println("t2 failure");
assertEquals(cache.get("k1"), "value_1_t1");
assertEquals(cache.get("k2"), "value_2_t1");
}
}
public void testDldReplaceAndPut() {
cache.put("k1", "initial_1");
cache.put("k2", "initial_2");
testLocalVsLocalTxDeadlock(PerCacheExecutorThread.Operations.REPLACE_KEY_VALUE,
PerCacheExecutorThread.Operations.PUT_KEY_VALUE);
if (response1 instanceof Exception) {
System.out.println("t1 failure");
assertEquals(cache.get("k1"), "value_1_t2");
assertEquals(cache.get("k2"), "value_2_t2");
} else {
System.out.println("t2 failure");
assertEquals(cache.get("k1"), "value_1_t1");
assertEquals(cache.get("k2"), "value_2_t1");
}
}
private void testLocalVsLocalTxDeadlock(PerCacheExecutorThread.Operations firstOperation, PerCacheExecutorThread.Operations secondOperation) {
assert PerCacheExecutorThread.OperationsResult.BEGIN_TX_OK == t1.execute(PerCacheExecutorThread.Operations.BEGIN_TX);
assert PerCacheExecutorThread.OperationsResult.BEGIN_TX_OK == t2.execute(PerCacheExecutorThread.Operations.BEGIN_TX);
t1.setKeyValue("k1", "value_1_t1");
t2.setKeyValue("k2", "value_2_t2");
assertEquals(t1.execute(firstOperation), firstOperation.getCorrespondingOkResult());
assertEquals(t2.execute(firstOperation), firstOperation.getCorrespondingOkResult());
assert lockManager.isLocked("k1");
assert lockManager.isLocked("k2");
t1.setKeyValue("k2", "value_2_t1");
t2.setKeyValue("k1", "value_1_t2");
t1.executeNoResponse(secondOperation);
t2.executeNoResponse(secondOperation);
response1 = t1.waitForResponse();
response2 = t2.waitForResponse();
assert xor(response1 instanceof DeadlockDetectedException, response2 instanceof DeadlockDetectedException) : "expected one and only one exception: " + response1 + ", " + response2;
assert xor(response1 == secondOperation.getCorrespondingOkResult(), response2 == secondOperation.getCorrespondingOkResult()) : "expected one and only one exception: " + response1 + ", " + response2;
assert lockManager.isLocked("k1");
assert lockManager.isLocked("k2");
assert lockManager.getOwner("k1") == lockManager.getOwner("k2");
if (response1 instanceof Exception) {
assertEquals(PerCacheExecutorThread.OperationsResult.COMMIT_TX_OK, t2.execute(PerCacheExecutorThread.Operations.COMMIT_TX));
assert t1.execute(PerCacheExecutorThread.Operations.COMMIT_TX) instanceof RollbackException;
} else {
assert PerCacheExecutorThread.OperationsResult.COMMIT_TX_OK == t1.execute(PerCacheExecutorThread.Operations.COMMIT_TX);
assert t2.execute(PerCacheExecutorThread.Operations.COMMIT_TX) instanceof RollbackException;
}
assert lockManager.getNumberOfLocksHeld() == 0;
assertEquals(lockManager.getDetectedLocalDeadlocks(), 1);
}
}