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