KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > test > PhantomTest


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

8
9 package com.sleepycat.je.test;
10
11 import java.io.File JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.util.Enumeration JavaDoc;
14
15 import junit.framework.Test;
16 import junit.framework.TestCase;
17 import junit.framework.TestSuite;
18
19 import com.sleepycat.bind.tuple.IntegerBinding;
20 import com.sleepycat.je.Cursor;
21 import com.sleepycat.je.CursorConfig;
22 import com.sleepycat.je.Database;
23 import com.sleepycat.je.DatabaseConfig;
24 import com.sleepycat.je.DatabaseEntry;
25 import com.sleepycat.je.DatabaseException;
26 import com.sleepycat.je.DeadlockException;
27 import com.sleepycat.je.Environment;
28 import com.sleepycat.je.EnvironmentConfig;
29 import com.sleepycat.je.LockMode;
30 import com.sleepycat.je.LockStats;
31 import com.sleepycat.je.OperationStatus;
32 import com.sleepycat.je.StatsConfig;
33 import com.sleepycat.je.Transaction;
34 import com.sleepycat.je.TransactionConfig;
35 import com.sleepycat.je.config.EnvironmentParams;
36 import com.sleepycat.je.junit.JUnitThread;
37 import com.sleepycat.je.log.FileManager;
38 import com.sleepycat.je.util.TestUtils;
39
40 /**
41  * Tests phantom prevention (range locking) added in SR [#10477].
42  *
43  * <p>We test that with a serializable txn, range locking will prevent phantoms
44  * from appearing. We also test that phantoms *do* appear for non-serializable
45  * isolation levels. These include read-uncommitted, read-committed and
46  * repeatable-read now.</p>
47  *
48  * <p>Test method names have the suffix _Sucess or _NotFound depending on
49  * whether they're testing a read operation with a SUCCESS or NOTFOUND outcome.
50  * If they're testing duplicates, the _Dup suffix is also added. Finally, a
51  * suffix is added for the isolation level at run time.</p>
52  *
53  * <p>All tests are for the case where the reader txn locks a range and then
54  * the writer txn tries to insert into the locked range. The reverse (where
55  * the writer inserts first) works without range locking because the reader
56  * will block on the inserted key, so we don't test that here.</p>
57  *
58  * <p>We test all read operations with and without duplicates (with duplicates
59  * the test name has _Dup appended) except for the following cases which are
60  * meaningless without duplicates because get{Next,Prev}Dup always return
61  * NOTFOUND when duplicates are not configured:
62  * testGetNextDup_Success, testGetNextDup_NotFound,
63  * testGetPrevDup_Success, testGetPrevDup_NotFound.</p>
64  */

