KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > incomp > INCompressorTest


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

8
9 package com.sleepycat.je.incomp;
10
11 import java.io.File JavaDoc;
12 import java.io.IOException JavaDoc;
13
14 import junit.framework.TestCase;
15
16 import com.sleepycat.je.CheckpointConfig;
17 import com.sleepycat.je.Cursor;
18 import com.sleepycat.je.Database;
19 import com.sleepycat.je.DatabaseConfig;
20 import com.sleepycat.je.DatabaseEntry;
21 import com.sleepycat.je.DatabaseException;
22 import com.sleepycat.je.DbInternal;
23 import com.sleepycat.je.Environment;
24 import com.sleepycat.je.EnvironmentConfig;
25 import com.sleepycat.je.OperationStatus;
26 import com.sleepycat.je.Transaction;
27 import com.sleepycat.je.config.EnvironmentParams;
28 import com.sleepycat.je.tree.BIN;
29 import com.sleepycat.je.tree.DBIN;
30 import com.sleepycat.je.tree.IN;
31 import com.sleepycat.je.util.TestUtils;
32
33 /**
34  * Test that BIN compression occurs in the various ways it is supposed to.
35  * <p>These are:</p>
36  * <ul>
37  * <li>transactional and non-transactional delete,</li>
38  * <li>delete duplicates and non-duplicates,</li>
39  * <li>removal of empty sub-trees (duplicates and non-duplicates),</li>
40  * <li>compression of BIN for deleted DIN subtree.</li>
41  * <li>removal of empty BIN after deleting a DIN subtree.</li>
42  * <li>undo causes compression of inserted LN during abort and recovery,</li>
43  * <li>redo causes compression of deleted LN during recovery,</li>
44  * </ul>
45  *
46  * <p>Also test that compression retries occur after we attempt to compress but
47  * cannot because:</p>
48  * <ul>
49  * <li>cursors are open on the BIN when the compressor dequeues them,</li>
50  * <li>cursors are open when attempting to delete a sub-tree (dup and non-dup
51  * are two separate code paths).</li>
52  * <li>a deleted key is locked during compression (NOT TESTED - this is very
53  * difficult to reproduce),</li>
54  * </ul>
55  *
56  * <p>Possible problem: When we attempt to delete a subtree because the BIN is
57  * empty, we give up when NodeNotEmptyException is thrown by the search.
58  * However, this is thrown not only when entries have been added but also when
59  * there are cursors on the BIN; it seems like we should retry in the latter
60  * case. Or is it impossible to have a cursor on an empty BIN?</p>
61  *
62  * <p>We do not test here the last ditch effort to compress to make room in
63  * IN.insertEntry1; that should never happen in theory, so I dodn't think it
64  * is worthwhile to try to reproduce it.</p>
65  */

