KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > tree > ReleaseLatchesTest


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

8 package com.sleepycat.je.tree;
9
10 import java.io.File JavaDoc;
11 import java.io.IOException JavaDoc;
12 import java.util.Enumeration JavaDoc;
13
14 import junit.framework.Test;
15 import junit.framework.TestCase;
16 import junit.framework.TestSuite;
17
18 import com.sleepycat.bind.tuple.IntegerBinding;
19 import com.sleepycat.je.CheckpointConfig;
20 import com.sleepycat.je.Cursor;
21 import com.sleepycat.je.Database;
22 import com.sleepycat.je.DatabaseConfig;
23 import com.sleepycat.je.DatabaseEntry;
24 import com.sleepycat.je.DatabaseException;
25 import com.sleepycat.je.DbInternal;
26 import com.sleepycat.je.Environment;
27 import com.sleepycat.je.EnvironmentConfig;
28 import com.sleepycat.je.LockMode;
29 import com.sleepycat.je.OperationStatus;
30 import com.sleepycat.je.RunRecoveryException;
31 import com.sleepycat.je.config.EnvironmentParams;
32 import com.sleepycat.je.dbi.EnvironmentImpl;
33 import com.sleepycat.je.latch.LatchSupport;
34 import com.sleepycat.je.log.FileManager;
35 import com.sleepycat.je.util.TestUtils;
36 import com.sleepycat.je.utilint.TestHook;
37
38 /**
39  * Check that latches are release properly even if we run into read errors.
40  */

