001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: TxnEndTest.java,v 1.67.2.3 2008/01/07 15:14:34 cwl Exp $
007: */
008:
009: package com.sleepycat.je.txn;
010:
011: import java.io.File;
012: import java.io.IOException;
013: import java.util.Arrays;
014:
015: import junit.framework.TestCase;
016:
017: import com.sleepycat.je.Cursor;
018: import com.sleepycat.je.CursorConfig;
019: import com.sleepycat.je.Database;
020: import com.sleepycat.je.DatabaseConfig;
021: import com.sleepycat.je.DatabaseEntry;
022: import com.sleepycat.je.DatabaseException;
023: import com.sleepycat.je.DbInternal;
024: import com.sleepycat.je.Environment;
025: import com.sleepycat.je.EnvironmentConfig;
026: import com.sleepycat.je.EnvironmentStats;
027: import com.sleepycat.je.LockMode;
028: import com.sleepycat.je.OperationStatus;
029: import com.sleepycat.je.Transaction;
030: import com.sleepycat.je.TransactionStats;
031: import com.sleepycat.je.VerifyConfig;
032: import com.sleepycat.je.config.EnvironmentParams;
033: import com.sleepycat.je.dbi.DatabaseImpl;
034: import com.sleepycat.je.junit.JUnitThread;
035: import com.sleepycat.je.log.FileManager;
036: import com.sleepycat.je.util.TestUtils;
037:
038: /*
039: * Test transaction aborts and commits.
040: */
041: public class TxnEndTest extends TestCase {
042: private static final int NUM_DBS = 1;
043: private Environment env;
044: private File envHome;
045: private Database[] dbs;
046: private Cursor[] cursors;
047:
048: public TxnEndTest() throws DatabaseException {
049:
050: envHome = new File(System.getProperty(TestUtils.DEST_DIR));
051: }
052:
053: public void setUp() throws IOException, DatabaseException {
054:
055: TestUtils.removeFiles("Setup", envHome, FileManager.JE_SUFFIX);
056:
057: /*
058: * Run environment without in compressor on so we can check the
059: * compressor queue in a deterministic way.
060: */
061: EnvironmentConfig envConfig = TestUtils.initEnvConfig();
062: envConfig.setTransactional(true);
063: envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(),
064: "6");
065: envConfig.setConfigParam(EnvironmentParams.ENV_RUN_INCOMPRESSOR
066: .getName(), "false");
067: envConfig.setAllowCreate(true);
068: env = new Environment(envHome, envConfig);
069: }
070:
071: public void tearDown() throws IOException, DatabaseException {
072:
073: if (env != null) {
074: try {
075: env.close();
076: } catch (Exception e) {
077: System.out.println("tearDown: " + e);
078: }
079: }
080: env = null;
081:
082: TestUtils.removeFiles("TearDown", envHome,
083: FileManager.JE_SUFFIX);
084: }
085:
086: private void createDbs() throws DatabaseException {
087:
088: // Make databases
089: dbs = new Database[NUM_DBS];
090: cursors = new Cursor[NUM_DBS];
091:
092: DatabaseConfig dbConfig = new DatabaseConfig();
093: dbConfig.setTransactional(true);
094: dbConfig.setAllowCreate(true);
095: for (int i = 0; i < NUM_DBS; i++) {
096: dbs[i] = env.openDatabase(null, "testDB" + i, dbConfig);
097: }
098: }
099:
100: private void closeAll() throws DatabaseException {
101:
102: for (int i = 0; i < NUM_DBS; i++) {
103: dbs[i].close();
104: }
105: dbs = null;
106: env.close();
107: env = null;
108: }
109:
110: /**
111: * Create cursors with this owning transaction
112: */
113: private void createCursors(Transaction txn)
114: throws DatabaseException {
115:
116: for (int i = 0; i < cursors.length; i++) {
117: cursors[i] = dbs[i].openCursor(txn, null);
118: }
119: }
120:
121: /**
122: * Close the current set of cursors
123: */
124: private void closeCursors() throws DatabaseException {
125:
126: for (int i = 0; i < cursors.length; i++) {
127: cursors[i].close();
128: }
129: }
130:
131: /**
132: * Insert keys from i=start; i <end using a cursor
133: */
134: private void cursorInsertData(int start, int end)
135: throws DatabaseException {
136:
137: DatabaseEntry key = new DatabaseEntry();
138: DatabaseEntry data = new DatabaseEntry();
139: for (int i = 0; i < NUM_DBS; i++) {
140: for (int d = start; d < end; d++) {
141: key.setData(TestUtils.getTestArray(d));
142: data.setData(TestUtils.getTestArray(d));
143: cursors[i].put(key, data);
144: }
145: }
146: }
147:
148: /**
149: * Insert keys from i=start; i < end using a db
150: */
151: private void dbInsertData(int start, int end, Transaction txn)
152: throws DatabaseException {
153:
154: DatabaseEntry key = new DatabaseEntry();
155: DatabaseEntry data = new DatabaseEntry();
156: for (int i = 0; i < NUM_DBS; i++) {
157: for (int d = start; d < end; d++) {
158: key.setData(TestUtils.getTestArray(d));
159: data.setData(TestUtils.getTestArray(d));
160: dbs[i].put(txn, key, data);
161: }
162: }
163: }
164:
165: /**
166: * Modify keys from i=start; i <end
167: */
168: private void cursorModifyData(int start, int end, int valueOffset)
169: throws DatabaseException {
170:
171: DatabaseEntry key = new DatabaseEntry();
172: DatabaseEntry data = new DatabaseEntry();
173: for (int i = 0; i < NUM_DBS; i++) {
174: OperationStatus status = cursors[i].getFirst(key, data,
175: LockMode.DEFAULT);
176: for (int d = start; d < end; d++) {
177: assertEquals(OperationStatus.SUCCESS, status);
178: byte[] changedVal = TestUtils.getTestArray(d
179: + valueOffset);
180: data.setData(changedVal);
181: cursors[i].putCurrent(data);
182: status = cursors[i]
183: .getNext(key, data, LockMode.DEFAULT);
184: }
185: }
186: }
187:
188: /**
189: * Delete records from i = start; i < end.
190: */
191: private void cursorDeleteData(int start, int end)
192: throws DatabaseException {
193:
194: DatabaseEntry key = new DatabaseEntry();
195: DatabaseEntry foundData = new DatabaseEntry();
196: for (int i = 0; i < NUM_DBS; i++) {
197: for (int d = start; d < end; d++) {
198: byte[] searchValue = TestUtils.getTestArray(d);
199: key.setData(searchValue);
200: OperationStatus status = cursors[i].getSearchKey(key,
201: foundData, LockMode.DEFAULT);
202: assertEquals(OperationStatus.SUCCESS, status);
203: assertEquals(OperationStatus.SUCCESS, cursors[i]
204: .delete());
205: }
206: }
207: }
208:
209: /**
210: * Delete records with a db.
211: */
212: private void dbDeleteData(int start, int end, Transaction txn)
213: throws DatabaseException {
214:
215: DatabaseEntry key = new DatabaseEntry();
216: for (int i = 0; i < NUM_DBS; i++) {
217: for (int d = start; d < end; d++) {
218: byte[] searchValue = TestUtils.getTestArray(d);
219: key.setData(searchValue);
220: dbs[i].delete(txn, key);
221: }
222: }
223: }
224:
225: /**
226: * Check that there are numKeys records in each db, and their value
227: * is i + offset.
228: */
229: private void verifyData(int numKeys, int valueOffset)
230: throws DatabaseException {
231:
232: for (int i = 0; i < NUM_DBS; i++) {
233: /* Run verify */
234: DatabaseImpl dbImpl = DbInternal.dbGetDatabaseImpl(dbs[i]);
235: assertTrue(dbImpl.verify(new VerifyConfig(), dbImpl
236: .getEmptyStats()));
237:
238: Cursor verifyCursor = dbs[i].openCursor(null,
239: CursorConfig.READ_UNCOMMITTED);
240: DatabaseEntry key = new DatabaseEntry();
241: DatabaseEntry data = new DatabaseEntry();
242: OperationStatus status = verifyCursor.getFirst(key, data,
243: LockMode.DEFAULT);
244: for (int d = 0; d < numKeys; d++) {
245: assertEquals("key=" + d, OperationStatus.SUCCESS,
246: status);
247: byte[] expected = TestUtils.getTestArray(d
248: + valueOffset);
249: assertTrue(Arrays.equals(expected, key.getData()));
250: assertTrue("Expected= "
251: + TestUtils.dumpByteArray(expected) + " saw="
252: + TestUtils.dumpByteArray(data.getData()),
253: Arrays.equals(expected, data.getData()));
254: status = verifyCursor.getNext(key, data,
255: LockMode.DEFAULT);
256: }
257: // should be the end of this database
258: assertTrue("More data than expected",
259: (status != OperationStatus.SUCCESS));
260: verifyCursor.close();
261: }
262: }
263:
264: /**
265: * Test basic commits, aborts with cursors
266: */
267: public void testBasicCursor() throws Throwable {
268:
269: try {
270: int numKeys = 7;
271: createDbs();
272:
273: // Insert more data with a user transaction, commit
274: Transaction txn = env.beginTransaction(null, null);
275: createCursors(txn);
276: cursorInsertData(0, numKeys * 2);
277: closeCursors();
278: txn.commit();
279: verifyData(numKeys * 2, 0);
280:
281: // Insert more data, abort, check that data is unchanged
282: txn = env.beginTransaction(null, null);
283: createCursors(txn);
284: cursorInsertData(numKeys * 2, numKeys * 3);
285: closeCursors();
286: txn.abort();
287: verifyData(numKeys * 2, 0);
288:
289: /*
290: * Check the in compressor queue, we should have some number of
291: * bins on. If the queue size is 0, then check the processed stats,
292: * the in compressor thread may have already woken up and dealt
293: * with the entries.
294: */
295: EnvironmentStats envStat = env
296: .getStats(TestUtils.FAST_STATS);
297: int queueSize = envStat.getInCompQueueSize();
298: assertTrue(queueSize > 0);
299:
300: // Modify data, abort, check that data is unchanged
301: txn = env.beginTransaction(null, null);
302: createCursors(txn);
303: cursorModifyData(0, numKeys * 2, 1);
304: closeCursors();
305: txn.abort();
306: verifyData(numKeys * 2, 0);
307:
308: // Delete data, abort, check that data is still there
309: txn = env.beginTransaction(null, null);
310: createCursors(txn);
311: cursorDeleteData(numKeys + 1, numKeys * 2);
312: closeCursors();
313: txn.abort();
314: verifyData(numKeys * 2, 0);
315: // Check the in compressor queue, nothing should be loaded
316: envStat = env.getStats(TestUtils.FAST_STATS);
317: assertEquals(queueSize, envStat.getInCompQueueSize());
318:
319: // Delete data, commit, check that data is gone
320: txn = env.beginTransaction(null, null);
321: createCursors(txn);
322: cursorDeleteData(numKeys, numKeys * 2);
323: closeCursors();
324: txn.commit();
325: verifyData(numKeys, 0);
326:
327: // Check the inCompressor queue, there should be more entries.
328: envStat = env.getStats(TestUtils.FAST_STATS);
329: assertTrue(envStat.getInCompQueueSize() > queueSize);
330:
331: closeAll();
332:
333: } catch (Throwable t) {
334: // print stacktrace before attempt to run tearDown
335: t.printStackTrace();
336: throw t;
337: }
338: }
339:
340: /**
341: * Test db creation and deletion.
342: */
343: public void testTxnClose() throws DatabaseException {
344:
345: createDbs();
346: Transaction txn = env.beginTransaction(null, null);
347: createCursors(txn);
348: try {
349: txn.commit();
350: fail("Commit should fail");
351: } catch (DatabaseException e) {
352: }
353: closeCursors();
354: closeAll();
355: }
356:
357: class CascadingAbortTestJUnitThread extends JUnitThread {
358: Transaction txn = null;
359: Database db = null;
360:
361: CascadingAbortTestJUnitThread(Transaction txn, Database db) {
362: super ("testCascadingAborts");
363: this .txn = txn;
364: this .db = db;
365: }
366: }
367:
368: /**
369: * Test cascading aborts in the face of deletes.
370: * [work in progress: cwl 1/15/04]
371: */
372: public void xtestCascadingAborts() throws Throwable {
373:
374: Database db = null;
375:
376: try {
377: DatabaseConfig dbConfig = new DatabaseConfig();
378: dbConfig.setAllowCreate(true);
379: dbConfig.setTransactional(true);
380: db = env.openDatabase(null, "testDB", dbConfig);
381:
382: DatabaseEntry key = new DatabaseEntry();
383: DatabaseEntry data = new DatabaseEntry();
384:
385: Transaction txn = env.beginTransaction(null, null);
386: key.setData("abb".getBytes());
387: data.setData("def".getBytes());
388: //db.put(txn, key, data, null);
389: key.setData("abc".getBytes());
390: data.setData("def".getBytes());
391: db.put(txn, key, data);
392: txn.commit();
393:
394: //DbInternal.dbGetDatabaseImpl(db).getTree().dump();
395:
396: Transaction txn1 = env.beginTransaction(null, null);
397: Transaction txn2 = env.beginTransaction(null, null);
398:
399: CascadingAbortTestJUnitThread tester1 = new CascadingAbortTestJUnitThread(
400: txn2, db) {
401: public void testBody() throws Throwable {
402:
403: Cursor c = db.openCursor(txn, null);
404: DatabaseEntry data = new DatabaseEntry();
405: try {
406: Thread.yield();
407: DatabaseEntry key = new DatabaseEntry();
408: key.setData("abc".getBytes());
409: OperationStatus status;
410: status = c.getSearchKeyRange(key, data,
411: LockMode.DEFAULT);
412: status = c.delete();
413: } catch (Throwable T) {
414: T.printStackTrace();
415: } finally {
416: c.close();
417: }
418: }
419: };
420:
421: tester1.start();
422: Thread.yield();
423: key.setData("abc".getBytes());
424: OperationStatus status;
425: status = db.delete(txn1, key);
426:
427: txn1.abort();
428: Thread.yield();
429:
430: txn2.abort();
431: tester1.finishTest();
432:
433: //DbInternal.dbGetDatabaseImpl(db).getTree().dump();
434:
435: if (false) {
436: db.close();
437: env.close();
438: EnvironmentConfig envConfig = TestUtils.initEnvConfig();
439: envConfig.setTransactional(true);
440: envConfig.setConfigParam(EnvironmentParams.NODE_MAX
441: .getName(), "6");
442: envConfig.setConfigParam(
443: EnvironmentParams.ENV_RUN_INCOMPRESSOR
444: .getName(), "false");
445: envConfig.setAllowCreate(true);
446: env = new Environment(envHome, envConfig);
447: db = env.openDatabase(null, "testDB", dbConfig);
448: }
449:
450: txn = env.beginTransaction(null, null);
451: System.out.println(db.getSearchBoth(txn, key, data,
452: LockMode.DEFAULT));
453: txn.commit();
454: } catch (Throwable T) {
455: T.printStackTrace();
456: } finally {
457: db.close();
458: }
459: }
460:
461: /**
462: * Test use through db.
463: */
464: public void testBasicDb() throws Throwable {
465:
466: try {
467: TransactionStats stats = env
468: .getTransactionStats(TestUtils.FAST_STATS);
469: assertEquals(0, stats.getNAborts());
470: int initialCommits = 1; // 1 commits for adding UP database
471: assertEquals(initialCommits, stats.getNCommits());
472:
473: int numKeys = 7;
474: createDbs();
475:
476: // Insert data with autocommit
477: dbInsertData(0, numKeys, null);
478: verifyData(numKeys, 0);
479:
480: // Insert data with a txn
481: Transaction txn = env.beginTransaction(null, null);
482: dbInsertData(numKeys, numKeys * 2, txn);
483: txn.commit();
484: verifyData(numKeys * 2, 0);
485:
486: stats = env.getTransactionStats(TestUtils.FAST_STATS);
487: assertEquals(0, stats.getNAborts());
488: assertEquals((initialCommits + 1 + // 1 explicit commit above
489: (1 * NUM_DBS) + // 1 per create/open
490: (numKeys * NUM_DBS)), // 1 per record, using autotxn
491: stats.getNCommits());
492:
493: // delete data with a txn, abort
494: txn = env.beginTransaction(null, null);
495: dbDeleteData(numKeys, numKeys * 2, txn);
496: verifyData(numKeys, 0); // verify w/dirty read
497: txn.abort();
498:
499: closeAll();
500: } catch (Throwable t) {
501: t.printStackTrace();
502: throw t;
503: }
504: }
505:
506: /**
507: * Test db creation and deletion
508: */
509:
510: public void testDbCreation() throws DatabaseException {
511:
512: Transaction txnA = env.beginTransaction(null, null);
513: Transaction txnB = env.beginTransaction(null, null);
514:
515: DatabaseConfig dbConfig = new DatabaseConfig();
516: dbConfig.setAllowCreate(true);
517: dbConfig.setTransactional(true);
518: Database dbA = env.openDatabase(txnA, "foo", dbConfig);
519:
520: // Try to see this database with another txn -- we should not see it
521:
522: dbConfig.setAllowCreate(false);
523:
524: try {
525: txnB.setLockTimeout(1000);
526:
527: env.openDatabase(txnB, "foo", dbConfig);
528: fail("Shouldn't be able to open foo");
529: } catch (DatabaseException e) {
530: }
531: /* txnB must be aborted since openDatabase timed out. */
532: txnB.abort();
533:
534: // Open this database with the same txn and another handle
535: Database dbC = env.openDatabase(txnA, "foo", dbConfig);
536:
537: // Now commit txnA and txnB should be able to open this.
538: txnA.commit();
539: txnB = env.beginTransaction(null, null);
540: Database dbB = env.openDatabase(txnB, "foo", dbConfig);
541: txnB.commit();
542:
543: // XXX, test db deletion
544:
545: dbA.close();
546: dbB.close();
547: dbC.close();
548: }
549:
550: /* Test that the transaction is unsable about a close. */
551: public void testClose() throws DatabaseException {
552:
553: Transaction txnA = env.beginTransaction(null, null);
554: txnA.commit();
555:
556: try {
557: env.openDatabase(txnA, "foo", null);
558: fail("Should not be able to use a closed exception");
559: } catch (DatabaseException expected) {
560: }
561: }
562:
563: }
|