66 public class INCompressorTest extends TestCase {
67     private static final boolean DEBUG = false;
68     private static final int NUM_RECS = 257;
69
70     private File JavaDoc envHome;
71     private Environment env;
72     private Database db;
73     private IN in;
74     private BIN bin;
75     private DBIN dbin;
76     private boolean hasDups;
77
78     /* Use high keys since we fill the first BIN with low keys. */
79     private DatabaseEntry entry0 = new DatabaseEntry(new byte[] {0});
80     private DatabaseEntry entry1 = new DatabaseEntry(new byte[] {1});
81     private DatabaseEntry entry2 = new DatabaseEntry(new byte[] {2});
82     private DatabaseEntry keyFound = new DatabaseEntry();
83     private DatabaseEntry dataFound = new DatabaseEntry();
84
85     public INCompressorTest() {
86         envHome = new File JavaDoc(System.getProperty(TestUtils.DEST_DIR));
87     }
88
89     public void setUp()
90         throws IOException JavaDoc {
91
92         TestUtils.removeLogFiles("Setup", envHome, false);
93     }
94     
95     public void tearDown()
96         throws Exception JavaDoc {
97
98         if (env != null) {
99             try { env.close(); } catch (DatabaseException ignored) { }
100         }
101         TestUtils.removeLogFiles("TearDown", envHome, false);
102         env = null;
103         db = null;
104         in = null;
105         bin = null;
106         dbin = null;
107         entry0 = null;
108         entry1 = null;
109         entry2 = null;
110         keyFound = null;
111         dataFound = null;
112     }
113
114     public void testDeleteTransactional()
115         throws DatabaseException {
116
117         /* Transactional no-dups, 2 keys. */
118         openAndInit(true, false);
119         OperationStatus status;
120
121         /* Cursor appears on BIN. */
122         Transaction txn = env.beginTransaction(null, null);
123         Cursor cursor = db.openCursor(txn, null);
124         status = cursor.getFirst(keyFound, dataFound, null);
125         assertEquals(OperationStatus.SUCCESS, status);
126         checkBinEntriesAndCursors(bin, 2, 1);
127
128         /* Delete without closing the cursor does not compress. */
129         status = cursor.delete();
130         assertEquals(OperationStatus.SUCCESS, status);
131         env.compress();
132         checkBinEntriesAndCursors(bin, 2, 1);
133
134         /* Closing the cursor without commit does not compress. */
135         cursor.close();
136         env.compress();
137         checkBinEntriesAndCursors(bin, 2, 0);
138
139         /* Commit without calling compress does not compress. */
140         txn.commit();
141         checkBinEntriesAndCursors(bin, 2, 0);
142
143         /* Finally compress can compress. */
144         env.compress();
145         checkBinEntriesAndCursors(bin, 1, 0);
146
147         /* Should be no change in parent nodes. */
148         assertEquals(2, in.getNEntries());
149
150         closeEnv();
151     }
152
153     public void testDeleteNonTransactional()
154         throws DatabaseException {
155
156         /* Non-transactional no-dups, 2 keys. */
157         openAndInit(false, false);
158         OperationStatus status;
159
160         /* Cursor appears on BIN. */
161         Cursor cursor = db.openCursor(null, null);
162         status = cursor.getFirst(keyFound, dataFound, null);
163         assertEquals(OperationStatus.SUCCESS, status);
164         checkBinEntriesAndCursors(bin, 2, 1);
165
166         /* Delete without closing the cursor does not compress. */
167         status = cursor.delete();
168         assertEquals(OperationStatus.SUCCESS, status);
169         env.compress();
170         checkBinEntriesAndCursors(bin, 2, 1);
171
172         /* Closing the cursor without calling compress does not compress. */
173         cursor.close();
174         checkBinEntriesAndCursors(bin, 2, 0);
175
176         /* Finally compress can compress. */
177         env.compress();
178         checkBinEntriesAndCursors(bin, 1, 0);
179
180         /* Should be no change in parent nodes. */
181         assertEquals(2, in.getNEntries());
182
183         closeEnv();
184     }
185
186     public void testDeleteDuplicate()
187         throws DatabaseException {
188
189         /* Non-transactional dups, 2 keys and 2 dups for 1st key. */
190         openAndInit(false, true);
191         OperationStatus status;
192
193         /* Cursor appears on DBIN. */
194         Cursor cursor = db.openCursor(null, null);
195         status = cursor.getFirst(keyFound, dataFound, null);
196         assertEquals(OperationStatus.SUCCESS, status);
197         checkBinEntriesAndCursors(dbin, 2, 1);
198
199         /* Delete without closing the cursor does not compress. */
200         status = cursor.delete();
201         assertEquals(OperationStatus.SUCCESS, status);
202         env.compress();
203         checkBinEntriesAndCursors(dbin, 2, 1);
204
205         /* Closing the cursor without calling compress does not compress. */
206         cursor.close();
207         checkBinEntriesAndCursors(dbin, 2, 0);
208
209         /* Finally compress can compress. */
210         env.compress();
211         checkBinEntriesAndCursors(dbin, 1, 0);
212
213         /* Should be no change in parent nodes. */
214         assertEquals(2, in.getNEntries());
215         checkBinEntriesAndCursors(bin, 2, 0);
216
217         closeEnv();
218     }
219
220     public void testRemoveEmptyBIN()
221         throws DatabaseException {
222
223         /* Non-transactional no-dups, 2 keys. */
224         openAndInit(false, false);
225         OperationStatus status;
226
227         /* Cursor appears on BIN. */
228         Cursor cursor = db.openCursor(null, null);
229         status = cursor.getFirst(keyFound, dataFound, null);
230         assertEquals(OperationStatus.SUCCESS, status);
231         checkBinEntriesAndCursors(bin, 2, 1);
232
233         /* Delete without closing the cursor does not compress. */
234         status = cursor.delete();
235         assertEquals(OperationStatus.SUCCESS, status);
236         status = cursor.getNext(keyFound, dataFound, null);
237         assertEquals(OperationStatus.SUCCESS, status);
238         status = cursor.delete();
239         assertEquals(OperationStatus.SUCCESS, status);
240         env.compress();
241         checkBinEntriesAndCursors(bin, 2, 1);
242
243         /* Closing the cursor without calling compress does not compress. */
244         cursor.close();
245         checkBinEntriesAndCursors(bin, 2, 0);
246
247         /* Finally compress can compress. */
248         env.compress();
249         checkBinEntriesAndCursors(bin, 0, 0);
250
251         /* BIN is empty so parent entry should be gone also. */
252         assertEquals(1, in.getNEntries());
253
254         closeEnv();
255     }
256
257     public void testRemoveEmptyDBIN()
258         throws DatabaseException {
259
260         /* Non-transactional dups, 2 keys and 2 dups for 1st key. */
261         openAndInit(false, true);
262         OperationStatus status;
263
264         /* Cursor appears on DBIN. */
265         Cursor cursor = db.openCursor(null, null);
266         status = cursor.getFirst(keyFound, dataFound, null);
267         assertEquals(OperationStatus.SUCCESS, status);
268         checkBinEntriesAndCursors(dbin, 2, 1);
269
270         /* Delete without closing the cursor does not compress. */
271         status = cursor.delete();
272         assertEquals(OperationStatus.SUCCESS, status);
273         status = cursor.getNext(keyFound, dataFound, null);
274         assertEquals(OperationStatus.SUCCESS, status);
275         status = cursor.delete();
276         assertEquals(OperationStatus.SUCCESS, status);
277         env.compress();
278         checkBinEntriesAndCursors(dbin, 2, 1);
279
280         /* Closing the cursor without calling compress does not compress. */
281         cursor.close();
282         checkBinEntriesAndCursors(dbin, 2, 0);
283
284         /* Finally compress can compress. */
285         env.compress();
286         checkBinEntriesAndCursors(dbin, 0, 0);
287
288         /* BIN parent should have one less entry. */
289         assertEquals(2, in.getNEntries());
290         checkBinEntriesAndCursors(bin, 1, 0);
291
292         closeEnv();
293     }
294
295     public void testRemoveEmptyDBINandBIN()
296         throws DatabaseException {
297
298         /* Non-transactional dups, 2 keys and 2 dups for 1st key. */
299         openAndInit(false, true);
300         OperationStatus status;
301
302         /* Delete key 1, cursor appears on BIN, no compression yet. */
303         Cursor cursor = db.openCursor(null, null);
304         status = cursor.getSearchKey(entry1, dataFound, null);
305         assertEquals(OperationStatus.SUCCESS, status);
306         status = cursor.delete();
307         assertEquals(OperationStatus.SUCCESS, status);
308         env.compress();
309         checkBinEntriesAndCursors(bin, 2, 1);
310         checkBinEntriesAndCursors(dbin, 2, 0);
311
312         /* Move cursor to 1st dup, cursor moves to DBIN, no compresion yet. */
313         status = cursor.getFirst(keyFound, dataFound, null);
314         assertEquals(OperationStatus.SUCCESS, status);
315         env.compress();
316         checkBinEntriesAndCursors(bin, 2, 1);
317         checkBinEntriesAndCursors(dbin, 2, 1);
318
319         /* Delete the duplicates for key 0, no compression yet. */
320         status = cursor.delete();
321         assertEquals(OperationStatus.SUCCESS, status);
322         status = cursor.getNext(keyFound, dataFound, null);
323         assertEquals(OperationStatus.SUCCESS, status);
324         status = cursor.delete();
325         assertEquals(OperationStatus.SUCCESS, status);
326         env.compress();
327         checkBinEntriesAndCursors(bin, 2, 1);
328         checkBinEntriesAndCursors(dbin, 2, 1);
329
330         /* Closing the cursor without calling compress does not compress. */
331         cursor.close();
332         checkBinEntriesAndCursors(bin, 2, 0);
333         checkBinEntriesAndCursors(dbin, 2, 0);
334
335         /* Finally compress can compress. */
336         env.compress();
337     /*
338      * Do this twice. The test is depending on the iterator in
339      * doCompress() getting the DBINReference first and the BINReference
340      * second. In JRockit, it's the opposite so the compress of the BIN
341      * doesn't do any good on the first time around. So take two
342      * iterations to get the job done.
343      */

344         env.compress();
345
346         checkBinEntriesAndCursors(bin, 0, 0);
347         checkBinEntriesAndCursors(dbin, 0, 0);
348
349         /* BIN is empty so parent entry should be gone also. */
350         assertEquals(1, in.getNEntries());
351
352         closeEnv();
353     }
354
355     public void testAbortInsert()
356         throws DatabaseException {
357
358         /* Transactional no-dups, 2 keys. */
359         openAndInit(true, false);
360      
361         /* Add key 2, cursor appears on BIN. */
362         Transaction txn = env.beginTransaction(null, null);
363         Cursor cursor = db.openCursor(txn, null);
364         cursor.put(entry2, entry0);
365         checkBinEntriesAndCursors(bin, 3, 1);
366
367         /* Closing the cursor without abort does not compress. */
368         cursor.close();
369         env.compress();
370         checkBinEntriesAndCursors(bin, 3, 0);
371
372         /* Abort without calling compress does not compress. */
373         txn.abort();
374         checkBinEntriesAndCursors(bin, 3, 0);
375
376         /* Finally compress can compress. */
377         env.compress();
378         checkBinEntriesAndCursors(bin, 2, 0);
379
380         /* Should be no change in parent nodes. */
381         assertEquals(2, in.getNEntries());
382
383         closeEnv();
384     }
385
386     public void testAbortInsertDuplicate()
387         throws DatabaseException {
388
389         /* Transactional dups, 2 keys and 2 dups for 1st key. */
390         openAndInit(true, true);
391
392         /* Add datum 2 for key 0, cursor appears on DBIN. */
393         Transaction txn = env.beginTransaction(null, null);
394         Cursor cursor = db.openCursor(txn, null);
395         cursor.put(entry0, entry2);
396         checkBinEntriesAndCursors(bin, 2, 1);
397         checkBinEntriesAndCursors(dbin, 3, 1);
398
399         /* Closing the cursor without abort does not compress. */
400         cursor.close();
401         env.compress();
402         checkBinEntriesAndCursors(bin, 2, 0);
403         checkBinEntriesAndCursors(dbin, 3, 0);
404
405         /* Abort without calling compress does not compress. */
406         txn.abort();
407         checkBinEntriesAndCursors(bin, 2, 0);
408         checkBinEntriesAndCursors(dbin, 3, 0);
409
410         /* Finally compress can compress. */
411         env.compress();
412         checkBinEntriesAndCursors(bin, 2, 0);
413         checkBinEntriesAndCursors(dbin, 2, 0);
414
415         /* Should be no change in parent nodes. */
416         assertEquals(2, in.getNEntries());
417
418         closeEnv();
419     }
420
421     public void testRollBackInsert()
422         throws DatabaseException {
423
424         /* Transactional no-dups, 2 keys. */
425         openAndInit(true, false);
426
427         /* Add key 2, cursor appears on BIN. */
428         Transaction txn = env.beginTransaction(null, null);
429         Cursor cursor = db.openCursor(txn, null);
430         cursor.put(entry2, entry0);
431         checkBinEntriesAndCursors(bin, 3, 1);
432
433         /* Closing the cursor without abort does not compress. */
434         cursor.close();
435         env.compress();
436         checkBinEntriesAndCursors(bin, 3, 0);
437
438         /* Checkpoint to preserve internal nodes through recovery. */
439         CheckpointConfig config = new CheckpointConfig();
440         config.setForce(true);
441         env.checkpoint(config);
442
443         /* Abort without calling compress does not compress. */
444         txn.abort();
445         checkBinEntriesAndCursors(bin, 3, 0);
446
447         /*
448          * Shutdown and reopen to run recovery. This will call a checkpoint,
449          * but it doesn't compress because the child is not resident.
450          */

451         db.close();
452         DbInternal.envGetEnvironmentImpl(env).close(false);
453         env = null;
454         openEnv(true, false, null);
455         initInternalNodes();
456         checkBinEntriesAndCursors(bin, 3, 0);
457
458         /* Should be no change in parent nodes. */
459         assertEquals(2, in.getNEntries());
460
461         /* Finally compress can compress. */
462         env.compress();
463         checkBinEntriesAndCursors(bin, 2, 0);
464
465         closeEnv();
466     }
467
468     public void testRollBackInsertDuplicate()
469         throws DatabaseException {
470
471         /* Transactional dups, 2 keys and 2 dups for 1st key. */
472         openAndInit(true, true);
473
474         /* Add datum 2 for key 0, cursor appears on DBIN. */
475         Transaction txn = env.beginTransaction(null, null);
476         Cursor cursor = db.openCursor(txn, null);
477         cursor.put(entry0, entry2);
478         checkBinEntriesAndCursors(bin, 2, 1);
479         checkBinEntriesAndCursors(dbin, 3, 1);
480
481         /* Closing the cursor without abort does not compress. */
482         cursor.close();
483         env.compress();
484         checkBinEntriesAndCursors(bin, 2, 0);
485         checkBinEntriesAndCursors(dbin, 3, 0);
486
487         /* Checkpoint to preserve internal nodes through recovery. */
488         CheckpointConfig config = new CheckpointConfig();
489         config.setForce(true);
490         env.checkpoint(config);
491
492         /* Abort without calling compress does not compress. */
493         txn.abort();
494         checkBinEntriesAndCursors(bin, 2, 0);
495         checkBinEntriesAndCursors(dbin, 3, 0);
496
497         /*
498          * Shutdown and reopen to run recovery. This will call a checkpoint,
499          * but it doesn't compress because the child is not resident.
500          */

501         db.close();
502         DbInternal.envGetEnvironmentImpl(env).close(false);
503         env = null;
504         openEnv(true, true, null);
505         initInternalNodes();
506         checkBinEntriesAndCursors(bin, 2, 0);
507         checkBinEntriesAndCursors(dbin, 3, 0);
508
509         /* Finally compress can compress. */
510         env.compress();
511         checkBinEntriesAndCursors(bin, 2, 0);
512         checkBinEntriesAndCursors(dbin, 2, 0);
513
514         /* Should be no change in parent nodes. */
515         assertEquals(2, in.getNEntries());
516
517         closeEnv();
518     }
519
520     public void testRollForwardDelete()
521         throws DatabaseException {
522
523         /* Non-transactional no-dups, 2 keys. */
524         openAndInit(false, false);
525         OperationStatus status;
526
527         /* Checkpoint to preserve internal nodes through recovery. */
528         CheckpointConfig config = new CheckpointConfig();
529         config.setForce(true);
530         env.checkpoint(config);
531
532         /* Cursor appears on BIN. */
533         Cursor cursor = db.openCursor(null, null);
534         status = cursor.getFirst(keyFound, dataFound, null);
535         assertEquals(OperationStatus.SUCCESS, status);
536         checkBinEntriesAndCursors(bin, 2, 1);
537
538         /* Delete without closing the cursor does not compress. */
539         status = cursor.delete();
540         assertEquals(OperationStatus.SUCCESS, status);
541         env.compress();
542         checkBinEntriesAndCursors(bin, 2, 1);
543
544         /* Closing the cursor without calling compress does not compress. */
545         cursor.close();
546         checkBinEntriesAndCursors(bin, 2, 0);
547
548         /*
549          * Shutdown and reopen to run recovery. This will call a checkpoint,
550          * but it doesn't compress because the child is not resident.
551          */

552         db.close();
553         DbInternal.envGetEnvironmentImpl(env).close(false);
554         openEnv(false, false, null);
555         initInternalNodes();
556         checkBinEntriesAndCursors(bin, 2, 0);
557
558         /* Finally compress can compress. */
559         env.compress();
560         checkBinEntriesAndCursors(bin, 1, 0);
561
562         /* Should be no change in parent nodes. */
563         assertEquals(2, in.getNEntries());
564
565         closeEnv();
566     }
567
568     public void testRollForwardDeleteDuplicate()
569         throws DatabaseException {
570
571         /* Non-transactional dups, 2 keys and 2 dups for 1st key. */
572         openAndInit(false, true);
573         OperationStatus status;
574
575         /* Checkpoint to preserve internal nodes through recovery. */
576         CheckpointConfig config = new CheckpointConfig();
577         config.setForce(true);
578         env.checkpoint(config);
579
580         /* Cursor appears on DBIN. */
581         Cursor cursor = db.openCursor(null, null);
582         status = cursor.getFirst(keyFound, dataFound, null);
583         assertEquals(OperationStatus.SUCCESS, status);
584         checkBinEntriesAndCursors(dbin, 2, 1);
585
586         /* Delete without closing the cursor does not compress. */
587         status = cursor.delete();
588         assertEquals(OperationStatus.SUCCESS, status);
589         env.compress();
590         checkBinEntriesAndCursors(dbin, 2, 1);
591
592         /* Closing the cursor without calling compress does not compress. */
593         cursor.close();
594         checkBinEntriesAndCursors(dbin, 2, 0);
595
596         /*
597          * Shutdown and reopen to run recovery. This will call a checkpoint,
598          * but it doesn't compress because the child is not resident.
599          */

600         db.close();
601         DbInternal.envGetEnvironmentImpl(env).close(false);
602         openEnv(false, true, null);
603         initInternalNodes();
604         checkBinEntriesAndCursors(dbin, 2, 0);
605
606         /* Finally compress can compress. */
607         env.compress();
608         checkBinEntriesAndCursors(dbin, 1, 0);
609
610         /* Should be no change in parent nodes. */
611         assertEquals(2, in.getNEntries());
612         checkBinEntriesAndCursors(bin, 2, 0);
613
614         closeEnv();
615     }
616
617     /**
618      * Test that we can handle cases where lazy compression runs first, but the
619      * daemon handles pruning. Testing against BINs.
620      */

621     public void testLazyPruning()
622         throws DatabaseException {
623
624         /* Non-transactional no-dups, 2 keys. */
625         openAndInit(false, false);
626
627         deleteAndLazyCompress(false);
628
629         /* Now compress, empty BIN should disappear. */
630         env.compress();
631         checkINCompQueueSize(0);
632         assertEquals(1, in.getNEntries());
633
634         closeEnv();
635     }
636
637     /**
638      * Test that we can handle cases where lazy compression runs first, but the
639      * daemon handles pruning. Testing against DBINs. [#11778]
640      */

641     public void testLazyPruningDups()
642         throws DatabaseException {
643
644         /* Non-transactional no-dups, 2 keys. */
645         openAndInit(false, true);
646
647         deleteAndLazyCompress(true);
648
649         /* Now compress, empty DBIN should disappear. */
650         env.compress();
651     /* Compress again. Empty BIN should disappear. */
652     env.compress();
653         checkINCompQueueSize(0);
654         assertEquals(1, in.getNEntries());
655
656         closeEnv();
657     }
658
659     /**
660      * Scan over an empty DBIN. [#11778]
661      */

662     public void testEmptyInitialDBINScan()
663         throws DatabaseException {
664
665         /* Non-transactional no-dups, 2 keys. */
666         openAndInit(false, true);
667
668         deleteAndLazyCompress(true);
669
670     /*
671      * Have IN with two entries, first entry is BIN with 1 entry. That
672      * entry is DIN with 1 entry. That entry is a DBIN with 0 entries.
673      * Position the cursor at the first entry so that we move over that
674      * zero-entry DBIN.
675      */

676         Cursor cursor = db.openCursor(null, null);
677         OperationStatus status = cursor.getFirst(keyFound, dataFound, null);
678         assertEquals(OperationStatus.SUCCESS, status);
679     assertTrue(keyFound.getData()[0] == 64);
680     cursor.close();
681         closeEnv();
682     }
683
684     /**
685      * Scan over an empty BIN. This looks very similar to
686      * com.sleepycat.je.test.SR11297Test. [#11778]
687      */

688     public void testEmptyInitialBINScan()
689         throws DatabaseException {
690
691         /* Non-transactional no-dups, 2 keys. */
692         openAndInit(false, false);
693
694         deleteAndLazyCompress(false);
695
696     /*
697      * Have IN with two entries, first entry is BIN with 0 entries.
698      * Position the cursor at the first entry so that we move over that
699      * zero-entry BIN.
700      */

701         Cursor cursor = db.openCursor(null, null);
702         OperationStatus status = cursor.getFirst(keyFound, dataFound, null);
703         assertEquals(OperationStatus.SUCCESS, status);
704     assertTrue(keyFound.getData()[0] == 64);
705     cursor.close();
706         closeEnv();
707     }
708
709     /**
710      * Test that we can handle cases where lazy compression runs first, but the
711      * daemon handles pruning.
712      */

713     public void testNodeNotEmpty()
714         throws DatabaseException {
715
716         /* Non-transactional no-dups, 2 keys. */
717         openAndInit(false, false);
718
719         deleteAndLazyCompress(false);
720
721         /*
722          * We now have an entry on the compressor queue, but let's re-insert a
723          * value to make pruning hit the NodeNotEmptyException case.
724          */

725         assertEquals(OperationStatus.SUCCESS, db.put(null, entry0, entry0));
726         checkBinEntriesAndCursors(bin, 1, 0);
727
728         env.compress();
729         assertEquals(2, in.getNEntries());
730         checkINCompQueueSize(0);
731
732         closeEnv();
733     }
734
735     /* Todo: Check cursor movement across an empty bin. */
736
737     /* Delete all records from the first bin and invoke lazy compression. */
738     private void deleteAndLazyCompress(boolean doDups)
739         throws DatabaseException {
740
741         /* Position the cursor at the first BIN and delete both keys. */
742         Cursor cursor = db.openCursor(null, null);
743         OperationStatus status = cursor.getFirst(keyFound, dataFound, null);
744         assertEquals(OperationStatus.SUCCESS, status);
745         checkBinEntriesAndCursors(bin, 2, 1);
746
747         status = cursor.delete();
748         assertEquals(OperationStatus.SUCCESS, status);
749         status = cursor.getNext(keyFound, dataFound, null);
750         assertEquals(OperationStatus.SUCCESS, status);
751     status = cursor.delete();
752     assertEquals(OperationStatus.SUCCESS, status);
753     if (doDups) {
754         status = cursor.getNext(keyFound, dataFound, null);
755         assertEquals(OperationStatus.SUCCESS, status);
756         status = cursor.delete();
757         assertEquals(OperationStatus.SUCCESS, status);
758     }
759         cursor.close();
760
761         /*
762      * Do lazy compression, leaving behind an empty BIN (and DBIN if dups.)
763      */

764         checkINCompQueueSize(doDups ? 2 : 1);
765         CheckpointConfig config = new CheckpointConfig();
766         config.setForce(true);
767         env.checkpoint(config);
768         checkBinEntriesAndCursors((doDups ? dbin : bin), 0, 0);
769
770         /* BIN is empty but tree pruning hasn't happened. */
771         assertEquals(2, in.getNEntries());
772         checkINCompQueueSize(1);
773     }
774
775     /**
776      * Checks for expected entry and cursor counts on the given BIN or DBIN.
777      */

778     private void checkBinEntriesAndCursors(BIN checkBin,
779                                            int nEntries,
780                                            int nCursors)
781         throws DatabaseException {
782
783         assertEquals("nEntries", nEntries, checkBin.getNEntries());
784         assertEquals("nCursors", nCursors, checkBin.nCursors());
785     }
786
787     /**
788      * Check expected size of the INCompressor queue.
789      */

790     private void checkINCompQueueSize(int expected)
791         throws DatabaseException {
792
793         assertEquals(expected,
794            DbInternal.envGetEnvironmentImpl(env).getINCompressorQueueSize());
795     }
796
797     /**
798      * Opens the environment and db and writes 2 records (3 if dups are used).
799      *
800      * <p>Without dups: {0,0}, {1,0}. This gives two LNs in the main tree.</p>
801      *
802      * <p>With dups: {0,0}, {0,1}, {1,0}. This gives one LN in the main tree,
803      * and a dup tree with two LNs.</p>
804      */

805     private void openAndInit(boolean transactional, boolean dups)
806         throws DatabaseException {
807
808         openEnv(transactional, dups, null);
809
810         /*
811          * We need at least 2 BINs, otherwise empty BINs won't be deleted. So
812          * we add keys until the BIN splits, then delete everything in the
813          * first BIN except the first two keys. Those are the keys we'll use
814          * for testing, and are key values 0 and 1.
815          */

816         BIN firstBin = null;
817         OperationStatus status;
818         for (int i = 0;; i += 1) {
819             DatabaseEntry key = new DatabaseEntry(new byte[] { (byte) i });
820             status = db.put(null, key, entry0);
821             assertEquals(OperationStatus.SUCCESS, status);
822             Cursor cursor = db.openCursor(null, null);
823             status = cursor.getLast(keyFound, dataFound, null);
824             assertEquals(OperationStatus.SUCCESS, status);
825             BIN b = DbInternal.getCursorImpl(cursor).getBIN();
826             cursor.close();
827             if (firstBin == null) {
828                 firstBin = b;
829             } else if (firstBin != b) {
830                 /* Now delete all but the first two keys in the first BIN. */
831                 while (firstBin.getNEntries() > 2) {
832                     cursor = db.openCursor(null, null);
833                     keyFound.setData(entry2.getData());
834                     status =
835             cursor.getSearchKeyRange(keyFound, dataFound, null);
836                     assertEquals(OperationStatus.SUCCESS, status);
837                     cursor.close();
838                     status = db.delete(null, keyFound);
839                     assertEquals(OperationStatus.SUCCESS, status);
840                     env.compress();
841                 }
842                 break;
843             }
844         }
845
846         /* Write dup records. */
847         if (dups) {
848             status = db.put(null, entry0, entry1);
849             assertEquals(OperationStatus.SUCCESS, status);
850         }
851
852         /* Set in, bin, dbin. */
853         initInternalNodes();
854         assertSame(bin, firstBin);
855
856         /* Check that all tree nodes are populated. */
857         assertEquals(2, in.getNEntries());
858         checkBinEntriesAndCursors(bin, 2, 0);
859         if (dups) {
860             checkBinEntriesAndCursors(dbin, 2, 0);
861         } else {
862             assertNull(dbin);
863         }
864     }
865
866     /**
867      * Initialize in, bin, dbin.
868      */

869     private void initInternalNodes()
870         throws DatabaseException {
871
872         /* Find the BIN/DBIN. */
873         Cursor cursor = db.openCursor(null, null);
874         OperationStatus status = cursor.getFirst(keyFound, dataFound, null);
875         assertEquals(OperationStatus.SUCCESS, status);
876         bin = DbInternal.getCursorImpl(cursor).getBIN();
877         dbin = DbInternal.getCursorImpl(cursor).getDupBIN();
878         cursor.close();
879
880         /* Find the IN parent of the BIN. */
881         bin.latch();
882         in = DbInternal.dbGetDatabaseImpl(db)
883                        .getTree()
884                        .getParentINForChildIN(bin, true, true)
885                        .parent;
886         assertNotNull(in);
887         in.releaseLatch();
888     }
889
890     /**
891      * Opens the environment and db.
892      */

893     private void openEnv(boolean transactional, boolean dups, String JavaDoc nodeMax)
894         throws DatabaseException {
895
896         hasDups = dups;
897
898         EnvironmentConfig envConfig = TestUtils.initEnvConfig();
899         envConfig.setTransactional(transactional);
900         envConfig.setConfigParam
901             (EnvironmentParams.ENV_RUN_INCOMPRESSOR.getName(), "false");
902     if (nodeMax != null) {
903         envConfig.setConfigParam
904         (EnvironmentParams.NODE_MAX.getName(), nodeMax);
905         envConfig.setConfigParam
906         (EnvironmentParams.NODE_MAX_DUPTREE.getName(), nodeMax);
907     }
908         envConfig.setAllowCreate(true);
909         env = new Environment(envHome, envConfig);
910
911         /* Make a db and open it. */
912         DatabaseConfig dbConfig = new DatabaseConfig();
913         dbConfig.setTransactional(transactional);
914         dbConfig.setSortedDuplicates(dups);
915         dbConfig.setAllowCreate(true);
916         db = env.openDatabase(null, "testDB", dbConfig);
917     }
918
919     /**
920      * Closes the db and environment.
921      */

922     private void closeEnv()
923         throws DatabaseException {
924
925         db.close();
926         db = null;
927         env.close();
928         env = null;
929     }
930 }
931
Popular Tags