001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: SecondaryDeadlockTest.java,v 1.10.2.2 2008/01/07 15:14:24 cwl Exp $
007: */
008:
009: package com.sleepycat.collections.test;
010:
011: import com.sleepycat.je.Database;
012: import com.sleepycat.je.DeadlockException;
013: import com.sleepycat.je.Environment;
014: import com.sleepycat.je.TransactionConfig;
015: import com.sleepycat.collections.StoredSortedMap;
016: import com.sleepycat.collections.TransactionRunner;
017: import com.sleepycat.collections.TransactionWorker;
018: import com.sleepycat.util.ExceptionUnwrapper;
019: import junit.framework.Test;
020: import junit.framework.TestCase;
021: import junit.framework.TestSuite;
022:
023: /**
024: * Tests whether secondary access can cause a self-deadlock when reading via a
025: * secondary because the collections API secondary implementation in DB 4.2
026: * opens two cursors. Part of the problem in [#10516] was because the
027: * secondary get() was not done in a txn. This problem should not occur in DB
028: * 4.3 and JE -- an ordinary deadlock occurs instead and is detected.
029: *
030: * @author Mark Hayes
031: */
032: public class SecondaryDeadlockTest extends TestCase {
033:
034: private static final Long N_ONE = new Long(1);
035: private static final Long N_101 = new Long(101);
036: private static final int N_ITERS = 20;
037: private static final int MAX_RETRIES = 1000;
038:
039: public static void main(String[] args) throws Exception {
040:
041: junit.framework.TestResult tr = junit.textui.TestRunner
042: .run(suite());
043: if (tr.errorCount() > 0 || tr.failureCount() > 0) {
044: System.exit(1);
045: } else {
046: System.exit(0);
047: }
048: }
049:
050: public static Test suite() throws Exception {
051:
052: TestSuite suite = new TestSuite(SecondaryDeadlockTest.class);
053: return suite;
054: }
055:
056: private Environment env;
057: private Database store;
058: private Database index;
059: private StoredSortedMap storeMap;
060: private StoredSortedMap indexMap;
061: private Exception exception;
062:
063: public SecondaryDeadlockTest(String name) {
064:
065: super (name);
066: }
067:
068: public void setUp() throws Exception {
069:
070: env = TestEnv.TXN.open("SecondaryDeadlockTest");
071: store = TestStore.BTREE_UNIQ.open(env, "store.db");
072: index = TestStore.BTREE_UNIQ.openIndex(store, "index.db");
073: storeMap = new StoredSortedMap(store, TestStore.BTREE_UNIQ
074: .getKeyBinding(), TestStore.BTREE_UNIQ
075: .getValueBinding(), true);
076: indexMap = new StoredSortedMap(index, TestStore.BTREE_UNIQ
077: .getKeyBinding(), TestStore.BTREE_UNIQ
078: .getValueBinding(), true);
079: }
080:
081: public void tearDown() {
082:
083: if (index != null) {
084: try {
085: index.close();
086: } catch (Exception e) {
087: System.out
088: .println("Ignored exception during tearDown: "
089: + e);
090: }
091: }
092: if (store != null) {
093: try {
094: store.close();
095: } catch (Exception e) {
096: System.out
097: .println("Ignored exception during tearDown: "
098: + e);
099: }
100: }
101: if (env != null) {
102: try {
103: env.close();
104: } catch (Exception e) {
105: System.out
106: .println("Ignored exception during tearDown: "
107: + e);
108: }
109: }
110: /* Allow GC of DB objects in the test case. */
111: env = null;
112: store = null;
113: index = null;
114: storeMap = null;
115: indexMap = null;
116: }
117:
118: public void testSecondaryDeadlock() throws Exception {
119:
120: final TransactionRunner runner = new TransactionRunner(env);
121: runner.setMaxRetries(MAX_RETRIES);
122:
123: /*
124: * This test deadlocks a lot at degree 3 serialization. In debugging
125: * this I discovered it was not due to phantom prevention per se but
126: * just to a change in timing.
127: */
128: TransactionConfig txnConfig = new TransactionConfig();
129: runner.setTransactionConfig(txnConfig);
130:
131: /*
132: * A thread to do put() and delete() via the primary, which will lock
133: * the primary first then the secondary. Uses transactions.
134: */
135: final Thread thread1 = new Thread(new Runnable() {
136: public void run() {
137: try {
138: /* The TransactionRunner performs retries. */
139: for (int i = 0; i < N_ITERS; i += 1) {
140: runner.run(new TransactionWorker() {
141: public void doWork() throws Exception {
142: assertEquals(null, storeMap.put(N_ONE,
143: N_101));
144: }
145: });
146: runner.run(new TransactionWorker() {
147: public void doWork() throws Exception {
148: assertEquals(N_101, storeMap
149: .remove(N_ONE));
150: }
151: });
152: }
153: } catch (Exception e) {
154: e.printStackTrace();
155: exception = e;
156: }
157: }
158: }, "ThreadOne");
159:
160: /*
161: * A thread to get() via the secondary, which will lock the secondary
162: * first then the primary. Does not use a transaction.
163: */
164: final Thread thread2 = new Thread(new Runnable() {
165: public void run() {
166: try {
167: for (int i = 0; i < N_ITERS; i += 1) {
168: for (int j = 0; j < MAX_RETRIES; j += 1) {
169: try {
170: Object value = indexMap.get(N_ONE);
171: assertTrue(value == null
172: || N_101.equals(value));
173: break;
174: } catch (Exception e) {
175: e = ExceptionUnwrapper.unwrap(e);
176: if (e instanceof DeadlockException) {
177: continue; /* Retry on deadlock. */
178: } else {
179: throw e;
180: }
181: }
182: }
183: }
184: } catch (Exception e) {
185: e.printStackTrace();
186: exception = e;
187: }
188: }
189: }, "ThreadTwo");
190:
191: thread1.start();
192: thread2.start();
193: thread1.join();
194: thread2.join();
195:
196: index.close();
197: index = null;
198: store.close();
199: store = null;
200: env.close();
201: env = null;
202:
203: if (exception != null) {
204: fail(exception.toString());
205: }
206: }
207: }
|