001: /*-
002: * See the file LICENSE for redistribution information.
003: *
004: * Copyright (c) 2002,2008 Oracle. All rights reserved.
005: *
006: * $Id: IOExceptionTest.java,v 1.10.2.6 2008/01/07 15:14:29 cwl Exp $
007: */
008:
009: package com.sleepycat.je.log;
010:
011: import java.io.File;
012: import java.io.IOException;
013: import java.nio.ByteBuffer;
014: import java.util.logging.Level;
015:
016: import junit.framework.TestCase;
017:
018: import com.sleepycat.bind.tuple.IntegerBinding;
019: import com.sleepycat.je.CheckpointConfig;
020: import com.sleepycat.je.Database;
021: import com.sleepycat.je.DatabaseConfig;
022: import com.sleepycat.je.DatabaseEntry;
023: import com.sleepycat.je.DatabaseException;
024: import com.sleepycat.je.DbInternal;
025: import com.sleepycat.je.Environment;
026: import com.sleepycat.je.EnvironmentConfig;
027: import com.sleepycat.je.EnvironmentStats;
028: import com.sleepycat.je.LockMode;
029: import com.sleepycat.je.OperationStatus;
030: import com.sleepycat.je.RunRecoveryException;
031: import com.sleepycat.je.Transaction;
032: import com.sleepycat.je.config.EnvironmentParams;
033: import com.sleepycat.je.dbi.EnvironmentImpl;
034: import com.sleepycat.je.dbi.MemoryBudget;
035: import com.sleepycat.je.util.TestUtils;
036: import com.sleepycat.je.utilint.DbLsn;
037: import com.sleepycat.je.utilint.Tracer;
038:
039: public class IOExceptionTest extends TestCase {
040:
041: private Environment env;
042: private Database db;
043: private File envHome;
044:
045: public IOExceptionTest() throws Exception {
046:
047: envHome = new File(System.getProperty(TestUtils.DEST_DIR));
048: }
049:
050: public void setUp() throws IOException, DatabaseException {
051:
052: TestUtils.removeFiles("Setup", envHome, FileManager.JE_SUFFIX);
053: }
054:
055: public void tearDown() throws IOException, DatabaseException {
056:
057: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = false;
058: if (db != null) {
059: db.close();
060: }
061:
062: if (env != null) {
063: env.close();
064: }
065:
066: TestUtils.removeFiles("TearDown", envHome,
067: FileManager.JE_SUFFIX);
068: }
069:
070: public void testIOExceptionOnRead() throws Exception {
071:
072: try {
073: createDatabase(MemoryBudget.MIN_MAX_MEMORY_SIZE, 0);
074:
075: final int N_RECS = 25;
076:
077: CheckpointConfig chkConf = new CheckpointConfig();
078: chkConf.setForce(true);
079: Transaction txn = env.beginTransaction(null, null);
080: int keyInt = 0;
081: for (int i = 0; i < N_RECS; i++) {
082: String keyStr = Integer.toString(keyInt);
083: DatabaseEntry key = new DatabaseEntry(keyStr.getBytes());
084: DatabaseEntry data = new DatabaseEntry(("d" + keyStr)
085: .getBytes());
086: try {
087: assertTrue(db.put(txn, key, data) == OperationStatus.SUCCESS);
088: } catch (DatabaseException DE) {
089: fail("unexpected DatabaseException");
090: break;
091: }
092: }
093:
094: try {
095: env.checkpoint(chkConf);
096: } catch (DatabaseException DE) {
097: fail("unexpected DatabaseException");
098: }
099:
100: txn.commit();
101:
102: db.close();
103: forceCloseEnvOnly();
104: createDatabase(0, 0);
105:
106: EnvironmentStats stats = env.getStats(null);
107: assertTrue((stats.getNFullINFlush() + stats
108: .getNFullBINFlush()) > 0);
109:
110: txn = env.beginTransaction(null, null);
111: FileManager.IO_EXCEPTION_TESTING_ON_READ = true;
112: for (int i = 0; i < N_RECS; i++) {
113: String keyStr = Integer.toString(keyInt);
114: DatabaseEntry key = new DatabaseEntry(keyStr.getBytes());
115: DatabaseEntry data = new DatabaseEntry();
116: try {
117: assertTrue(db.get(txn, key, data, null) == OperationStatus.SUCCESS);
118: assertEquals(new String(data.getData()), "d"
119: + keyStr);
120: } catch (DatabaseException DE) {
121: // ignore
122: }
123: }
124:
125: FileManager.IO_EXCEPTION_TESTING_ON_READ = false;
126:
127: for (int i = 0; i < N_RECS; i++) {
128: String keyStr = Integer.toString(keyInt);
129: DatabaseEntry key = new DatabaseEntry(keyStr.getBytes());
130: DatabaseEntry data = new DatabaseEntry();
131: try {
132: assertTrue(db.get(txn, key, data, null) == OperationStatus.SUCCESS);
133: assertEquals(new String(data.getData()), "d"
134: + keyStr);
135: } catch (DatabaseException DE) {
136: fail("unexpected DatabaseException on read");
137: }
138: }
139:
140: /*
141: * Now we have some IN's in the log buffer and there have been
142: * IOExceptions that will later force rewriting that buffer.
143: */
144: try {
145: txn.commit();
146: } catch (DatabaseException DE) {
147: fail("unexpected DatabaseException");
148: }
149: } catch (Throwable E) {
150: E.printStackTrace();
151: }
152: }
153:
154: public void testRunRecoveryExceptionOnWrite() throws Exception {
155:
156: try {
157: createDatabase(200000, 0);
158:
159: final int N_RECS = 25;
160:
161: CheckpointConfig chkConf = new CheckpointConfig();
162: chkConf.setForce(true);
163: Transaction txn = env.beginTransaction(null, null);
164: int keyInt = 0;
165: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = false;
166: for (int i = 0; i < N_RECS; i++) {
167: String keyStr = Integer.toString(keyInt);
168: DatabaseEntry key = new DatabaseEntry(keyStr.getBytes());
169: DatabaseEntry data = new DatabaseEntry(("d" + keyStr)
170: .getBytes());
171: if (i == (N_RECS - 1)) {
172: FileManager.THROW_RRE_FOR_UNIT_TESTS = true;
173: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = true;
174: }
175: try {
176: assertTrue(db.put(txn, key, data) == OperationStatus.SUCCESS);
177: } catch (DatabaseException DE) {
178: fail("unexpected DatabaseException");
179: break;
180: }
181: }
182:
183: try {
184: txn.commit();
185: fail("expected DatabaseException");
186: } catch (RunRecoveryException DE) {
187: }
188: forceCloseEnvOnly();
189:
190: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = false;
191: FileManager.THROW_RRE_FOR_UNIT_TESTS = false;
192: db = null;
193: env = null;
194: } catch (Throwable E) {
195: E.printStackTrace();
196: }
197: }
198:
199: public void testIOExceptionNoRecovery() throws Throwable {
200:
201: doIOExceptionTest(false);
202: }
203:
204: public void testIOExceptionWithRecovery() throws Throwable {
205:
206: doIOExceptionTest(true);
207: }
208:
209: public void testEviction() throws Exception {
210:
211: try {
212: createDatabase(200000, 0);
213:
214: final int N_RECS = 25;
215:
216: CheckpointConfig chkConf = new CheckpointConfig();
217: chkConf.setForce(true);
218: Transaction txn = env.beginTransaction(null, null);
219: int keyInt = 0;
220: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = true;
221: for (int i = 0; i < N_RECS; i++) {
222: String keyStr = Integer.toString(keyInt);
223: DatabaseEntry key = new DatabaseEntry(keyStr.getBytes());
224: DatabaseEntry data = new DatabaseEntry(("d" + keyStr)
225: .getBytes());
226: try {
227: assertTrue(db.put(txn, key, data) == OperationStatus.SUCCESS);
228: } catch (DatabaseException DE) {
229: fail("unexpected DatabaseException");
230: break;
231: }
232: }
233:
234: try {
235: env.checkpoint(chkConf);
236: fail("expected DatabaseException");
237: } catch (DatabaseException DE) {
238: }
239:
240: EnvironmentStats stats = env.getStats(null);
241: assertTrue((stats.getNFullINFlush() + stats
242: .getNFullBINFlush()) > 0);
243:
244: /* Read back the data and make sure it all looks ok. */
245: for (int i = 0; i < N_RECS; i++) {
246: String keyStr = Integer.toString(keyInt);
247: DatabaseEntry key = new DatabaseEntry(keyStr.getBytes());
248: DatabaseEntry data = new DatabaseEntry();
249: try {
250: assertTrue(db.get(txn, key, data, null) == OperationStatus.SUCCESS);
251: assertEquals(new String(data.getData()), "d"
252: + keyStr);
253: } catch (DatabaseException DE) {
254: fail("unexpected DatabaseException");
255: break;
256: }
257: }
258:
259: /*
260: * Now we have some IN's in the log buffer and there have been
261: * IOExceptions that will later force rewriting that buffer.
262: */
263: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = false;
264: try {
265: txn.commit();
266: } catch (DatabaseException DE) {
267: fail("unexpected DatabaseException");
268: }
269: } catch (Exception E) {
270: E.printStackTrace();
271: }
272: }
273:
274: /*
275: * Test for SR 13898. Write out some records with
276: * IO_EXCEPTION_TESTING_ON_WRITE true thereby forcing some commits to be
277: * rewritten as aborts. Ensure that the checksums are correct on those
278: * rewritten records by reading them back with a file reader.
279: */
280: public void testIOExceptionReadBack() throws Exception {
281:
282: try {
283: createDatabase(100000, 1000);
284:
285: final int N_RECS = 25;
286:
287: CheckpointConfig chkConf = new CheckpointConfig();
288: chkConf.setForce(true);
289: Transaction txn = env.beginTransaction(null, null);
290: int keyInt = 0;
291: for (int i = 0; i < N_RECS; i++) {
292: String keyStr = Integer.toString(i);
293: DatabaseEntry key = new DatabaseEntry(keyStr.getBytes());
294: DatabaseEntry data = new DatabaseEntry(new byte[100]);
295: try {
296: assertTrue(db.put(txn, key, data) == OperationStatus.SUCCESS);
297: } catch (DatabaseException DE) {
298: fail("unexpected DatabaseException");
299: break;
300: }
301: try {
302: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = true;
303: txn.commit();
304: fail("expected DatabaseException");
305: } catch (DatabaseException DE) {
306: }
307: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = false;
308: txn = env.beginTransaction(null, null);
309: }
310:
311: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = false;
312: try {
313: env.checkpoint(chkConf);
314: } catch (DatabaseException DE) {
315: DE.printStackTrace();
316: fail("unexpected DatabaseException");
317: }
318:
319: EnvironmentStats stats = env.getStats(null);
320: assertTrue((stats.getNFullINFlush() + stats
321: .getNFullBINFlush()) > 0);
322: long lastCheckpointLsn = stats.getLastCheckpointStart();
323:
324: try {
325: txn.commit();
326: } catch (DatabaseException DE) {
327: fail("unexpected DatabaseException");
328: }
329:
330: FileReader reader = new FileReader(DbInternal
331: .envGetEnvironmentImpl(env), 4096, true, 0, null,
332: DbLsn.NULL_LSN, DbLsn.NULL_LSN) {
333: protected boolean processEntry(ByteBuffer entryBuffer)
334: throws DatabaseException {
335:
336: entryBuffer.position(entryBuffer.position()
337: + currentEntryHeader.getItemSize());
338: return true;
339: }
340: };
341:
342: while (reader.readNextEntry()) {
343: }
344: } catch (Throwable E) {
345: E.printStackTrace();
346: }
347: }
348:
349: public void testLogBufferOverflowAbortNoDupes() throws Exception {
350:
351: doLogBufferOverflowTest(false, false);
352: }
353:
354: public void testLogBufferOverflowCommitNoDupes() throws Exception {
355:
356: doLogBufferOverflowTest(true, false);
357: }
358:
359: public void testLogBufferOverflowAbortDupes() throws Exception {
360:
361: doLogBufferOverflowTest(false, true);
362: }
363:
364: public void testLogBufferOverflowCommitDupes() throws Exception {
365:
366: doLogBufferOverflowTest(true, true);
367: }
368:
369: private void doLogBufferOverflowTest(boolean abort, boolean dupes)
370: throws Exception {
371:
372: try {
373: EnvironmentConfig envConfig = TestUtils.initEnvConfig();
374: envConfig.setTransactional(true);
375: envConfig.setAllowCreate(true);
376: envConfig.setCacheSize(100000);
377: envConfig.setConfigParam("java.util.logging.level", "OFF");
378: env = new Environment(envHome, envConfig);
379:
380: String databaseName = "ioexceptiondb";
381: DatabaseConfig dbConfig = new DatabaseConfig();
382: dbConfig.setAllowCreate(true);
383: dbConfig.setSortedDuplicates(true);
384: dbConfig.setTransactional(true);
385: db = env.openDatabase(null, databaseName, dbConfig);
386:
387: Transaction txn = env.beginTransaction(null, null);
388: DatabaseEntry oneKey = (dupes ? new DatabaseEntry("2"
389: .getBytes()) : new DatabaseEntry("1".getBytes()));
390: DatabaseEntry oneData = new DatabaseEntry(new byte[10]);
391: DatabaseEntry twoKey = new DatabaseEntry("2".getBytes());
392: DatabaseEntry twoData = new DatabaseEntry(new byte[100000]);
393: if (dupes) {
394: DatabaseEntry temp = oneKey;
395: oneKey = oneData;
396: oneData = temp;
397: temp = twoKey;
398: twoKey = twoData;
399: twoData = temp;
400: }
401:
402: try {
403: assertTrue(db.put(txn, oneKey, oneData) == OperationStatus.SUCCESS);
404: db.put(txn, twoKey, twoData);
405: } catch (DatabaseException DE) {
406: fail("unexpected DatabaseException");
407: }
408:
409: /* Read back the data and make sure it all looks ok. */
410: try {
411: assertTrue(db.get(txn, oneKey, oneData, null) == OperationStatus.SUCCESS);
412: assertTrue(oneData.getData().length == (dupes ? 1 : 10));
413: } catch (DatabaseException DE) {
414: fail("unexpected DatabaseException");
415: }
416:
417: try {
418: assertTrue(db.get(txn, twoKey, twoData, null) == OperationStatus.SUCCESS);
419: } catch (DatabaseException DE) {
420: fail("unexpected DatabaseException");
421: }
422:
423: try {
424: if (abort) {
425: txn.abort();
426: } else {
427: txn.commit();
428: }
429: } catch (DatabaseException DE) {
430: fail("unexpected DatabaseException");
431: }
432:
433: /* Read back the data and make sure it all looks ok. */
434: try {
435: assertTrue(db.get(null, oneKey, oneData, null) == (abort ? OperationStatus.NOTFOUND
436: : OperationStatus.SUCCESS));
437: assertTrue(oneData.getData().length == (dupes ? 1 : 10));
438: } catch (DatabaseException DE) {
439: fail("unexpected DatabaseException");
440: }
441:
442: try {
443: assertTrue(db.get(null, twoKey, twoData, null) == (abort ? OperationStatus.NOTFOUND
444: : OperationStatus.SUCCESS));
445: } catch (DatabaseException DE) {
446: fail("unexpected DatabaseException");
447: }
448:
449: } catch (Exception E) {
450: E.printStackTrace();
451: }
452: }
453:
454: public void testPutTransactionalWithIOException() throws Throwable {
455:
456: try {
457: createDatabase(100000, 0);
458:
459: Transaction txn = env.beginTransaction(null, null);
460: int keyInt = 0;
461: String keyStr;
462: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = true;
463:
464: /* Fill up the buffer until we see an IOException. */
465: while (true) {
466: keyStr = Integer.toString(++keyInt);
467: DatabaseEntry key = new DatabaseEntry(keyStr.getBytes());
468: DatabaseEntry data = new DatabaseEntry(("d" + keyStr)
469: .getBytes());
470: try {
471: assertTrue(db.put(txn, key, data) == OperationStatus.SUCCESS);
472: } catch (DatabaseException DE) {
473: break;
474: }
475: }
476:
477: /* Buffer still hasn't been written. This should also fail. */
478: try {
479: db.put(txn, new DatabaseEntry("shouldFail".getBytes()),
480: new DatabaseEntry("shouldFailD".getBytes()));
481: fail("expected DatabaseException");
482: } catch (DatabaseException DE) {
483: // expected
484: }
485: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = false;
486:
487: /* Buffer should write out ok now. */
488: try {
489: db.put(txn, new DatabaseEntry("shouldNotFail"
490: .getBytes()), new DatabaseEntry(
491: "shouldNotFailD".getBytes()));
492: } catch (DatabaseException DE) {
493: fail("unexpected DatabaseException");
494: }
495: txn.commit();
496:
497: DatabaseEntry data = new DatabaseEntry();
498: assertTrue(db.get(null, new DatabaseEntry("shouldNotFail"
499: .getBytes()), data, null) == OperationStatus.SUCCESS);
500: assertTrue(new String(data.getData())
501: .equals("shouldNotFailD"));
502:
503: assertTrue(db.get(null, new DatabaseEntry("shouldFail"
504: .getBytes()), data, null) == OperationStatus.NOTFOUND);
505:
506: assertTrue(db.get(null, new DatabaseEntry("shouldFail"
507: .getBytes()), data, null) == OperationStatus.NOTFOUND);
508:
509: assertTrue(db.get(null,
510: new DatabaseEntry(keyStr.getBytes()), data, null) == OperationStatus.NOTFOUND);
511:
512: for (int i = --keyInt; i > 0; i--) {
513: keyStr = Integer.toString(i);
514: assertTrue(db.get(null, new DatabaseEntry(keyStr
515: .getBytes()), data, null) == OperationStatus.SUCCESS);
516: assertTrue(new String(data.getData()).equals("d"
517: + keyStr));
518: }
519:
520: } catch (Throwable T) {
521: T.printStackTrace();
522: }
523: }
524:
525: public void testIOExceptionDuringFileFlippingWrite()
526: throws Throwable {
527:
528: doIOExceptionDuringFileFlippingWrite(8, 33, 2);
529: }
530:
531: private void doIOExceptionDuringFileFlippingWrite(
532: int numIterations, int exceptionStartWrite,
533: int exceptionWriteCount) throws Throwable {
534:
535: try {
536: EnvironmentConfig envConfig = new EnvironmentConfig();
537: DbInternal.disableParameterValidation(envConfig);
538: envConfig.setTransactional(true);
539: envConfig.setAllowCreate(true);
540: envConfig.setConfigParam("je.log.fileMax", "1000");
541: envConfig.setConfigParam("je.log.bufferSize", "1025");
542: envConfig.setConfigParam("je.env.runCheckpointer", "false");
543: envConfig.setConfigParam("je.env.runCleaner", "false");
544: env = new Environment(envHome, envConfig);
545:
546: EnvironmentImpl envImpl = DbInternal
547: .envGetEnvironmentImpl(env);
548: DatabaseConfig dbConfig = new DatabaseConfig();
549: dbConfig.setTransactional(true);
550: dbConfig.setAllowCreate(true);
551: db = env.openDatabase(null, "foo", dbConfig);
552:
553: String stuff = new String(
554: "..hello world ......................"
555: + "...................................");
556:
557: /*
558: * Put one record into the database so it gets populated w/INs and
559: * LNs, and we can fake out the RMW commits used below.
560: */
561: DatabaseEntry key = new DatabaseEntry();
562: DatabaseEntry data = new DatabaseEntry();
563: IntegerBinding.intToEntry(5, key);
564: IntegerBinding.intToEntry(5, data);
565: db.put(null, key, data);
566:
567: /*
568: * Now generate trace and commit log entries. The trace records
569: * aren't forced out, but the commit records are forced.
570: */
571: FileManager.WRITE_COUNT = 0;
572: FileManager.THROW_ON_WRITE = true;
573: FileManager.STOP_ON_WRITE_COUNT = exceptionStartWrite;
574: FileManager.N_BAD_WRITES = exceptionWriteCount;
575: for (int i = 0; i < numIterations; i++) {
576:
577: try {
578: /* Generate a non-forced record. */
579: if (i == (numIterations - 1)) {
580:
581: /*
582: * On the last iteration, write a record that is large
583: * enough to force a file flip (i.e. an fsync which
584: * succeeds) followed by the large write (which doesn't
585: * succeed due to an IOException). In [#15754] the
586: * large write fails on Out Of Disk Space, rolling back
587: * the savedLSN to the previous file, even though the
588: * file has flipped. The subsequent write ends up in
589: * the flipped file, but at the offset of the older
590: * file (leaving a hole in the new flipped file).
591: */
592: Tracer.trace(Level.SEVERE, envImpl, i + "/"
593: + FileManager.WRITE_COUNT + " "
594: + new String(new byte[2000]));
595: } else {
596: Tracer.trace(Level.SEVERE, envImpl, i + "/"
597: + FileManager.WRITE_COUNT + " " + "xx");
598: }
599: } catch (IllegalStateException ISE) {
600: /* Eat exception thrown by TraceLogHandler. */
601: }
602:
603: /* Generated a forced record by calling commit. */
604: Transaction txn = env.beginTransaction(null, null);
605: db.get(txn, key, data, LockMode.RMW);
606: txn.commit();
607: }
608: db.close();
609:
610: /*
611: * Verify that the log files are ok and have no checksum errors.
612: */
613: FileReader reader = new FileReader(DbInternal
614: .envGetEnvironmentImpl(env), 4096, true, 0, null,
615: DbLsn.NULL_LSN, DbLsn.NULL_LSN) {
616: protected boolean processEntry(ByteBuffer entryBuffer)
617: throws DatabaseException {
618:
619: entryBuffer.position(entryBuffer.position()
620: + currentEntryHeader.getItemSize());
621: return true;
622: }
623: };
624:
625: DbInternal.envGetEnvironmentImpl(env).getLogManager()
626: .flush();
627:
628: while (reader.readNextEntry()) {
629: }
630:
631: /* Make sure the reader really did scan the files. */
632: assert (DbLsn.getFileNumber(reader.getLastLsn()) == 4) : DbLsn
633: .toString(reader.getLastLsn());
634:
635: env.close();
636: env = null;
637: db = null;
638: } catch (Throwable T) {
639: T.printStackTrace();
640: }
641: }
642:
643: private void doIOExceptionTest(boolean doRecovery) throws Throwable {
644:
645: Transaction txn = null;
646: createDatabase(0, 0);
647: writeAndVerify(null, false, "k1", "d1", doRecovery);
648: writeAndVerify(null, true, "k2", "d2", doRecovery);
649: writeAndVerify(null, false, "k3", "d3", doRecovery);
650:
651: txn = env.beginTransaction(null, null);
652: writeAndVerify(txn, false, "k4", "d4", false);
653: txn.abort();
654: verify(null, true, "k4", doRecovery);
655: verify(null, false, "k1", doRecovery);
656: verify(null, false, "k3", doRecovery);
657:
658: txn = env.beginTransaction(null, null);
659: writeAndVerify(txn, false, "k4", "d4", false);
660: txn.commit();
661: verify(null, false, "k4", doRecovery);
662:
663: txn = env.beginTransaction(null, null);
664: writeAndVerify(txn, true, "k5", "d5", false);
665: /* Ensure that writes after IOExceptions don't succeed. */
666: writeAndVerify(txn, false, "k5a", "d5a", false);
667: txn.abort();
668: verify(null, true, "k5", doRecovery);
669: verify(null, true, "k5a", doRecovery);
670:
671: txn = env.beginTransaction(null, null);
672: writeAndVerify(txn, false, "k6", "d6", false);
673: writeAndVerify(txn, true, "k6a", "d6a", false);
674:
675: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = true;
676: try {
677: txn.commit();
678: fail("expected DatabaseException");
679: } catch (DatabaseException DE) {
680: }
681: verify(null, true, "k6", doRecovery);
682: verify(null, true, "k6a", doRecovery);
683:
684: txn = env.beginTransaction(null, null);
685: writeAndVerify(txn, false, "k6", "d6", false);
686: writeAndVerify(txn, true, "k6a", "d6a", false);
687: writeAndVerify(txn, false, "k6b", "d6b", false);
688:
689: try {
690: txn.commit();
691: } catch (DatabaseException DE) {
692: fail("expected success");
693: }
694:
695: /*
696: * k6a will still exist because the writeAndVerify didn't fail -- there
697: * was no write. The write happens at commit time.
698: */
699: verify(null, false, "k6", doRecovery);
700: verify(null, false, "k6a", doRecovery);
701: verify(null, false, "k6b", doRecovery);
702: }
703:
704: private void writeAndVerify(Transaction txn,
705: boolean throwIOException, String keyString,
706: String dataString, boolean doRecovery)
707: throws DatabaseException {
708:
709: //System.out.println("Key: " + keyString + " Data: " + dataString);
710: DatabaseEntry key = new DatabaseEntry(keyString.getBytes());
711: DatabaseEntry data = new DatabaseEntry(dataString.getBytes());
712: FileManager.IO_EXCEPTION_TESTING_ON_WRITE = throwIOException;
713: try {
714: assertTrue(db.put(txn, key, data) == OperationStatus.SUCCESS);
715:
716: /*
717: * We don't expect an IOException if we're in a transaction because
718: * the put() only writes to the buffer, not the disk. The write to
719: * disk doesn't happen until the commit/abort.
720: */
721: if (throwIOException && txn == null) {
722: fail("didn't catch DatabaseException.");
723: }
724: } catch (DatabaseException DE) {
725: if (!throwIOException) {
726: fail("caught DatabaseException.");
727: }
728: }
729: verify(txn, throwIOException, keyString, doRecovery);
730: }
731:
732: private void verify(Transaction txn, boolean expectFailure,
733: String keyString, boolean doRecovery)
734: throws DatabaseException {
735:
736: if (doRecovery) {
737: db.close();
738: forceCloseEnvOnly();
739: createDatabase(0, 0);
740: }
741: DatabaseEntry key = new DatabaseEntry(keyString.getBytes());
742: DatabaseEntry returnedData = new DatabaseEntry();
743: OperationStatus status = db.get(txn, key, returnedData,
744: LockMode.DEFAULT);
745: //System.out.println(keyString + " => " + status);
746: assertTrue(status == ((expectFailure && txn == null) ? OperationStatus.NOTFOUND
747: : OperationStatus.SUCCESS));
748: }
749:
750: private void createDatabase(long cacheSize, long maxFileSize)
751: throws DatabaseException {
752:
753: EnvironmentConfig envConfig = TestUtils.initEnvConfig();
754: envConfig.setTransactional(true);
755: envConfig.setAllowCreate(true);
756: envConfig.setConfigParam(EnvironmentParams.NUM_LOG_BUFFERS
757: .getName(), "2");
758: envConfig.setConfigParam(EnvironmentParams.LOG_MEM_SIZE
759: .getName(), EnvironmentParams.LOG_MEM_SIZE_MIN_STRING);
760: if (maxFileSize != 0) {
761: DbInternal.disableParameterValidation(envConfig);
762: envConfig.setConfigParam(EnvironmentParams.LOG_FILE_MAX
763: .getName(), "" + maxFileSize);
764: }
765: if (cacheSize != 0) {
766: envConfig.setCacheSize(cacheSize);
767: envConfig.setConfigParam("java.util.logging.level", "OFF");
768: }
769: env = new Environment(envHome, envConfig);
770:
771: String databaseName = "ioexceptiondb";
772: DatabaseConfig dbConfig = new DatabaseConfig();
773: dbConfig.setAllowCreate(true);
774: dbConfig.setSortedDuplicates(true);
775: dbConfig.setTransactional(true);
776: db = env.openDatabase(null, databaseName, dbConfig);
777: }
778:
779: /* Force the environment to be closed even with outstanding handles.*/
780: private void forceCloseEnvOnly() throws DatabaseException {
781:
782: /* Close w/out checkpointing, in order to exercise recovery better.*/
783: try {
784: DbInternal.envGetEnvironmentImpl(env).close(false);
785: } catch (DatabaseException DE) {
786: if (!FileManager.IO_EXCEPTION_TESTING_ON_WRITE) {
787: throw DE;
788: } else {
789: /* Expect an exception from flushing the log manager. */
790: }
791: }
792: env = null;
793: }
794: }
|