41 public class ReleaseLatchesTest extends TestCase {
42     private static final boolean DEBUG = false;
43
44     private Environment env;
45     private File JavaDoc envHome;
46     private Database db;
47     private TestDescriptor testActivity;
48
49     /*
50      * The OPERATIONS declared here define the test cases for this test. Each
51      * TestDescriptor describes a particular JE activity. The
52      * testCheckLatchLeaks method generates read i/o exceptions during the test
53      * descriptor's action, and will check that we come up clean.
54      */

55     public static TestDescriptor [] OPERATIONS = {
56
57         /*
58          * TestDescriptor params:
59          * - operation name: for debugging
60          * - number of times to generate an exception. For example if N,
61          * the test action will be executed in a loop N times, with an
62          * read/io on read 1, read 2, read 3 ... read n-1
63          * - number of records in the database.
64          */

65         new TestDescriptor("database put", 6, 30, false) {
66             void doAction(ReleaseLatchesTest test, int exceptionCount)
67                 throws DatabaseException {
68
69                 test.populate(false);
70             }
71
72             void reinit(ReleaseLatchesTest test)
73                 throws DatabaseException{
74
75                 test.closeDb();
76                 test.getEnv().truncateDatabase(null, "foo", false);
77             }
78         },
79         new TestDescriptor("cursor scan", 31, 20, false) {
80             void doAction(ReleaseLatchesTest test, int exceptionCount)
81         throws DatabaseException {
82
83                 test.scan();
84             }
85         },
86         new TestDescriptor("cursor scan duplicates", 23, 3, true) {
87             void doAction(ReleaseLatchesTest test, int exceptionCount)
88         throws DatabaseException {
89
90                 test.scan();
91             }
92         },
93         new TestDescriptor("database get", 31, 20, false) {
94             void doAction(ReleaseLatchesTest test, int exceptionCount)
95                 throws DatabaseException {
96
97                 test.get();
98             }
99         },
100         new TestDescriptor("database delete", 40, 30, false) {
101             void doAction(ReleaseLatchesTest test, int exceptionCount)
102                 throws DatabaseException {
103
104                 test.delete();
105             }
106
107             void reinit(ReleaseLatchesTest test)
108                 throws DatabaseException{
109
110                 test.populate(false);
111             }
112         },
113         new TestDescriptor("checkpoint", 40, 10, false) {
114             void doAction(ReleaseLatchesTest test, int exceptionCount)
115                 throws DatabaseException {
116
117                 test.modify(exceptionCount);
118                 CheckpointConfig config = new CheckpointConfig();
119                 config.setForce(true);
120                 if (DEBUG) {
121                     System.out.println("Got to checkpoint");
122                 }
123                 test.getEnv().checkpoint(config);
124             }
125         },
126         new TestDescriptor("clean", 100, 5, false) {
127             void doAction(ReleaseLatchesTest test, int exceptionCount)
128                 throws DatabaseException {
129
130                 test.modify(exceptionCount);
131                 CheckpointConfig config = new CheckpointConfig();
132                 config.setForce(true);
133                 if (DEBUG) {
134                     System.out.println("Got to cleaning");
135                 }
136                 test.getEnv().cleanLog();
137             }
138         },
139         new TestDescriptor("compress", 20, 10, false) {
140             void doAction(ReleaseLatchesTest test, int exceptionCount)
141                 throws DatabaseException {
142
143                      test.delete();
144                      if (DEBUG) {
145                          System.out.println("Got to compress");
146                      }
147                      test.getEnv().compress();
148             }
149
150             void reinit(ReleaseLatchesTest test)
151                 throws DatabaseException{
152
153                 test.populate(false);
154             }
155         }
156     };
157
158     public static Test suite() {
159         TestSuite allTests = new TestSuite();
160         for (int i = 0; i < OPERATIONS.length; i++) {
161             TestSuite suite = new TestSuite(ReleaseLatchesTest.class);
162             Enumeration JavaDoc e = suite.tests();
163             while (e.hasMoreElements()) {
164                 ReleaseLatchesTest t = (ReleaseLatchesTest) e.nextElement();
165                 t.initTest(OPERATIONS[i]);
166                 allTests.addTest(t);
167             }
168         }
169         return allTests;
170     }
171
172     public ReleaseLatchesTest() {
173
174         envHome = new File JavaDoc(System.getProperty(TestUtils.DEST_DIR));
175     }
176
177     public void setUp()
178     throws IOException JavaDoc, DatabaseException {
179
180         TestUtils.removeFiles("Setup", envHome, FileManager.JE_SUFFIX);
181     }
182
183     public void tearDown()
184     throws IOException JavaDoc, DatabaseException {
185
186         setName(getName() + ":" + testActivity.getName());
187         TestUtils.removeFiles("TearDown", envHome,
188                               FileManager.JE_SUFFIX, true);
189     }
190
191     private void init(boolean duplicates)
192         throws DatabaseException {
193
194         openEnvAndDb();
195
196         populate(duplicates);
197         env.checkpoint(null);
198         db.close();
199         db = null;
200         env.close();
201         env = null;
202     }
203
204     private void openEnvAndDb()
205         throws DatabaseException {
206
207         /*
208          * Make an environment with small nodes and no daemons.
209          */

210         EnvironmentConfig envConfig = TestUtils.initEnvConfig();
211     DbInternal.disableParameterValidation(envConfig);
212         envConfig.setAllowCreate(true);
213         envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(), "4");
214         envConfig.setConfigParam("je.env.runEvictor", "false");
215         envConfig.setConfigParam("je.env.runCheckpointer", "false");
216         envConfig.setConfigParam("je.env.runCleaner", "false");
217         envConfig.setConfigParam("je.env.runINCompressor", "false");
218         envConfig.setConfigParam
219             (EnvironmentParams.CLEANER_MIN_UTILIZATION.getName(), "90");
220         envConfig.setConfigParam(EnvironmentParams.LOG_FILE_MAX.getName(),
221                   Integer.toString(20000));
222
223         env = new Environment(envHome, envConfig);
224
225         DatabaseConfig dbConfig = new DatabaseConfig();
226         dbConfig.setAllowCreate(true);
227         dbConfig.setSortedDuplicates(true);
228         db = env.openDatabase(null, "foo", dbConfig);
229     }
230
231     /* Calling close under -ea will check for leaked latches. */
232     private void doCloseAndCheckLeaks()
233         throws Throwable JavaDoc {
234
235         try {
236             if (db != null) {
237                 db.close();
238                 db = null;
239             }
240
241             if (env != null) {
242                 env.close();
243                 env = null;
244             }
245         } catch (Throwable JavaDoc t) {
246             System.out.println("operation = " + testActivity.name);
247             t.printStackTrace();
248             throw t;
249         }
250     }
251     
252     private void closeDb()
253         throws DatabaseException {
254
255         if (db != null) {
256             db.close();
257             db = null;
258         }
259     }
260
261     private Environment getEnv() {
262         return env;
263     }
264
265     private void initTest(TestDescriptor action) {
266         this.testActivity = action;
267     }
268
269     /*
270      * This is the heart of the unit test. Given a TestDescriptor, run the
271      * operation's activity in a loop, generating read i/o exceptions at
272      * different points. Check for latch leaks after the i/o exception
273      * happens.
274      */