65 public class PhantomTest extends TestCase {
66
67     private static final TransactionConfig READ_UNCOMMITTED_CONFIG
68                                            = new TransactionConfig();
69     private static final TransactionConfig READ_COMMITTED_CONFIG
70                                            = new TransactionConfig();
71     private static final TransactionConfig REPEATABLE_READ_CONFIG
72                                            = new TransactionConfig();
73     private static final TransactionConfig SERIALIZABLE_CONFIG
74                                            = new TransactionConfig();
75     static {
76         READ_UNCOMMITTED_CONFIG.setReadUncommitted(true);
77         READ_COMMITTED_CONFIG.setReadCommitted(true);
78         SERIALIZABLE_CONFIG.setSerializableIsolation(true);
79     }
80     private static final TransactionConfig[] TXN_CONFIGS = {
81         READ_UNCOMMITTED_CONFIG,
82         READ_COMMITTED_CONFIG,
83         REPEATABLE_READ_CONFIG,
84         SERIALIZABLE_CONFIG,
85     };
86
87     private static final String JavaDoc DB_NAME = "PhantomTest";
88
89     public static Test suite() {
90         TestSuite all = new TestSuite();
91         for (int i = 0; i < TXN_CONFIGS.length; i += 1) {
92             TestSuite suite = new TestSuite(PhantomTest.class);
93             Enumeration JavaDoc e = suite.tests();
94             while (e.hasMoreElements()) {
95                 PhantomTest test = (PhantomTest) e.nextElement();
96                 test.init(TXN_CONFIGS[i]);
97                 all.addTest(test);
98             }
99         }
100         return all;
101     }
102
103     private static final int MAX_INSERT_MILLIS = 5000;
104
105     private File JavaDoc envHome;
106     private Environment env;
107     private Database db;
108     private TransactionConfig txnConfig;
109     private JUnitThread writerThread;
110     private boolean txnSerializable;
111     private boolean dups;
112     private boolean insertFinished;
113
114     public PhantomTest() {
115         envHome = new File JavaDoc(System.getProperty(TestUtils.DEST_DIR));
116     }
117
118     private void init(TransactionConfig txnConfig) {
119         this.txnConfig = txnConfig;
120         txnSerializable = (txnConfig == SERIALIZABLE_CONFIG);
121     }
122
123     public void setUp()
124         throws IOException JavaDoc {
125
126         TestUtils.removeLogFiles("Setup", envHome, false);
127         TestUtils.removeFiles("Setup", envHome, FileManager.DEL_SUFFIX);
128     }
129     
130     public void tearDown()
131         throws Exception JavaDoc {
132
133         String JavaDoc txnType;
134         if (txnConfig == SERIALIZABLE_CONFIG) {
135             txnType = "-Serializable";
136         } else if (txnConfig == REPEATABLE_READ_CONFIG) {
137             txnType = "-RepeatableRead";
138         } else if (txnConfig == READ_COMMITTED_CONFIG) {
139             txnType = "-ReadCommitted";
140         } else if (txnConfig == READ_UNCOMMITTED_CONFIG) {
141             txnType = "-ReadUncommitted";
142         } else {
143             throw new IllegalStateException JavaDoc();
144         }
145         setName(getName() + txnType);
146
147         try {
148             if (env != null) {
149                 env.close();
150             }
151         } catch (Throwable JavaDoc e) {
152             System.out.println("tearDown: " + e);
153         }
154                 
155         try {
156             //*
157
TestUtils.removeLogFiles("tearDown", envHome, true);
158             TestUtils.removeFiles("tearDown", envHome, FileManager.DEL_SUFFIX);
159             //*/
160
} catch (Throwable JavaDoc e) {
161             System.out.println("tearDown: " + e);
162         }
163
164         envHome = null;
165         env = null;
166         db = null;
167
168         if (writerThread != null) {
169             while (writerThread.isAlive()) {
170                 writerThread.interrupt();
171                 Thread.yield();
172             }
173             writerThread = null;
174         }
175     }
176
177     /**
178      * Opens the environment and database.
179      */

180     private void openEnv(boolean dups)
181         throws DatabaseException {
182
183         openEnv(dups, null);
184     }
185
186     /**
187      * Opens the environment and database.
188      */

189     private void openEnv(boolean dups, EnvironmentConfig envConfig)
190         throws DatabaseException {
191
192         this.dups = dups;
193         if (envConfig == null) {
194             envConfig = TestUtils.initEnvConfig();
195             /* Control over isolation level is required by this test. */
196             TestUtils.clearIsolationLevel(envConfig);
197         }
198
199         /* Disable the daemons so the don't interfere with stats. */
200         envConfig.setConfigParam
201             (EnvironmentParams.ENV_RUN_EVICTOR.getName(), "false");
202         envConfig.setConfigParam
203             (EnvironmentParams.ENV_RUN_CLEANER.getName(), "false");
204         envConfig.setConfigParam
205             (EnvironmentParams.ENV_RUN_CHECKPOINTER.getName(), "false");
206         envConfig.setConfigParam
207             (EnvironmentParams.ENV_RUN_INCOMPRESSOR.getName(), "false");
208
209         envConfig.setAllowCreate(true);
210         envConfig.setTransactional(true);
211         env = new Environment(envHome, envConfig);
212
213         DatabaseConfig dbConfig = new DatabaseConfig();
214         dbConfig.setAllowCreate(true);
215         dbConfig.setTransactional(true);
216         dbConfig.setSortedDuplicates(dups);
217         db = env.openDatabase(null, DB_NAME, dbConfig);
218     }
219
220     /**
221      * Closes the environment and database.
222      */

223     private void closeEnv()
224         throws DatabaseException {
225
226         if (db != null) {
227             db.close();
228             db = null;
229         }
230         if (env != null) {
231             env.close();
232             env = null;
233         }
234     }
235     
236     public void testGetSearchKey_Success()
237         throws DatabaseException, InterruptedException JavaDoc {
238
239         openEnv(false);
240
241         /* Insert key 2. */
242         insert(2);
243
244         /* getSearchKey returns key 2. */
245         Transaction readerTxn = env.beginTransaction(null, txnConfig);
246         Cursor cursor = db.openCursor(readerTxn, null);
247         assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2));
248
249         /* Insertions are never blocked. */
250         try {
251             insert(1);
252         } catch (DeadlockException e) {
253             fail();
254         }
255
256         cursor.close();
257         readerTxn.commitNoSync();
258         closeEnv();
259     }
260     
261     public void testGetSearchKey_Success_Dup()
262         throws DatabaseException, InterruptedException JavaDoc {
263
264         openEnv(true);
265
266         /* Insert dups. */
267         insert(1, 2);
268         insert(1, 3);
269
270         /* getSearchKey returns key {1,2}. */
271         Transaction readerTxn = env.beginTransaction(null, txnConfig);
272         Cursor cursor = db.openCursor(readerTxn, null);
273         assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 2));
274
275         /* Insertions are never blocked. */
276         try {
277             insert(1, 1);
278         } catch (DeadlockException e) {
279             fail();
280         }
281
282         cursor.close();
283         readerTxn.commitNoSync();
284         closeEnv();
285     }
286     
287     public void testGetSearchKey_NotFound()
288         throws DatabaseException, InterruptedException JavaDoc {
289
290         openEnv(false);
291
292         /* Insert key 1. */
293         insert(1);
294
295         /* getSearchKey for key 2 returns NOTFOUND. */
296         Transaction readerTxn = env.beginTransaction(null, txnConfig);
297         Cursor cursor = db.openCursor(readerTxn, null);
298         assertEquals(OperationStatus.NOTFOUND, searchKey(cursor, 2));
299
300         /* Insert key 2 in a writer thread. */
301         startInsert(2);
302         
303         /*
304          * If serializable, getSearchKey should return NOTFOUND again;
305          * otherwise getSearchKey should see key 2.
306          */

307         if (txnSerializable) {
308             assertEquals(OperationStatus.NOTFOUND, searchKey(cursor, 2));
309         } else {
310             assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2));
311         }
312
313         /* Close reader to allow writer to finish. */
314         cursor.close();
315         readerTxn.commitNoSync();
316         waitForInsert();
317
318         /* getSearchKey returns key 2. */
319         readerTxn = env.beginTransaction(null, txnConfig);
320         cursor = db.openCursor(readerTxn, null);
321         assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2));
322         cursor.close();
323         readerTxn.commit();
324
325         closeEnv();
326     }
327     
328     public void testGetSearchKey_NotFound_Dup()
329         throws DatabaseException, InterruptedException JavaDoc {
330
331         openEnv(true);
332
333         /* Insert dups. */
334         insert(2, 1);
335         insert(2, 2);
336
337         /* getSearchKey for {1,1} returns NOTFOUND. */
338         Transaction readerTxn = env.beginTransaction(null, txnConfig);
339         Cursor cursor = db.openCursor(readerTxn, null);
340         assertEquals(OperationStatus.NOTFOUND, searchKey(cursor, 1, 1));
341
342         /* Insert {1,1} in a writer thread. */
343         startInsert(1, 1);
344         
345         /*
346          * If serializable, getSearchKey should return NOTFOUND again;
347          * otherwise getSearchKey should see {1,1}.
348          */

349         if (txnSerializable) {
350             assertEquals(OperationStatus.NOTFOUND, searchKey(cursor, 1, 1));
351         } else {
352             assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 1));
353         }
354
355         /* Close reader to allow writer to finish. */
356         cursor.close();
357         readerTxn.commitNoSync();
358         waitForInsert();
359
360         /* getSearchKey returns {1,1}. */
361         readerTxn = env.beginTransaction(null, txnConfig);
362         cursor = db.openCursor(readerTxn, null);
363         assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 1));
364         cursor.close();
365         readerTxn.commit();
366
367         closeEnv();
368     }
369     
370     public void testGetSearchBoth_Success()
371         throws DatabaseException, InterruptedException JavaDoc {
372
373         openEnv(false);
374
375         /* Insert key 2. */
376         insert(2);
377
378         /* getSearchBoth returns {2,0}. */
379         Transaction readerTxn = env.beginTransaction(null, txnConfig);
380         Cursor cursor = db.openCursor(readerTxn, null);
381         assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 0));
382
383         /* Insertions are never blocked. */
384         try {
385             insert(1);
386         } catch (DeadlockException e) {
387             fail();
388         }
389
390         cursor.close();
391         readerTxn.commitNoSync();
392         closeEnv();
393     }
394     
395     public void testGetSearchBoth_Success_Dup()
396         throws DatabaseException, InterruptedException JavaDoc {
397
398         openEnv(true);
399
400         /* Insert dups. */
401         insert(1, 1);
402         insert(1, 3);
403
404         /* getSearchBoth returns key {1,3}. */
405         Transaction readerTxn = env.beginTransaction(null, txnConfig);
406         Cursor cursor = db.openCursor(readerTxn, null);
407         assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 3));
408
409         /* Insertions are never blocked. */
410         try {
411             insert(1, 2);
412         } catch (DeadlockException e) {
413             fail();
414         }
415
416         cursor.close();
417         readerTxn.commitNoSync();
418         closeEnv();
419     }
420     
421     public void testGetSearchBoth_NotFound()
422         throws DatabaseException, InterruptedException JavaDoc {
423
424         openEnv(false);
425
426         /* Insert key 1. */
427         insert(1);
428
429         /* getSearchBoth for key 2 returns NOTFOUND. */
430         Transaction readerTxn = env.beginTransaction(null, txnConfig);
431         Cursor cursor = db.openCursor(readerTxn, null);
432         assertEquals(OperationStatus.NOTFOUND, searchBoth(cursor, 2));
433
434         /* Insert key 2 in a writer thread. */
435         startInsert(2);
436         
437         /*
438          * If serializable, getSearchBoth should return NOTFOUND again;
439          * otherwise getSearchBoth should see key 2.
440          */

441         if (txnSerializable) {
442             assertEquals(OperationStatus.NOTFOUND, searchBoth(cursor, 2));
443         } else {
444             assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2));
445         }
446
447         /* Close reader to allow writer to finish. */
448         cursor.close();
449         readerTxn.commitNoSync();
450         waitForInsert();
451
452         /* getSearchBoth returns key 2. */
453         readerTxn = env.beginTransaction(null, txnConfig);
454         cursor = db.openCursor(readerTxn, null);
455         assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2));
456         cursor.close();
457         readerTxn.commit();
458
459         closeEnv();
460     }
461     
462     public void testGetSearchBoth_NotFound_Dup()
463         throws DatabaseException, InterruptedException JavaDoc {
464
465         openEnv(true);
466
467         /* Insert dups. */
468         insert(1, 1);
469         insert(1, 3);
470
471         /* getSearchBoth for {1,2} returns NOTFOUND. */
472         Transaction readerTxn = env.beginTransaction(null, txnConfig);
473         Cursor cursor = db.openCursor(readerTxn, null);
474         assertEquals(OperationStatus.NOTFOUND, searchBoth(cursor, 1, 2));
475
476         /* Insert {1,2} in a writer thread. */
477         startInsert(1, 2);
478         
479         /*
480          * If serializable, getSearchBoth should return NOTFOUND again;
481          * otherwise getSearchBoth should see {1,2}.
482          */

483         if (txnSerializable) {
484             assertEquals(OperationStatus.NOTFOUND, searchBoth(cursor, 1, 2));
485         } else {
486             assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 2));
487         }
488
489         /* Close reader to allow writer to finish. */
490         cursor.close();
491         readerTxn.commitNoSync();
492         waitForInsert();
493
494         /* getSearchBoth returns {1,2}. */
495         readerTxn = env.beginTransaction(null, txnConfig);
496         cursor = db.openCursor(readerTxn, null);
497         assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 2));
498         cursor.close();
499         readerTxn.commit();
500
501         closeEnv();
502     }
503     
504     public void testGetSearchKeyRange_Success()
505         throws DatabaseException, InterruptedException JavaDoc {
506
507         openEnv(false);
508         DatabaseEntry key = new DatabaseEntry();
509         DatabaseEntry data = new DatabaseEntry();
510         OperationStatus status;
511
512         /* Insert key 1 and 3. */
513         insert(1);
514         insert(3);
515
516         /* getSearchKeyRange for key 2 returns key 3. */
517         Transaction readerTxn = env.beginTransaction(null, txnConfig);
518         Cursor cursor = db.openCursor(readerTxn, null);
519         IntegerBinding.intToEntry(2, key);
520         status = cursor.getSearchKeyRange(key, data, null);
521         assertEquals(OperationStatus.SUCCESS, status);
522         assertEquals(3, IntegerBinding.entryToInt(key));
523
524         /* Insert key 2 in a writer thread. */
525         startInsert(2);
526         
527         /*
528          * If serializable, getSearchKeyRange should return key 3 again;
529          * otherwise getSearchKeyRange should see key 2.
530          */

531         IntegerBinding.intToEntry(2, key);
532         status = cursor.getSearchKeyRange(key, data, null);
533         assertEquals(OperationStatus.SUCCESS, status);
534         if (txnSerializable) {
535             assertEquals(3, IntegerBinding.entryToInt(key));
536         } else {
537             assertEquals(2, IntegerBinding.entryToInt(key));
538         }
539
540         /* Close reader to allow writer to finish. */
541         cursor.close();
542         readerTxn.commitNoSync();
543         waitForInsert();
544
545         /* getSearchKeyRange returns key 2. */
546         readerTxn = env.beginTransaction(null, txnConfig);
547         cursor = db.openCursor(readerTxn, null);
548         IntegerBinding.intToEntry(2, key);
549         status = cursor.getSearchKeyRange(key, data, null);
550         assertEquals(OperationStatus.SUCCESS, status);
551         assertEquals(2, IntegerBinding.entryToInt(key));
552         cursor.close();
553         readerTxn.commit();
554
555         closeEnv();
556     }
557     
558     public void testGetSearchKeyRange_Success_Dup()
559         throws DatabaseException, InterruptedException JavaDoc {
560
561         openEnv(true);
562         DatabaseEntry key = new DatabaseEntry();
563         DatabaseEntry data = new DatabaseEntry();
564         OperationStatus status;
565
566         /* Insert dups. */
567         insert(1, 1);
568         insert(1, 2);
569         insert(3, 2);
570         insert(3, 3);
571
572         /* getSearchKeyRange for key 2 returns {3,2}. */
573         Transaction readerTxn = env.beginTransaction(null, txnConfig);
574         Cursor cursor = db.openCursor(readerTxn, null);
575         IntegerBinding.intToEntry(2, key);
576         status = cursor.getSearchKeyRange(key, data, null);
577         assertEquals(3, IntegerBinding.entryToInt(key));
578         assertEquals(2, IntegerBinding.entryToInt(data));
579         assertEquals(OperationStatus.SUCCESS, status);
580
581         /* Insert {3,1} in a writer thread. */
582         startInsert(3, 1);
583         
584         /*
585          * If serializable, getSearchKeyRange should return {3,2} again;
586          * otherwise getSearchKeyRange should see {3,1}.
587          */

588         IntegerBinding.intToEntry(2, key);
589         status = cursor.getSearchKeyRange(key, data, null);
590         assertEquals(OperationStatus.SUCCESS, status);
591         if (txnSerializable) {
592             assertEquals(3, IntegerBinding.entryToInt(key));
593             assertEquals(2, IntegerBinding.entryToInt(data));
594         } else {
595             assertEquals(3, IntegerBinding.entryToInt(key));
596             assertEquals(1, IntegerBinding.entryToInt(data));
597         }
598
599         /* Close reader to allow writer to finish. */
600         cursor.close();
601         readerTxn.commitNoSync();
602         waitForInsert();
603
604         /* getSearchKeyRange returns {3,1}. */
605         readerTxn = env.beginTransaction(null, txnConfig);
606         cursor = db.openCursor(readerTxn, null);
607         IntegerBinding.intToEntry(2, key);
608         status = cursor.getSearchKeyRange(key, data, null);
609         assertEquals(OperationStatus.SUCCESS, status);
610         assertEquals(3, IntegerBinding.entryToInt(key));
611         assertEquals(1, IntegerBinding.entryToInt(data));
612         cursor.close();
613         readerTxn.commit();
614
615         closeEnv();
616     }
617     
618     public void testGetSearchKeyRange_NotFound()
619         throws DatabaseException, InterruptedException JavaDoc {
620
621         openEnv(false);
622         DatabaseEntry key = new DatabaseEntry();
623         DatabaseEntry data = new DatabaseEntry();
624         OperationStatus status;
625
626         /* Insert key 1. */
627         insert(1);
628
629         /* getSearchKeyRange for key 2 returns NOTFOUND. */
630         Transaction readerTxn = env.beginTransaction(null, txnConfig);
631         Cursor cursor = db.openCursor(readerTxn, null);
632         IntegerBinding.intToEntry(2, key);
633         status = cursor.getSearchKeyRange(key, data, null);
634         assertEquals(OperationStatus.NOTFOUND, status);
635
636         /* Insert key 3 in a writer thread. */
637         startInsert(3);
638         
639         /*
640          * If serializable, getSearchKeyRange should return NOTFOUND again;
641          * otherwise getSearchKeyRange should see key 3.
642          */

643         IntegerBinding.intToEntry(2, key);
644         status = cursor.getSearchKeyRange(key, data, null);
645         if (txnSerializable) {
646             assertEquals(OperationStatus.NOTFOUND, status);
647         } else {
648             assertEquals(OperationStatus.SUCCESS, status);
649             assertEquals(3, IntegerBinding.entryToInt(key));
650         }
651
652         /* Close reader to allow writer to finish. */
653         cursor.close();
654         readerTxn.commitNoSync();
655         waitForInsert();
656
657         /* getSearchKeyRange returns key 3. */
658         readerTxn = env.beginTransaction(null, txnConfig);
659         cursor = db.openCursor(readerTxn, null);
660         IntegerBinding.intToEntry(2, key);
661         status = cursor.getSearchKeyRange(key, data, null);
662         assertEquals(OperationStatus.SUCCESS, status);
663         assertEquals(3, IntegerBinding.entryToInt(key));
664         cursor.close();
665         readerTxn.commit();
666
667         closeEnv();
668     }
669     
670     public void testGetSearchKeyRange_NotFound_Dup()
671         throws DatabaseException, InterruptedException JavaDoc {
672
673         openEnv(true);
674         DatabaseEntry key = new DatabaseEntry();
675         DatabaseEntry data = new DatabaseEntry();
676         OperationStatus status;
677
678         /* Insert dups. */
679         insert(1, 1);
680         insert(1, 2);
681
682         /* getSearchKeyRange for key 2 returns NOTFOUND. */
683         Transaction readerTxn = env.beginTransaction(null, txnConfig);
684         Cursor cursor = db.openCursor(readerTxn, null);
685         IntegerBinding.intToEntry(2, key);
686         status = cursor.getSearchKeyRange(key, data, null);
687         assertEquals(OperationStatus.NOTFOUND, status);
688
689         /* Insert {3,1} in a writer thread. */
690         startInsert(3, 1);
691         
692         /*
693          * If serializable, getSearchKeyRange should return NOTFOUND again;
694          * otherwise getSearchKeyRange should see {3,1}.
695          */

696         IntegerBinding.intToEntry(2, key);
697         status = cursor.getSearchKeyRange(key, data, null);
698         if (txnSerializable) {
699             assertEquals(OperationStatus.NOTFOUND, status);
700         } else {
701             assertEquals(OperationStatus.SUCCESS, status);
702             assertEquals(3, IntegerBinding.entryToInt(key));
703             assertEquals(1, IntegerBinding.entryToInt(data));
704         }
705
706         /* Close reader to allow writer to finish. */
707         cursor.close();
708         readerTxn.commitNoSync();
709         waitForInsert();
710
711         /* getSearchKeyRange returns {3,1}. */
712         readerTxn = env.beginTransaction(null, txnConfig);
713         cursor = db.openCursor(readerTxn, null);
714         IntegerBinding.intToEntry(2, key);
715         status = cursor.getSearchKeyRange(key, data, null);
716         assertEquals(OperationStatus.SUCCESS, status);
717         assertEquals(3, IntegerBinding.entryToInt(key));
718         assertEquals(1, IntegerBinding.entryToInt(data));
719         cursor.close();
720         readerTxn.commit();
721
722         closeEnv();
723     }
724     
725     /*
726      * A testGetSearchBothRange_Success test case is not possible because it is
727      * not possible to insert a duplicate when only one LN for the key already
728      * exists, without locking the existing LN. Therefore, the insert thread
729      * will deadlock with the reader thread, which has the existing LN locked.
730      * This is a testing anomoly, not a bug.
731      */

