KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > recovery > CheckNewRootTest


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2004,2005 Oracle. All rights reserved.
5  *
6  * $Id: CheckNewRootTest.java,v 1.4 2006/10/30 21:14:48 bostic Exp $
7  */

8 package com.sleepycat.je.recovery;
9
10 import java.io.IOException JavaDoc;
11 import java.util.HashSet JavaDoc;
12 import java.util.logging.Level JavaDoc;
13
14 import com.sleepycat.bind.tuple.IntegerBinding;
15 import com.sleepycat.je.CheckpointConfig;
16 import com.sleepycat.je.Database;
17 import com.sleepycat.je.DatabaseConfig;
18 import com.sleepycat.je.DatabaseEntry;
19 import com.sleepycat.je.DatabaseException;
20 import com.sleepycat.je.DbInternal;
21 import com.sleepycat.je.Environment;
22 import com.sleepycat.je.EnvironmentConfig;
23 import com.sleepycat.je.OperationStatus;
24 import com.sleepycat.je.StatsConfig;
25 import com.sleepycat.je.config.EnvironmentParams;
26 import com.sleepycat.je.dbi.EnvironmentImpl;
27 import com.sleepycat.je.tree.Node;
28 import com.sleepycat.je.util.TestUtils;
29 import com.sleepycat.je.utilint.TestHook;
30 import com.sleepycat.je.utilint.Tracer;
31
32 /**
33  * Test situations where a new root is created
34  */

