KickJava   Java API By Example, From Geeks To Geeks.

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


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

8
9 package com.sleepycat.je.recovery;
10
11 import java.io.File JavaDoc;
12 import java.io.FileInputStream JavaDoc;
13 import java.io.FileOutputStream JavaDoc;
14 import java.io.IOException JavaDoc;
15 import java.io.InputStream JavaDoc;
16 import java.io.OutputStream JavaDoc;
17 import java.util.ArrayList JavaDoc;
18 import java.util.HashMap JavaDoc;
19 import java.util.HashSet JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.Map JavaDoc;
23 import java.util.Set JavaDoc;
24 import java.util.logging.Level JavaDoc;
25
26 import junit.framework.TestCase;
27
28 import com.sleepycat.bind.tuple.IntegerBinding;
29 import com.sleepycat.je.Cursor;
30 import com.sleepycat.je.Database;
31 import com.sleepycat.je.DatabaseConfig;
32 import com.sleepycat.je.DatabaseEntry;
33 import com.sleepycat.je.DatabaseException;
34 import com.sleepycat.je.DbInternal;
35 import com.sleepycat.je.Environment;
36 import com.sleepycat.je.EnvironmentConfig;
37 import com.sleepycat.je.OperationStatus;
38 import com.sleepycat.je.VerifyConfig;
39 import com.sleepycat.je.cleaner.VerifyUtils;
40 import com.sleepycat.je.config.EnvironmentParams;
41 import com.sleepycat.je.dbi.EnvironmentImpl;
42 import com.sleepycat.je.log.FileManager;
43 import com.sleepycat.je.recovery.stepwise.EntryTrackerReader;
44 import com.sleepycat.je.recovery.stepwise.LogEntryInfo;
45 import com.sleepycat.je.recovery.stepwise.TestData;
46 import com.sleepycat.je.util.TestUtils;
47 import com.sleepycat.je.utilint.CmdUtil;
48 import com.sleepycat.je.utilint.DbLsn;
49 import com.sleepycat.je.utilint.Tracer;
50
51 public class CheckBase extends TestCase {
52
53     private static final boolean DEBUG = false;
54     private HashSet JavaDoc expected;
55     private Set JavaDoc found;
56
57     File JavaDoc envHome;
58     Environment env;
59
60     private List JavaDoc logDescription;
61     private long stepwiseStartLsn;
62
63     private boolean checkLsns = true;
64
65     public CheckBase() {
66         envHome = new File JavaDoc(System.getProperty(TestUtils.DEST_DIR));
67     }
68
69     public void setUp()
70         throws IOException JavaDoc {
71
72         TestUtils.removeLogFiles("Setup", envHome, false);
73         TestUtils.removeFiles("Setup", envHome, ".jdb_save");
74     }
75     
76     public void tearDown() {
77         if (env != null) {
78             try {
79                 env.close();
80                 env = null;
81             } catch (Exception JavaDoc ignore) {
82             }
83         }
84         
85         try {
86             TestUtils.removeLogFiles("TearDown", envHome, false);
87             TestUtils.removeFiles("TearDown", envHome, ".jdb_save");
88         } catch (Exception JavaDoc ignore) {
89         }
90     }
91
92     /**
93      * Create an environment, generate data, record the expected values.
94      * Then close the environment and recover, and check that the expected
95      * values are there.
96      */

97     protected void testOneCase(String JavaDoc dbName,
98                                EnvironmentConfig startEnvConfig,
99                                DatabaseConfig startDbConfig,
100                                TestGenerator testGen,
101                                EnvironmentConfig validateEnvConfig,
102                                DatabaseConfig validateDbConfig)
103         throws Throwable JavaDoc {
104
105         try {
106             /* Create an environment. */
107             env = new Environment(envHome, startEnvConfig);
108             Database db = env.openDatabase(null, dbName, startDbConfig);
109
110             /* Generate test data. */
111             testGen.generateData(db);
112
113             /* Scan the database to save what values we should have. */
114             loadExpectedData(db);
115
116             /* Check for overlap between the tree and utilization profile. */
117         if (checkLsns) {
118         VerifyUtils.checkLsns(db);
119         }
120
121             /* Close w/out checkpointing. */
122             db.close();
123             DbInternal.envGetEnvironmentImpl(env).close(false);
124             env = null;
125
126             if (testGen.generateLogDescription) {
127                 makeLogDescription();
128             }
129
130             tryRecovery(validateEnvConfig,
131                         validateDbConfig,
132                         dbName,
133                         expected);
134         } catch (Throwable JavaDoc t) {
135             /* Dump stack trace before trying to tear down. */
136             t.printStackTrace();
137             throw t;
138         }
139     }
140
141     /* Run recovery and validation twice. */
142     private void tryRecovery(EnvironmentConfig validateEnvConfig,
143                              DatabaseConfig validateDbConfig,
144                              String JavaDoc dbName,
145                              HashSet JavaDoc useExpected)
146         throws DatabaseException {
147         /* Recover and load what's in the database post-recovery. */
148         recoverAndLoadData(validateEnvConfig,
149                            validateDbConfig,
150                            dbName);
151
152         /* Check the pre and post recovery data. */
153         if (useExpected == null) {
154             useExpected = expected;
155         }
156         validate(useExpected);
157
158         /* Repeat the recovery and validation. */
159         recoverAndLoadData(validateEnvConfig,
160                            validateDbConfig,
161                            dbName);
162
163         validate(useExpected);
164     }
165     
166     void setCheckLsns(boolean checkLsns) {
167     this.checkLsns = checkLsns;
168     }
169
170     /**
171      * Call this method to set the start of the stepwise loop. The stepwise
172      * testing will begin at this point in the log.
173      */

174     void setStepwiseStart() {
175
176         /*
177          * Put a tracing message both for debugging assistance, and also
178          * to force the truncation to start at this log entry, since we're
179          * getting the last used LSN.
180          */

181         Tracer.trace(Level.SEVERE, DbInternal.envGetEnvironmentImpl(env),
182                      "StepwiseStart");
183         FileManager fileManager =
184             DbInternal.envGetEnvironmentImpl(env).getFileManager();
185         stepwiseStartLsn = fileManager.getLastUsedLsn();
186     }
187
188     void stepwiseLoop(String JavaDoc dbName,
189                       EnvironmentConfig validateEnvConfig,
190                       DatabaseConfig validateDbConfig,
191                       HashSet JavaDoc useExpected,
192                       int startingIteration)
193         throws DatabaseException, IOException JavaDoc {
194
195         assertTrue(logDescription.size() > 0);
196         saveLogFiles(envHome);
197         
198         /* txnId -> LogEntryInfo */
199         Map JavaDoc newUncommittedRecords = new HashMap JavaDoc();
200         Map JavaDoc deletedUncommittedRecords = new HashMap JavaDoc();
201
202         /* Now run recovery repeatedly, truncating at different locations. */
203         String JavaDoc status = null;
204         try {
205
206             /*
207              * Some tests are not working with starting at 0. As a workaround,
208              * start at another iteration.
209              */

210             DatabaseEntry keyEntry = new DatabaseEntry();
211             DatabaseEntry dataEntry = new DatabaseEntry();
212             for (int i = startingIteration; i < logDescription.size(); i++ ) {
213
214                 /* Find out where to truncate. */
215                 LogEntryInfo info = (LogEntryInfo) logDescription.get(i);
216                 long lsn = info.getLsn();
217
218                 if (lsn == 0) {
219                     continue;
220                 }
221
222                 status = "Iteration " + i + " out of " +
223                     logDescription.size() + " truncate at 0x" +
224                     DbLsn.getNoFormatString(lsn);
225
226                 if (DEBUG) {
227                     System.out.println(status);
228                 }
229             
230                 /* copy files back. */
231                 resetLogFiles(envHome);
232
233                 /* truncate */
234                 truncateAtOffset(envHome, lsn);
235
236                 /* recover */
237                 tryRecovery(validateEnvConfig, validateDbConfig,
238                             dbName, useExpected);
239
240                 /* Adjust the set of expected records for the next iteration.*/
241                 info.updateExpectedSet(useExpected, newUncommittedRecords,
242                                        deletedUncommittedRecords);
243             }
244         } catch (Error JavaDoc e) {
245             System.err.println("Failure at step: " + status);
246             throw e;
247         }
248     }
249
250     protected void turnOffEnvDaemons(EnvironmentConfig envConfig) {
251         envConfig.setConfigParam(EnvironmentParams.ENV_RUN_CLEANER.getName(),
252                                  "false");
253         envConfig.setConfigParam(EnvironmentParams.
254                                  ENV_RUN_CHECKPOINTER.getName(),
255                                  "false");
256         envConfig.setConfigParam(EnvironmentParams.ENV_RUN_EVICTOR.getName(),
257                                  "false");
258         envConfig.setConfigParam(EnvironmentParams.
259                                  ENV_RUN_INCOMPRESSOR.getName(),
260                                  "false");
261     }
262
263     /**
264      * Re-open the environment and load all data present, to compare to the
265      * data set of expected values.
266      */

267     protected void recoverAndLoadData(EnvironmentConfig envConfig,
268                                       DatabaseConfig dbConfig,
269                                       String JavaDoc dbName)
270         throws DatabaseException {
271
272         env = new Environment(envHome, envConfig);
273         Database db = env.openDatabase(null, dbName, dbConfig);
274
275         /* Check for overlap between the tree and utilization profile. */
276     if (checkLsns) {
277         VerifyUtils.checkLsns(db);
278     }
279
280         found = new HashSet JavaDoc();
281
282         Cursor cursor = db.openCursor(null, null);
283         DatabaseEntry key = new DatabaseEntry();
284         DatabaseEntry data = new DatabaseEntry();
285
286         try {
287             while (cursor.getNext(key, data, null) ==
288                    OperationStatus.SUCCESS) {
289                 TestData t = new TestData(key, data);
290                 if (DEBUG) {
291                     System.out.println("found k=" +
292                                        IntegerBinding.entryToInt(key) +
293                                        " d=" +
294                                        IntegerBinding.entryToInt(data));
295                 }
296                 found.add(t);
297             }
298         }
299         finally {
300             cursor.close();
301         }
302
303         db.close();
304         
305         assertTrue(env.verify(new VerifyConfig(), System.out));
306         env.close();
307     }
308
309     /*
310      * The found and expected data sets need to match exactly after recovery.
311      */

312     void validate(HashSet JavaDoc expected)
313         throws DatabaseException {
314
315         Set JavaDoc useExpected = (Set JavaDoc) expected.clone();
316         
317         if (useExpected.size() != found.size()) {
318             System.err.println("expected---------");
319             dumpHashSet(useExpected);
320             System.err.println("actual---------");
321             dumpHashSet(found);
322             assertEquals("expected and found set sizes don't match" ,
323                          useExpected.size(), found.size());
324         }
325
326         Iterator JavaDoc foundIter = found.iterator();
327         while (foundIter.hasNext()) {
328             TestData t = (TestData) foundIter.next();
329             assertTrue("Missing " + t + "from the expected set",
330                        useExpected.remove(t));
331         }
332
333         assertEquals("Expected has " + useExpected.size() + " items remaining",
334                      0, useExpected.size());
335     }
336
337     protected void putTestData(Database db,
338                              DatabaseEntry key,
339                              DatabaseEntry data)
340         throws DatabaseException {
341
342         assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
343     }
344
345     private void loadExpectedData(Database db)
346         throws DatabaseException {
347         
348         expected = new HashSet JavaDoc();
349
350         Cursor cursor = db.openCursor(null, null);
351         DatabaseEntry key = new DatabaseEntry();
352         DatabaseEntry data = new DatabaseEntry();
353
354         try {
355             while (cursor.getNext(key, data, null) ==
356                    OperationStatus.SUCCESS) {
357                 if (DEBUG) {
358                     System.out.println("expect k=" +
359                                        IntegerBinding.entryToInt(key) +
360                                        " d=" +
361                                        IntegerBinding.entryToInt(data));
362                 }
363                 TestData t = new TestData(key, data);
364                 expected.add(t);
365             }
366         }
367         finally {
368             cursor.close();
369         }
370     }
371
372     void dumpData(Database db)
373         throws DatabaseException {
374
375         Cursor cursor = db.openCursor(null, null);
376         DatabaseEntry key = new DatabaseEntry();
377         DatabaseEntry data = new DatabaseEntry();
378         int i = 0;
379         try {
380             while (cursor.getNext(key, data, null) ==
381                    OperationStatus.SUCCESS) {
382                 TestData t = new TestData(key, data);
383                 System.out.println(t);
384                 i++;
385             }
386         }
387         finally {
388             cursor.close();
389         }
390         System.out.println("scanned=" + i);
391     }
392
393     private void dumpHashSet(Set JavaDoc expected) {
394         Iterator JavaDoc iter = expected.iterator();
395         System.err.println("size=" + expected.size());
396         while (iter.hasNext()) {
397             System.err.println((TestData)iter.next());
398         }
399     }
400
401     private void makeLogDescription()
402         throws DatabaseException {
403
404         EnvironmentImpl cmdEnvImpl =
405             CmdUtil.makeUtilityEnvironment(envHome, false);
406         logDescription = new ArrayList JavaDoc();
407
408         try {
409             EntryTrackerReader reader =
410                 new EntryTrackerReader(cmdEnvImpl,
411                                        stepwiseStartLsn,
412                                        logDescription);
413             while (reader.readNextEntry()) {
414             }
415         } catch (IOException JavaDoc e) {
416             throw new DatabaseException(e);
417         } finally {
418             cmdEnvImpl.close();
419         }
420
421         if (DEBUG) {
422             Iterator JavaDoc iter = logDescription.iterator();
423             while (iter.hasNext()) {
424             Object JavaDoc o = iter.next();
425             LogEntryInfo entryInfo =(LogEntryInfo) o;
426                 System.out.println(entryInfo);
427             }
428         }
429     }
430
431     /**
432      * Truncate the log at the specified offset.
433      */

434     private void truncateAtOffset(File JavaDoc envHome, long lsn)
435         throws DatabaseException, IOException JavaDoc {
436
437         EnvironmentImpl cmdEnvImpl =
438             CmdUtil.makeUtilityEnvironment(envHome, false);
439
440         cmdEnvImpl.getFileManager().truncateLog(DbLsn.getFileNumber(lsn),
441                                                 DbLsn.getFileOffset(lsn));
442
443         cmdEnvImpl.close();
444     }
445
446     /* Copy all .jdb files to .jdb_save for stepwise processing. */
447     private void saveLogFiles(File JavaDoc envHome)
448         throws IOException JavaDoc {
449
450         String JavaDoc [] suffix = new String JavaDoc [] {".jdb"};
451         String JavaDoc [] fileNames = FileManager.listFiles(envHome, suffix);
452
453         for (int i = 0; i < fileNames.length; i++) {
454             File JavaDoc src = new File JavaDoc(envHome, fileNames[i]);
455             File JavaDoc dst = new File JavaDoc(envHome, fileNames[i]+ "_save");
456             copy(src, dst);
457         }
458     }
459
460     /* Copy all .jdb_save file back to ._jdb */
461     private void resetLogFiles(File JavaDoc envHome)
462         throws IOException JavaDoc {
463         String JavaDoc [] suffix = new String JavaDoc [] {".jdb_save"};
464         String JavaDoc [] fileNames = FileManager.listFiles(envHome, suffix);
465
466         for (int i = 0; i < fileNames.length; i++) {
467             String JavaDoc srcName = fileNames[i];
468             int end = srcName.indexOf("_save");
469             String JavaDoc dstName = srcName.substring(0, end);
470             copy(new File JavaDoc(envHome, srcName), new File JavaDoc(envHome, dstName));
471         }
472     }
473
474     private void copy(File JavaDoc src, File JavaDoc dst)
475         throws IOException JavaDoc {
476
477         InputStream JavaDoc in = new FileInputStream JavaDoc(src);
478         OutputStream JavaDoc out = new FileOutputStream JavaDoc(dst);
479     
480         // Transfer bytes from in to out
481
byte[] buf = new byte[1024];
482         int len;
483         while ((len = in.read(buf)) > 0) {
484             out.write(buf, 0, len);
485         }
486         in.close();
487         out.close();
488     }
489
490     /*
491      * Each unit test overrides the generateData method. Don't make this
492      * abstract, because we may want different unit tests to call different
493      * flavors of generateData(), and we want a default implementation for each
494      * flavor.
495      *
496      * A unit test may also specify an implementation for truncateLog. When
497      * that happens, the truncation is done before the first recovery.
498      */

499     protected class TestGenerator {
500
501         /* If true, generate a log description to use in stepwise testing. */
502         boolean generateLogDescription;
503
504         /*
505          * Some generators run off a list of test operations which specify
506          * what actions to use when generating data.
507          */

508         public List JavaDoc operationList;
509
510         public TestGenerator() {
511         }
512
513         public TestGenerator(boolean generateLogDescription) {
514             this.generateLogDescription = generateLogDescription;
515         }
516
517         public TestGenerator(List JavaDoc operationList) {
518             this.operationList = operationList;
519         }
520
521         void generateData(Database db)
522             throws Exception JavaDoc {
523         }
524     }
525 }
526
Popular Tags