732     
733     public void testGetSearchBothRange_Success_Dup()
734         throws DatabaseException, InterruptedException JavaDoc {
735
736         openEnv(true);
737         DatabaseEntry key = new DatabaseEntry();
738         DatabaseEntry data = new DatabaseEntry();
739         OperationStatus status;
740
741         /* Insert dups. */
742         insert(1, 1);
743         insert(1, 2);
744         insert(3, 2);
745         insert(3, 3);
746
747         /* getSearchBothRange for {3, 0} returns {3,2}. */
748         Transaction readerTxn = env.beginTransaction(null, txnConfig);
749         Cursor cursor = db.openCursor(readerTxn, null);
750         IntegerBinding.intToEntry(3, key);
751         IntegerBinding.intToEntry(0, data);
752         status = cursor.getSearchBothRange(key, data, null);
753         assertEquals(OperationStatus.SUCCESS, status);
754         assertEquals(3, IntegerBinding.entryToInt(key));
755         assertEquals(2, IntegerBinding.entryToInt(data));
756
757         /* Insert {3,1} in a writer thread. */
758         startInsert(3, 1);
759         
760         /*
761          * If serializable, getSearchBothRange should return {3,2} again;
762          * otherwise getSearchBothRange should see {3,1}.
763          */

764         IntegerBinding.intToEntry(3, key);
765         IntegerBinding.intToEntry(0, data);
766         status = cursor.getSearchBothRange(key, data, null);
767         assertEquals(OperationStatus.SUCCESS, status);
768         if (txnSerializable) {
769             assertEquals(3, IntegerBinding.entryToInt(key));
770             assertEquals(2, IntegerBinding.entryToInt(data));
771         } else {
772             assertEquals(3, IntegerBinding.entryToInt(key));
773             assertEquals(1, IntegerBinding.entryToInt(data));
774         }
775
776         /* Close reader to allow writer to finish. */
777         cursor.close();
778         readerTxn.commitNoSync();
779         waitForInsert();
780
781         /* getSearchBothRange returns {3,1}. */
782         readerTxn = env.beginTransaction(null, txnConfig);
783         cursor = db.openCursor(readerTxn, null);
784         IntegerBinding.intToEntry(3, key);
785         IntegerBinding.intToEntry(0, data);
786         status = cursor.getSearchBothRange(key, data, null);
787         assertEquals(OperationStatus.SUCCESS, status);
788         assertEquals(3, IntegerBinding.entryToInt(key));
789         assertEquals(1, IntegerBinding.entryToInt(data));
790         cursor.close();
791         readerTxn.commit();
792
793         closeEnv();
794     }
795     
796     public void testGetSearchBothRange_NotFound()
797         throws DatabaseException, InterruptedException JavaDoc {
798
799         openEnv(false);
800         DatabaseEntry key = new DatabaseEntry();
801         DatabaseEntry data = new DatabaseEntry();
802         OperationStatus status;
803
804         /* Insert key 1. */
805         insert(1);
806
807         /* getSearchBothRange for {3, 0} returns NOTFOUND. */
808         Transaction readerTxn = env.beginTransaction(null, txnConfig);
809         Cursor cursor = db.openCursor(readerTxn, null);
810         IntegerBinding.intToEntry(3, key);
811         IntegerBinding.intToEntry(0, data);
812         status = cursor.getSearchBothRange(key, data, null);
813         assertEquals(OperationStatus.NOTFOUND, status);
814
815         /* Insert {3, 1} in a writer thread. */
816         startInsert(3, 1);
817         
818         /*
819          * If serializable, getSearchBothRange should return NOTFOUND again;
820          * otherwise getSearchBothRange should see key 3.
821          */

822         IntegerBinding.intToEntry(3, key);
823         IntegerBinding.intToEntry(0, data);
824         status = cursor.getSearchBothRange(key, data, null);
825         if (txnSerializable) {
826             assertEquals(OperationStatus.NOTFOUND, status);
827         } else {
828             assertEquals(OperationStatus.SUCCESS, status);
829             assertEquals(3, IntegerBinding.entryToInt(key));
830             assertEquals(1, IntegerBinding.entryToInt(data));
831         }
832
833         /* Close reader to allow writer to finish. */
834         cursor.close();
835         readerTxn.commitNoSync();
836         waitForInsert();
837
838         /* getSearchBothRange returns key 3. */
839         readerTxn = env.beginTransaction(null, txnConfig);
840         cursor = db.openCursor(readerTxn, null);
841         IntegerBinding.intToEntry(3, key);
842         IntegerBinding.intToEntry(0, data);
843         status = cursor.getSearchBothRange(key, data, null);
844         assertEquals(OperationStatus.SUCCESS, status);
845         assertEquals(3, IntegerBinding.entryToInt(key));
846         assertEquals(1, IntegerBinding.entryToInt(data));
847         cursor.close();
848         readerTxn.commit();
849
850         closeEnv();
851     }
852     
853     public void testGetSearchBothRange_NotFound_Dup()
854         throws DatabaseException, InterruptedException JavaDoc {
855
856         openEnv(true);
857         DatabaseEntry key = new DatabaseEntry();
858         DatabaseEntry data = new DatabaseEntry();
859         OperationStatus status;
860
861         /* Insert dups. */
862         insert(3, 0);
863         insert(3, 1);
864
865         /* getSearchBothRange for {3, 2} returns NOTFOUND. */
866         Transaction readerTxn = env.beginTransaction(null, txnConfig);
867