35 public class CheckNewRootTest extends CheckBase {
36
37     private static final boolean DEBUG = false;
38     private static final String JavaDoc DB_NAME = "simpleDB";
39
40     private boolean purgeRoot = false;
41     private boolean useDups = false;
42     private static CheckpointConfig FORCE_CONFIG = new CheckpointConfig();
43     static {
44         FORCE_CONFIG.setForce(true);
45     }
46     
47     /**
48      * Create a tree, make sure the root changes and is logged
49      * before any checkpointing. The bug found in [#13897] was this:
50      *
51      * 100 BIN a
52      * 110 RootIN b
53      * 120 MapLN points to root IN at 110
54      * 130 RootIN b written as part of compression
55      * 140 ckpt start
56      * 150 ckpt end
57      *
58      * Since the compression was writing a root IN w/out updating the mapLN,
59      * the obsolete root at 110 was recovered instead of newer rootIN at 130.
60      */

61     public void testWrittenByCompression()
62         throws Throwable JavaDoc {
63
64         EnvironmentConfig envConfig = setupEnvConfig();
65         DatabaseConfig dbConfig = setupDbConfig();
66
67         /* Run the full test case w/out truncating the log. */
68         testOneCase(DB_NAME, envConfig, dbConfig,
69                     new TestGenerator(true /* generate log description. */){
70                         void generateData(Database db)
71                             throws DatabaseException {
72                             setupWrittenByCompression(db);
73                         }
74                     },
75                     envConfig, dbConfig);
76
77         
78         /*
79          * Now run the test in a stepwise loop, truncate after each log entry.
80          * Our baseline expected set is empty -- no records expected.
81          */

82         HashSet JavaDoc currentExpected = new HashSet JavaDoc();
83         stepwiseLoop(DB_NAME, envConfig, dbConfig, currentExpected, 0);
84     }
85
86     /**
87      * Create a populated tree, delete all records, then begin to insert again.
88      */

89     private void setupWrittenByCompression(Database db)
90         throws DatabaseException {
91         setStepwiseStart();
92
93         DatabaseEntry key = new DatabaseEntry();
94         DatabaseEntry data = new DatabaseEntry();
95
96         /* Populate a tree so it grows to 2 levels, with 2 BINs. */
97         for (int i = 0; i < 10; i ++) {
98             IntegerBinding.intToEntry(i, key);
99             IntegerBinding.intToEntry(i, data);
100             assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
101         }
102
103         Tracer.trace(Level.SEVERE, DbInternal.envGetEnvironmentImpl(env),
104                      "After inserts");
105         env.checkpoint(FORCE_CONFIG);
106         if (DEBUG) {
107             System.out.println(db.getStats(new StatsConfig()));
108         }
109
110         /* Now delete all of 1 BIN. */
111         for (int i = 0; i < 5; i ++) {
112             IntegerBinding.intToEntry(i, key);
113             assertEquals(OperationStatus.SUCCESS, db.delete(null, key));
114         }
115
116         /* Compress, removing a BIN. */
117         env.compress();
118         if (DEBUG) {
119             System.out.println("After compress");
120             System.out.println(db.getStats(new StatsConfig()));
121         }
122
123         /* Checkpoint again. */
124         env.checkpoint(FORCE_CONFIG);
125     }
126
127     /**
128      * Create a tree, make sure the root changes and is logged
129      * before any checkpointing. The bug found in [#13897] was this:
130      *
131      * 110 RootIN b
132      * 120 MapLN points to root IN at 110
133      * 130 BINb split
134      * 140 RootIN b written as part of split
135      * 150 ckpt start
136      * 160 ckpt end
137      *
138      * Since the compression was writing a root IN w/out updating the mapLN,
139      * the obsolete root at 110 was recovered instead of newer rootIN at 130.
140      */

141     public void testWrittenBySplit()
142         throws Throwable JavaDoc {
143
144         EnvironmentConfig envConfig = setupEnvConfig();
145         DatabaseConfig dbConfig = setupDbConfig();
146
147         /* Run the full test case w/out truncating the log. */
148         testOneCase(DB_NAME, envConfig, dbConfig,
149                     new TestGenerator(true /* generate log description. */){
150                         void generateData(Database db)
151                             throws DatabaseException {
152                             setupWrittenBySplits(db);
153                         }
154                     },
155                     envConfig, dbConfig);
156
157         
158         /*
159          * Now run the test in a stepwise loop, truncate after each log entry.
160          * Our baseline expected set is empty -- no records expected.
161          */

162         HashSet JavaDoc currentExpected = new HashSet JavaDoc();
163         stepwiseLoop(DB_NAME, envConfig, dbConfig, currentExpected, 0);
164     }
165
166     /**
167      */

168     private void setupWrittenBySplits(Database db)
169         throws DatabaseException {
170         setStepwiseStart();
171
172         DatabaseEntry key = new DatabaseEntry();
173         DatabaseEntry data = new DatabaseEntry();
174
175         /* Create a tree and checkpoint. */
176         IntegerBinding.intToEntry(0, key);
177         IntegerBinding.intToEntry(0, data);
178         assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
179         env.checkpoint(FORCE_CONFIG);
180         Tracer.trace(Level.SEVERE, DbInternal.envGetEnvironmentImpl(env),
181                      "After creation");
182
183         /* Populate a tree so it splits. */
184         for (int i = 1; i < 6; i ++) {
185             IntegerBinding.intToEntry(i, key);
186             IntegerBinding.intToEntry(i, data);
187             assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
188         }
189
190         Tracer.trace(Level.SEVERE, DbInternal.envGetEnvironmentImpl(env),
191                      "After inserts");
192         env.checkpoint(FORCE_CONFIG);
193     }
194
195     /*
196      * Scenario from [#13897]: tree is created. Log looks like this
197      * provisional BIN
198      * root IN
199      * checkpoint start
200      * LN is logged but not yet attached to BIN
201      * checkpoint end
202      * BIN is dirtied, but is not part of checkpoint, because dirtying wasn't
203      * seen
204      * In this case, getParentForBIN hangs, because there is no root.
205      * This test is for debugging only, because it's not really possible to
206      * run a real checkpoint in the small window when the bin is not dirty.
207      * Attempts to run a checkpoint programmatically result in failing the
208      * assert that no latches are held when the inlist latch is taken.
209      * Instead, we do this pseudo checkpoint, to make the hang reproduce. But
210      * this test will still fail even with the fixed code because the fix
211      * now causes the rootIN to get re-logged, and the pseudo checkpoint
212      * doesn't do that logging.
213      */

214     public void xxtestCreateNewTree() // This test for debugging only
215
throws Throwable JavaDoc {
216
217         EnvironmentConfig envConfig = setupEnvConfig();
218         DatabaseConfig dbConfig = setupDbConfig();
219
220         /* Run the full test case w/out truncating the log. */
221         testOneCase(DB_NAME, envConfig, dbConfig,
222                     new TestGenerator(true /* generate log description. */){
223                         void generateData(Database db)
224                             throws DatabaseException {
225                             setupCreateNewTree(db);
226                         }
227                     },
228                     envConfig, dbConfig);
229
230         
231         /*
232          * Now run the test in a stepwise loop, truncate after each log entry.
233          * Our baseline expected set is empty -- no records expected.
234          */

235         HashSet JavaDoc currentExpected = new HashSet JavaDoc();
236         stepwiseLoop(DB_NAME, envConfig, dbConfig, currentExpected, 0);
237     }
238
239     /**
240      * Create a populated tree, delete all records, then begin to insert again.
241      */

242     private void setupCreateNewTree(Database db)
243         throws DatabaseException {
244
245
246
247         DatabaseEntry key = new DatabaseEntry();
248         DatabaseEntry data = new DatabaseEntry();
249
250         TestHook ckptHook = new CheckpointHook(env);
251         DbInternal.dbGetDatabaseImpl(db).getTree().setCkptHook(ckptHook);
252
253         env.checkpoint(FORCE_CONFIG);
254
255         /*
256          * Create in the log
257          * provisional BIN, IN, ckpt start, LN
258          */

259         IntegerBinding.intToEntry(1, key);
260         IntegerBinding.intToEntry(1, data);
261         assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
262     }
263
264     /*
265      * Force a checkpoint into the log. Use another thread, lest the asserts
266      * about held latches take effect.
267      */

268     private static class CheckpointHook implements TestHook {
269         private Environment env;
270         
271         CheckpointHook(Environment env) {
272             this.env = env;
273         }
274         public void doIOHook() throws IOException JavaDoc {}
275         public void doHook() {
276             try {
277                 EnvironmentImpl envImpl =
278                     DbInternal.envGetEnvironmentImpl(env);
279         CheckpointStart startEntry =
280             new CheckpointStart(100, "test");
281         long checkpointStart = envImpl.getLogManager().log(startEntry);
282                 CheckpointEnd endEntry =
283                     new CheckpointEnd("test",
284                                       checkpointStart,
285                                       envImpl.getRootLsn(),
286                                       envImpl.getTxnManager().getFirstActiveLsn(),
287                                       Node.getLastId(),
288                                       envImpl.getDbMapTree().getLastDbId(),
289                                       envImpl.getTxnManager().getLastTxnId(),
290                                       100);
291                 envImpl.getLogManager().logForceFlush(endEntry, true);
292             } catch (DatabaseException e) {
293             fail(e.getMessage());
294             }
295         }
296         public Object JavaDoc getHookValue() {return null;}
297     }
298
299     /**
300      * Make sure eviction doesn't evict roots. If it did, we'd need to
301      * log the mapLN to be sure that recovery is correct.
302      */

303     public void testChangeAndEvictRoot()
304         throws Throwable JavaDoc {
305
306         EnvironmentConfig envConfig = setupEnvConfig();
307         DatabaseConfig dbConfig = setupDbConfig();
308
309         /* Run the full test case w/out truncating the log. */
310         testOneCase(DB_NAME, envConfig, dbConfig,
311                     new TestGenerator(true /* generate log description. */){
312                         void generateData(Database db)
313                             throws DatabaseException {
314                             setupEvictedRoot(db);
315                         }
316                     },
317                     envConfig, dbConfig);
318
319         
320         /*
321          * Now run the test in a stepwise loop, truncate after each log entry.
322          * Our baseline expected set is empty -- no records expected.
323          */

324         HashSet JavaDoc currentExpected = new HashSet JavaDoc();
325         stepwiseLoop(DB_NAME, envConfig, dbConfig, currentExpected, 0);
326     }
327
328     /**
329      * Create a populated tree, delete all records, then begin to insert again.
330      */

331     private void setupEvictedRoot(Database db)
332         throws DatabaseException {
333         setStepwiseStart();
334
335         DatabaseEntry key = new DatabaseEntry();
336         DatabaseEntry data = new DatabaseEntry();
337
338         /* Populate a tree so it grows to 2 levels, with 2 BINs. */
339         for (int i = 0; i < 10; i ++) {
340             IntegerBinding.intToEntry(i, key);
341             IntegerBinding.intToEntry(i, data);
342             assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
343         }
344
345         Tracer.trace(Level.SEVERE, DbInternal.envGetEnvironmentImpl(env),
346                      "After inserts");
347         env.checkpoint(FORCE_CONFIG);
348
349         /*
350          * Add another record so that the eviction below will log
351          * a different versions of the IN nodes.
352          */

353         IntegerBinding.intToEntry(10, key);
354         IntegerBinding.intToEntry(10, data);
355         assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
356
357         /* Evict */
358         TestHook evictHook = new TestHook() {
359                 public void doIOHook() throws IOException JavaDoc {}
360                 public void doHook() {}
361                 public Object JavaDoc getHookValue() {
362                     return Boolean.TRUE;
363                 }
364             };
365         DbInternal.envGetEnvironmentImpl(env).getEvictor().
366                                            setRunnableHook(evictHook);
367         env.evictMemory();
368
369         /* Checkpoint again. */
370         env.checkpoint(FORCE_CONFIG);
371     }
372
373     private EnvironmentConfig setupEnvConfig() {
374         EnvironmentConfig envConfig = TestUtils.initEnvConfig();
375         turnOffEnvDaemons(envConfig);
376         envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(),
377                                  "4");
378         envConfig.setAllowCreate(true);
379         return envConfig;
380     }
381
382     private DatabaseConfig setupDbConfig() {
383         DatabaseConfig dbConfig = new DatabaseConfig();
384         dbConfig.setSortedDuplicates(useDups);
385         dbConfig.setAllowCreate(true);
386         return dbConfig;
387     }
388 }
389
Popular Tags