275     public void testCheckLatchLeaks()
276         throws Throwable JavaDoc {
277
278         int maxExceptionCount = testActivity.getNumExceptions();
279         if (DEBUG) {
280             System.out.println("Starting test: " + testActivity.getName());
281         }
282
283         try {
284             init(testActivity.getDuplicates());
285
286             /*
287              * Run the action repeatedly, generating exceptions at different
288              * points.
289              */

290             for (int i = 1; i <= maxExceptionCount; i++) {
291                 
292                 /*
293                  * Open the env and database anew each time, so that we need to
294                  * fault in objects and will trigger read i/o exceptions.
295                  */

296                 openEnvAndDb();
297                 EnvironmentImpl envImpl =
298                     DbInternal.envGetEnvironmentImpl(env);
299                 boolean exceptionOccurred = false;
300
301                 try {
302                     ReadIOExceptionHook readHook = new ReadIOExceptionHook(i);
303                     envImpl.getLogManager().setReadHook(readHook);
304                     testActivity.doAction(this, i);
305                 } catch (RunRecoveryException e) {
306
307                     /*
308              * It's possible for a read error to induce a
309              * RunRecoveryException if the read error happens when we
310              * are opening a new write file channel. (We read and
311              * validate the file header). In that case, check for
312              * latches, and re-open the database.
313                      */

314                     checkLatchCount(e, i);
315                     env.close();
316                     openEnvAndDb();
317                     exceptionOccurred = true;
318                 } catch (DatabaseException e) {
319                     checkLatchCount(e, i);
320                     exceptionOccurred = true;
321                 }
322                 
323                 if (DEBUG && !exceptionOccurred) {
324                     System.out.println("Don't need ex count " + i +
325                                        " for test activity " +
326                                        testActivity.getName());
327                 }
328
329                 envImpl.getLogManager().setReadHook(null);
330                 testActivity.reinit(this);
331                 doCloseAndCheckLeaks();
332             }
333         } catch (Throwable JavaDoc t) {
334             t.printStackTrace();
335             throw t;
336         }
337     }
338
339     private void checkLatchCount(DatabaseException e,
340                                  int exceptionCount)
341         throws DatabaseException {
342
343     /* Only rethrow the exception if we didn't clean up latches. */
344         if (LatchSupport.countLatchesHeld() > 0) {
345             LatchSupport.dumpLatchesHeld();
346             System.out.println("Operation = " + testActivity.getName() +
347                                " exception count=" + exceptionCount +
348                                " Held latches = " +
349                                LatchSupport.countLatchesHeld());
350             /* Show stacktrace where the latch was lost. */
351             e.printStackTrace();
352             throw e;
353         }
354     }
355
356     /* Insert records into a database. */
357     private void populate(boolean duplicates)
358         throws DatabaseException {
359
360         DatabaseEntry key = new DatabaseEntry();
361         DatabaseEntry data = new DatabaseEntry();
362         DatabaseEntry data1 = new DatabaseEntry();
363         DatabaseEntry data2 = new DatabaseEntry();
364         DatabaseEntry data3 = new DatabaseEntry();
365         DatabaseEntry data4 = new DatabaseEntry();
366         IntegerBinding.intToEntry(0, data);
367         IntegerBinding.intToEntry(1, data1);
368         IntegerBinding.intToEntry(2, data2);
369         IntegerBinding.intToEntry(3, data3);
370         IntegerBinding.intToEntry(4, data4);
371
372         for (int i = 0; i < testActivity.getNumRecords(); i++) {
373             IntegerBinding.intToEntry(i, key);
374             assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
375         if (duplicates) {
376         assertEquals(OperationStatus.SUCCESS,
377                  db.put(null, key, data1));
378         assertEquals(OperationStatus.SUCCESS,
379                  db.put(null, key, data2));
380         assertEquals(OperationStatus.SUCCESS,
381                  db.put(null, key, data3));
382         assertEquals(OperationStatus.SUCCESS,
383                  db.put(null, key, data4));
384         }
385         }
386     }
387
388     /* Modify the database. */
389     private void modify(int dataVal)
390         throws DatabaseException {
391
392         DatabaseEntry key = new DatabaseEntry();
393         DatabaseEntry data = new DatabaseEntry();
394         IntegerBinding.intToEntry(dataVal, data);
395
396         for (int i = 0; i < testActivity.getNumRecords(); i++) {
397             IntegerBinding.intToEntry(i, key);
398             assertEquals(OperationStatus.SUCCESS, db.put(null, key, data));
399         }
400     }
401
402     /* Cursor scan the data. */
403     private void scan()
404         throws DatabaseException {
405
406         Cursor cursor = null;
407         try {
408             cursor = db.openCursor(null, null);
409             DatabaseEntry key = new DatabaseEntry();
410             DatabaseEntry data = new DatabaseEntry();
411
412             while (cursor.getNext(key, data, LockMode.DEFAULT) ==
413                    OperationStatus.SUCCESS) {
414             }
415         } finally {
416             if (cursor != null) {
417                 cursor.close();
418             }
419         }
420     }
421
422     /* Database.get() for all records. */
423     private void get()
424         throws DatabaseException {
425
426         DatabaseEntry key = new DatabaseEntry();
427         DatabaseEntry data = new DatabaseEntry();
428         for (int i = 0; i < testActivity.getNumRecords(); i++) {
429             IntegerBinding.intToEntry(i, key);
430             assertEquals(OperationStatus.SUCCESS,
431                          db.get(null, key, data, LockMode.DEFAULT));
432         }
433     }
434
435     /* Delete all records. */
436     private void delete()
437         throws DatabaseException {
438
439         DatabaseEntry key = new DatabaseEntry();
440         for (int i = 0; i < testActivity.getNumRecords(); i++) {
441             IntegerBinding.intToEntry(i, key);
442             assertEquals("key = " + IntegerBinding.entryToInt(key),
443                          OperationStatus.SUCCESS, db.delete(null, key));
444         }
445     }
446     /*
447      * This TestHook implementation generates io exceptions during reads.
448      */

