1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002, 2014 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 8 package com.sleepycat.je; 9 10 import static org.junit.Assert.assertTrue; 11 import static org.junit.Assert.fail; 12 13 import java.io.File; 14 import java.io.RandomAccessFile; 15 import java.nio.ByteBuffer; 16 import java.nio.channels.FileChannel; 17 18 import org.junit.After; 19 import org.junit.Before; 20 import org.junit.Test; 21 22 import com.sleepycat.je.config.EnvironmentParams; 23 import com.sleepycat.je.log.FileManager; 24 import com.sleepycat.je.util.TestUtils; 25 import com.sleepycat.util.test.SharedTestUtils; 26 import com.sleepycat.util.test.TestBase; 27 28 /** 29 * @excludeDualMode 30 * This test does not run in Replication Dual Mode. There are several 31 * logistical issues. 32 * 33 * -It assumes that all log files are in the <envHome> directory, whereas 34 * dual mode environments are in <envHome>/rep* 35 * -It attempts to set the log file size to 1024, which is overridden by the 36 * dual mode framework. 37 * 38 * Since the test doesn't add any unique coverage to dual mode testing, it's 39 * not worth overcoming the logistical issues. 40 */ 41 public class RunRecoveryFailureTest extends TestBase { 42 43 private Environment env; 44 private final File envHome; 45 RunRecoveryFailureTest()46 public RunRecoveryFailureTest() { 47 envHome = SharedTestUtils.getTestDir(); 48 } 49 50 @Before setUp()51 public void setUp() 52 throws Exception { 53 54 super.setUp(); 55 openEnv(); 56 } 57 58 @After tearDown()59 public void tearDown() { 60 61 /* 62 * Close down environments in case the unit test failed so that the log 63 * files can be removed. 64 */ 65 try { 66 if (env != null) { 67 env.close(); 68 env = null; 69 } 70 } catch (RunRecoveryException e) { 71 /* ok, the test hosed it. */ 72 return; 73 } catch (DatabaseException e) { 74 /* ok, the test closed it */ 75 } 76 } 77 openEnv()78 private void openEnv() 79 throws DatabaseException { 80 81 EnvironmentConfig envConfig = TestUtils.initEnvConfig(); 82 envConfig.setTransactional(true); 83 84 /* 85 * Run with tiny log buffers, so we can go to disk more (and see the 86 * checksum errors) 87 */ 88 DbInternal.disableParameterValidation(envConfig); 89 envConfig.setConfigParam 90 (EnvironmentParams.NUM_LOG_BUFFERS.getName(), "2"); 91 envConfig.setConfigParam 92 (EnvironmentParams.LOG_MEM_SIZE.getName(), 93 EnvironmentParams.LOG_MEM_SIZE_MIN_STRING); 94 envConfig.setConfigParam 95 (EnvironmentParams.LOG_FILE_MAX.getName(), "1024"); 96 envConfig.setAllowCreate(true); 97 env = new Environment(envHome, envConfig); 98 } 99 100 /* 101 * Corrupt an environment while open, make sure we get a 102 * RunRecoveryException. 103 */ 104 @Test testInvalidateEnvMidStream()105 public void testInvalidateEnvMidStream() 106 throws Throwable { 107 108 try { 109 /* Make a new db in this env and flush the file. */ 110 Transaction txn = 111 env.beginTransaction(null, TransactionConfig.DEFAULT); 112 DatabaseConfig dbConfig = new DatabaseConfig(); 113 dbConfig.setTransactional(true); 114 dbConfig.setAllowCreate(true); 115 Database db = env.openDatabase(txn, "foo", dbConfig); 116 DatabaseEntry key = new DatabaseEntry(new byte[1000]); 117 DatabaseEntry data = new DatabaseEntry(new byte[1000]); 118 for (int i = 0; i < 100; i += 1) { 119 db.put(txn, key, data); 120 } 121 122 env.getEnvironmentImpl().getLogManager().flush(); 123 env.getEnvironmentImpl().getFileManager().clear(); 124 125 /* 126 * Corrupt each log file, then abort the txn. Aborting the txn 127 * results in an undo of each insert, which will provoke JE into 128 * reading the log a lot, and noticing the file corruption. Should 129 * get a checksum error, which should invalidate the environment. 130 */ 131 long currentFile = DbInternal.getEnvironmentImpl(env) 132 .getFileManager() 133 .getCurrentFileNum(); 134 for (int fileNum = 0; fileNum <= currentFile; fileNum += 1) { 135 String logFileName = 136 FileManager.getFileName(fileNum, FileManager.JE_SUFFIX); 137 File file = new File(envHome, logFileName); 138 RandomAccessFile starterFile = 139 new RandomAccessFile(file, "rw"); 140 FileChannel channel = starterFile.getChannel(); 141 long fileSize = channel.size(); 142 if (fileSize > FileManager.firstLogEntryOffset()) { 143 ByteBuffer junkBuffer = ByteBuffer.allocate 144 ((int) fileSize - FileManager.firstLogEntryOffset()); 145 int written = channel.write 146 (junkBuffer, FileManager.firstLogEntryOffset()); 147 assertTrue(written > 0); 148 starterFile.close(); 149 } 150 } 151 152 try { 153 txn.abort(); 154 fail("Should see a run recovery exception"); 155 } catch (RunRecoveryException e) { 156 } 157 158 try { 159 env.getDatabaseNames(); 160 fail("Should see a run recovery exception again"); 161 } catch (RunRecoveryException e) { 162 } 163 } catch (Throwable t) { 164 t.printStackTrace(); 165 throw t; 166 } 167 } 168 } 169