449     static class ReadIOExceptionHook implements TestHook {
450         private int counter = 0;
451         private int throwCount;
452         
453         ReadIOExceptionHook(int throwCount) {
454             this.throwCount = throwCount;
455         }
456         
457         public void doIOHook()
458             throws IOException JavaDoc {
459
460             if (throwCount == counter) {
461                 counter++;
462                 throw new IOException JavaDoc("Generated exception: " +
463                                       this.getClass().getName());
464             } else {
465                 counter++;
466             }
467         }
468
469     public void doHook() {}
470
471         public Object JavaDoc getHookValue() {
472             return null;
473         }
474     }
475
476     static abstract class TestDescriptor {
477         private String JavaDoc name;
478         private int numExceptions;
479         private int numRecords;
480     private boolean duplicates;
481
482         TestDescriptor(String JavaDoc name,
483                int numExceptions,
484                int numRecords,
485                boolean duplicates) {
486             this.name = name;
487             this.numExceptions = numExceptions;
488             this.numRecords = numRecords;
489         this.duplicates = duplicates;
490         }
491
492         int getNumRecords() {
493             return numRecords;
494         }
495
496         int getNumExceptions() {
497             return numExceptions;
498         }
499         
500         String JavaDoc getName() {
501             return name;
502         }
503             
504     boolean getDuplicates() {
505         return duplicates;
506     }
507
508         /* Do a series of operations. */
509         abstract void doAction(ReleaseLatchesTest test,
510                                int exceptionCount)
511             throws DatabaseException;
512
513         /* Reinitialize the database if doAction modified it. */
514         void reinit(ReleaseLatchesTest test)
515         throws DatabaseException {
516
517     }
518     }
519 }
520
Popular Tags