KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sleepycat > je > CursorEdgeTest


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

8
9 package com.sleepycat.je;
10
11 import java.io.File JavaDoc;
12
13 import junit.framework.TestCase;
14
15 import com.sleepycat.je.LockNotGrantedException;
16 import com.sleepycat.je.config.EnvironmentParams;
17 import com.sleepycat.je.junit.JUnitThread;
18 import com.sleepycat.je.latch.LatchSupport;
19 import com.sleepycat.je.util.TestUtils;
20
21 /**
22  * Test edge case in cursor traversals. In particular, look at duplicates and
23  * sets of keys interspersed with deletions.
24  */

25 public class CursorEdgeTest extends TestCase {
26
27     private static final boolean DEBUG = false;
28     private Environment env;
29     private File JavaDoc envHome;
30     private boolean operationStarted;
31
32     public CursorEdgeTest() {
33         envHome = new File JavaDoc(System.getProperty(TestUtils.DEST_DIR));
34     }
35
36     public void setUp()
37     throws Exception JavaDoc {
38
39         TestUtils.removeLogFiles("Setup", envHome, false);
40
41         /*
42          * Create an environment w/transactions and a max node size of 6.
43          * Be sure to disable the compressor, we want some holes in the
44          * tree.
45          */

46         EnvironmentConfig envConfig = TestUtils.initEnvConfig();
47         envConfig.setTransactional(true);
48         envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(), "6");
49         envConfig.setConfigParam(EnvironmentParams.ENV_RUN_INCOMPRESSOR.getName(),
50                                  "false");
51         envConfig.setAllowCreate(true);
52         env = new Environment(envHome, envConfig);
53     }
54     
55     public void tearDown()
56     throws Exception JavaDoc {
57         
58         try {
59             env.close();
60         } catch (Throwable JavaDoc e) {
61             System.out.println("Exception during tearDown");
62             e.printStackTrace();
63         }
64     env = null;
65         TestUtils.removeLogFiles("TearDown", envHome, false);
66     }
67
68     /**
69      * Insert a number of duplicates, making sure that the duplicate tree
70      * has multiple bins. Make sure that we can skip over the duplicates and
71      * find the right value.
72      */

73     public void testSearchOnDuplicatesWithDeletions()
74     throws Throwable JavaDoc {
75
76         Database myDb = null;
77         Cursor cursor = null;
78     try {
79             /* Set up a db */
80             DatabaseConfig dbConfig = new DatabaseConfig();
81             dbConfig.setTransactional(true);
82             dbConfig.setSortedDuplicates(true);
83             dbConfig.setAllowCreate(true);
84             myDb = env.openDatabase(null, "foo", dbConfig);
85
86             /*
87              * Insert k1/d1, then a duplicate range of k2/d1 -> k2/d15, then
88              * k3/d1. Now delete the beginning part of the duplicate
89              * range, trying to get more than a whole bin's worth
90              * (k2/d1 -> k2/d7). Because the compressor is not
91              * enabled, there will be a hole in the k2 range. While
92              * we're at it, delete k2/d10 - k2/d13 too, make sure we
93              * can traverse a hole in the middle of the duplicate
94              * range.
95              */

96             DatabaseEntry key = new DatabaseEntry();
97             DatabaseEntry data = new DatabaseEntry();
98             key.setData(TestUtils.getTestArray(1));
99             data.setData(TestUtils.getTestArray(1));
100             myDb.put(null, key, data); // k1/d1
101
key.setData(TestUtils.getTestArray(3));
102             myDb.put(null, key, data); // k3/d1
103

104             /* insert k2 range */
105             key.setData(TestUtils.getTestArray(2));
106             for (int i = 1; i <= 15; i++) {
107                 data.setData(TestUtils.getTestArray(i));
108                 myDb.put(null, key, data);
109             }
110
111             /* Now delete k2/d1 -> k2/d7 */
112             Transaction txn =
113         env.beginTransaction(null, TransactionConfig.DEFAULT);
114             cursor = myDb.openCursor(txn, CursorConfig.DEFAULT);
115             assertEquals(OperationStatus.SUCCESS,
116              cursor.getSearchKey(key, data, LockMode.DEFAULT));
117             for (int i = 0; i < 7; i ++) {
118                 assertEquals(OperationStatus.SUCCESS, cursor.delete());
119                 assertEquals(OperationStatus.SUCCESS,
120                  cursor.getNext(key, data, LockMode.DEFAULT));
121             }
122
123             /* Also delete k2/d10 - k2/d13 */
124             data.setData(TestUtils.getTestArray(10));
125             assertEquals(OperationStatus.SUCCESS,
126              cursor.getSearchBoth(key, data, LockMode.DEFAULT));
127             for (int i = 0; i < 3; i ++) {
128                 assertEquals(OperationStatus.SUCCESS, cursor.delete());
129                 assertEquals(OperationStatus.SUCCESS,
130                  cursor.getNext(key, data, LockMode.DEFAULT));
131             }
132
133             /* Double check what's in the tree */
134             if (DEBUG) {
135                 Cursor checkCursor = myDb.openCursor(txn,
136                              CursorConfig.DEFAULT);
137                 while (checkCursor.getNext(key, data, LockMode.DEFAULT) ==
138                OperationStatus.SUCCESS) {
139                     System.out.println("key=" +
140                                        TestUtils.getTestVal(key.getData()) +
141                                        " data=" +
142                                        TestUtils.getTestVal(data.getData()));
143                 }
144                 checkCursor.close();
145             }
146             cursor.close();
147             cursor = null;
148             txn.commit();
149
150             /*
151              * Now make sure we can find k2/d8
152              */

153             Cursor readCursor = myDb.openCursor(null, CursorConfig.DEFAULT);
154             key.setData(TestUtils.getTestArray(2));
155             
156             /* Use key search */
157             assertEquals(OperationStatus.SUCCESS,
158              readCursor.getSearchKey(key, data, LockMode.DEFAULT));
159             assertEquals(2, TestUtils.getTestVal(key.getData()));
160             assertEquals(8, TestUtils.getTestVal(data.getData()));
161
162             /* Use range search */
163             assertEquals(OperationStatus.SUCCESS,
164              readCursor.getSearchKeyRange(key, data,
165                               LockMode.DEFAULT));
166             assertEquals(2, TestUtils.getTestVal(key.getData()));
167             assertEquals(8, TestUtils.getTestVal(data.getData()));
168
169             /* Use search both */
170             data.setData(TestUtils.getTestArray(8));
171             assertEquals(OperationStatus.SUCCESS,
172              readCursor.getSearchBoth(key, data,
173                           LockMode.DEFAULT));
174             assertEquals(2, TestUtils.getTestVal(key.getData()));
175             assertEquals(8, TestUtils.getTestVal(data.getData()));
176
177             /* Use search both range, starting data at 8 */
178             data.setData(TestUtils.getTestArray(8));
179             assertEquals(OperationStatus.SUCCESS,
180              readCursor.getSearchBothRange(key, data,
181                                LockMode.DEFAULT));
182             assertEquals(2, TestUtils.getTestVal(key.getData()));
183             assertEquals(8, TestUtils.getTestVal(data.getData()));
184
185             /* Use search both range, starting at 1 */
186             data.setData(TestUtils.getTestArray(1));
187             assertEquals(OperationStatus.SUCCESS,
188              readCursor.getSearchBothRange(key, data,
189                                LockMode.DEFAULT));
190             assertEquals(2, TestUtils.getTestVal(key.getData()));
191             assertEquals(8, TestUtils.getTestVal(data.getData()));
192
193             /*
194              * Make sure we can find k2/d13 with a range search.
195              */

196
197             /*
198          * Insert a set of duplicates, k5/d0 -> k5/d9, then delete
199              * all of them (but don't compress). Make sure no form of
200              * search every finds them.
201              */

202             key.setData(TestUtils.getTestArray(5));
203             for (int i = 0; i < 10; i++) {
204                 data.setData(TestUtils.getTestArray(i));
205                 myDb.put(null, key, data);
206             }
207             myDb.delete(null, key); // delete all k5's
208

209             /* All searches on key 5 should fail */
210             assertFalse(readCursor.getSearchKey(key, data, LockMode.DEFAULT) ==
211             OperationStatus.SUCCESS);
212             assertFalse(readCursor.getSearchKeyRange(key, data,
213                              LockMode.DEFAULT) ==
214             OperationStatus.SUCCESS);
215             data.setData(TestUtils.getTestArray(0));
216             assertFalse(readCursor.getSearchBoth(key, data,
217                          LockMode.DEFAULT) ==
218             OperationStatus.SUCCESS);
219             assertFalse(readCursor.getSearchBothRange(key, data,
220                               LockMode.DEFAULT) ==
221             OperationStatus.SUCCESS);
222
223             /* All ranges on key 4 should also fail. */
224             key.setData(TestUtils.getTestArray(4));
225             assertFalse(readCursor.getSearchKeyRange(key, data,
226                              LockMode.DEFAULT) ==
227             OperationStatus.SUCCESS);
228             assertFalse(readCursor.getSearchBothRange(key, data,
229                                                       LockMode.DEFAULT) ==
230             OperationStatus.SUCCESS);
231
232             readCursor.close();
233     } catch (Throwable JavaDoc t) {
234
235         t.printStackTrace();
236         throw t;
237     } finally {
238             if (cursor != null) {
239                 cursor.close();
240             }
241             myDb.close();
242         }
243     }
244
245     /**
246      * Test the case where we allow duplicates in the database, but
247      * don't actually insert a duplicate. So we have a key/value pair
248      * and do a getSearchBothRange using key and data-1 (i.e. we land
249      * on the key, but just before the data in the dup set (which isn't
250      * a dup set since there's only one). getSearchBothRange should land
251      * on the key/value pair in this case. See SR #9248.
252      */

253     public void testSearchBothWithOneDuplicate()
254     throws Throwable JavaDoc {
255
256         Database myDb = null;
257         Cursor cursor = null;
258     try {
259             /* Set up a db */
260             DatabaseConfig dbConfig = new DatabaseConfig();
261             dbConfig.setTransactional(true);
262             dbConfig.setSortedDuplicates(true);
263             dbConfig.setAllowCreate(true);
264             myDb = env.openDatabase(null, "foo", dbConfig);
265
266             /* Put one record */
267             DatabaseEntry key = new DatabaseEntry();
268             DatabaseEntry data = new DatabaseEntry();
269             key.setData(TestUtils.getTestArray(1));
270             data.setData(TestUtils.getTestArray(1));
271             myDb.put(null, key, data);
272             
273             key.setData(TestUtils.getTestArray(1));
274             data.setData(TestUtils.getTestArray(0));
275             cursor = myDb.openCursor(null, CursorConfig.DEFAULT);
276             OperationStatus status =
277         cursor.getSearchBothRange(key, data, LockMode.DEFAULT);
278             assertSame(status, OperationStatus.SUCCESS);
279             assertEquals(1, TestUtils.getTestVal(key.getData()));
280             assertEquals(1, TestUtils.getTestVal(data.getData()));
281     } finally {
282             if (cursor != null) {
283                 cursor.close();
284             }
285             if (myDb != null) {
286                 myDb.close();
287             }
288         }
289     }
290
291     /**
292      * Tests a bug fix to CursorImpl.fetchCurrent [#11195].
293      *
294      * T1 inserts K1-D1 and holds WRITE on K1-D1 (no dup tree yet)
295      * T2 calls getFirst and waits for READ on K1-D1
296      * T1 inserts K1-D2 which creates the dup tree
297      * T1 commits, allowing T2 to proceed
298      *
299      * T2 is in the middle of CursorImpl.fetchCurrent, and assumes incorrectly
300      * that it has a lock on an LN in BIN; actually the LN was replaced by a
301      * DIN and a ClassCastException occurs.
302      */

303     public void testGetCurrentDuringDupTreeCreation()
304     throws Throwable JavaDoc {
305
306         /* Set up a db */
307         DatabaseConfig dbConfig = new DatabaseConfig();
308         dbConfig.setTransactional(true);
309         dbConfig.setSortedDuplicates(true);
310         dbConfig.setAllowCreate(true);
311         final Database myDb = env.openDatabase(null, "foo", dbConfig);
312
313         /* T1 inserts K1-D1. */
314         Transaction t1 = env.beginTransaction(null, null);
315         DatabaseEntry key = new DatabaseEntry();
316         DatabaseEntry data = new DatabaseEntry();
317         key.setData(TestUtils.getTestArray(1));
318         data.setData(TestUtils.getTestArray(1));
319         myDb.put(t1, key, data);
320
321         /* T2 calls getFirst. */
322         JUnitThread thread = new JUnitThread("getFirst") {
323             public void testBody()
324                 throws DatabaseException {
325                 DatabaseEntry key = new DatabaseEntry();
326                 DatabaseEntry data = new DatabaseEntry();
327                 Transaction t2 = env.beginTransaction(null, null);
328                 operationStarted = true;
329                 Cursor cursor = myDb.openCursor(t2, null);
330                 OperationStatus status = cursor.getFirst(key, data, null);
331                 assertEquals(1, TestUtils.getTestVal(key.getData()));
332                 assertEquals(1, TestUtils.getTestVal(data.getData()));
333                 assertEquals(OperationStatus.SUCCESS, status);
334                 cursor.close();
335                 t2.commitNoSync();
336             }
337         };
338         thread.start();
339         while (!operationStarted) {
340             Thread.yield();
341         }
342         Thread.sleep(10);
343
344         /* T1 inserts K1-D2. */
345         key.setData(TestUtils.getTestArray(1));
346         data.setData(TestUtils.getTestArray(2));
347         myDb.put(t1, key, data);
348         t1.commitNoSync();
349
350         try {
351             thread.finishTest();
352         } catch (Throwable JavaDoc e) {
353             e.printStackTrace();
354             fail(e.toString());
355         }
356         myDb.close();
357     }
358
359     /**
360      * Tests a bug fix to CursorImpl.fetchCurrent [#11700] that caused
361      * ArrayIndexOutOfBoundsException.
362      */

363     public void testGetPrevNoDupWithEmptyTree()
364     throws Throwable JavaDoc {
365
366         OperationStatus status;
367
368         /*
369          * Set up a db
370          */

371         DatabaseConfig dbConfig = new DatabaseConfig();
372         dbConfig.setSortedDuplicates(true);
373         dbConfig.setAllowCreate(true);
374         Database myDb = env.openDatabase(null, "foo", dbConfig);
375
376         /*
377          * Insert two sets of duplicates.
378          */

379         DatabaseEntry key = new DatabaseEntry();
380         DatabaseEntry data = new DatabaseEntry();
381
382         key.setData(TestUtils.getTestArray(1));
383         data.setData(TestUtils.getTestArray(1));
384         myDb.put(null, key, data);
385         data.setData(TestUtils.getTestArray(2));
386         myDb.put(null, key, data);
387
388         key.setData(TestUtils.getTestArray(2));
389         data.setData(TestUtils.getTestArray(1));
390         myDb.put(null, key, data);
391         data.setData(TestUtils.getTestArray(2));
392         myDb.put(null, key, data);
393
394         /*
395          * Delete all duplicates with a cursor.
396          */

397         Cursor cursor = myDb.openCursor(null, null);
398         while ((status = cursor.getNext(key, data, null)) ==
399                 OperationStatus.SUCCESS) {
400             cursor.delete();
401         }
402
403         /*
404          * Compress to empty the two DBINs. The BIN will not be deleted
405          * because a cursor is attached to it. This causes a cursor to be
406          * positioned on an empty DBIN, which brings out the bug.
407          */

408         env.compress();
409
410         /*
411          * Before the bug fix, getPrevNoDup caused
412          * ArrayIndexOutOfBoundsException.
413          */

414         status = cursor.getPrevNoDup(key, data, null);
415         assertEquals(OperationStatus.NOTFOUND, status);
416
417         cursor.close();
418         myDb.close();
419     }
420
421     /*
422      * Check that non transactional cursors can't do update operations
423      * against a transactional database.
424      */

425     public void testNonTxnalCursorNoUpdates()
426         throws Throwable JavaDoc {
427
428         Database myDb = null;
429         SecondaryDatabase mySecDb = null;
430         Cursor cursor = null;
431         SecondaryCursor secCursor = null;
432     try {
433             /* Set up a db with a secondary, insert something. */
434             DatabaseConfig dbConfig = new DatabaseConfig();
435             dbConfig.setTransactional(true);
436             dbConfig.setAllowCreate(true);
437             myDb = env.openDatabase(null, "foo", dbConfig);
438
439             SecondaryConfig secConfig = new SecondaryConfig();
440             secConfig.setTransactional(true);
441             secConfig.setAllowCreate(true);
442             secConfig.setKeyCreator(new KeyCreator());
443             mySecDb = env.openSecondaryDatabase(null, "fooSecDb", myDb,
444                                                 secConfig);
445
446             /* Insert something. */
447             DatabaseEntry key = new DatabaseEntry(new byte[1]);
448             assertEquals(myDb.put(null, key, key), OperationStatus.SUCCESS);
449
450             /* Open a non-txnal cursor on the primary database. */
451             cursor = myDb.openCursor(null, null);
452             DatabaseEntry data = new DatabaseEntry();
453             assertEquals(OperationStatus.SUCCESS,
454                          cursor.getNext(key, data, LockMode.DEFAULT));
455
456             /* All updates should be prohibited. */
457             updatesShouldBeProhibited(cursor);
458
459             /* Open a secondary non-txnal cursor */
460             secCursor = mySecDb.openSecondaryCursor(null, null);
461             assertEquals(OperationStatus.SUCCESS,
462                          secCursor.getNext(key, data, LockMode.DEFAULT));
463
464             /* All updates should be prohibited. */
465             updatesShouldBeProhibited(secCursor);
466
467     } catch (Throwable JavaDoc t) {
468         t.printStackTrace();
469         throw t;
470     } finally {
471             if (secCursor != null) {
472                 secCursor.close();
473             }
474
475             if (cursor != null) {
476                 cursor.close();
477             }
478
479             if (mySecDb != null) {
480                 mySecDb.close();
481             }
482
483             myDb.close();
484         }
485     }
486
487     /* Updates should not be possible with this cursor. */
488     private void updatesShouldBeProhibited(Cursor cursor)
489         throws Exception JavaDoc {
490         
491         try {
492             cursor.delete();
493             fail("Should not be able to do a delete");
494         } catch (DatabaseException e) {
495             checkForTransactionException(e);
496         }
497
498         DatabaseEntry key = new DatabaseEntry(new byte[0]);
499         DatabaseEntry data = new DatabaseEntry(new byte[0]);
500
501         try {
502             cursor.put(key, data);
503             fail("Should not be able to do a put");
504         } catch (UnsupportedOperationException JavaDoc e) {
505             /* disregard for secondary cursors */
506         } catch (DatabaseException e) {
507             checkForTransactionException(e);
508         }
509
510
511         try {
512             cursor.putCurrent(data);
513             fail("Should not be able to do a putCurrent");
514         } catch (UnsupportedOperationException JavaDoc e) {
515             /* disregard for secondary cursors */
516         } catch (DatabaseException e) {
517             checkForTransactionException(e);
518         }
519
520         try {
521             cursor.putNoDupData(key, data);
522             fail("Should not be able to do a putNoDupData");
523         } catch (UnsupportedOperationException JavaDoc e) {
524             /* disregard for secondary cursors */
525         } catch (DatabaseException e) {
526             checkForTransactionException(e);
527         }
528
529         try {
530             cursor.putNoOverwrite(key, data);
531             fail("Should not be able to do a putNoOverwrite");
532         } catch (UnsupportedOperationException JavaDoc e) {
533             /* disregard for secondary cursors */
534         } catch (DatabaseException e) {
535             checkForTransactionException(e);
536         }
537     }
538
539     private void checkForTransactionException(DatabaseException e) {
540         /*
541          * Check that it's a transaction problem. Crude, but since we
542          * don't want to add exception types, necessary.
543          */

544         String JavaDoc eMsg = e.getMessage();
545         assertTrue(TestUtils.skipVersion(e).startsWith("A transaction was not supplied"));
546     }
547
548     private static class KeyCreator implements SecondaryKeyCreator {
549         public boolean createSecondaryKey(SecondaryDatabase secondaryDb,
550                                           DatabaseEntry keyEntry,
551                                           DatabaseEntry dataEntry,
552                                           DatabaseEntry resultEntry) {
553             resultEntry.setData(dataEntry.getData());
554             return true;
555         }
556     }
557
558     /**
559      * Tests that when a LockNotGrantedException is thrown as the result of a
560      * cursor operation, all latches are released properly. There are two
561      * cases corresponding to the two methods in CursorImpl --
562      * lockLNDeletedAllowed and lockDupCountLN, which lock leaf LNs and dup
563      * count LNs, respectively -- that handle locking and latching. These
564      * methods optimize by not releasing latches while obtaining a non-blocking
565      * lock. Prior to the fix for [#15142], these methods did not release
566      * latches when LockNotGrantedException, which can occur when a transaction
567      * is configured for "no wait".
568      */

569     public void testNoWaitLatchRelease()
570     throws Throwable JavaDoc {
571
572         DatabaseEntry key = new DatabaseEntry();
573         DatabaseEntry data = new DatabaseEntry();
574
575         /* Open the database. */
576         DatabaseConfig dbConfig = new DatabaseConfig();
577         dbConfig.setTransactional(true);
578         dbConfig.setAllowCreate(true);
579         dbConfig.setSortedDuplicates(true);
580         Database db = env.openDatabase(null, "foo", dbConfig);
581
582         /* Insert record 1. */
583         key.setData(TestUtils.getTestArray(1));
584         data.setData(TestUtils.getTestArray(1));
585         db.put(null, key, data);
586
587         /* Open cursor1 with txn1 and lock record 1. */
588         Transaction txn1 = env.beginTransaction(null, null);
589         Cursor cursor1 = db.openCursor(txn1, null);
590         key.setData(TestUtils.getTestArray(1));
591         data.setData(TestUtils.getTestArray(1));
592         OperationStatus status = cursor1.getSearchBoth(key, data, null);
593         assertSame(status, OperationStatus.SUCCESS);
594         assertEquals(1, TestUtils.getTestVal(key.getData()));
595         assertEquals(1, TestUtils.getTestVal(data.getData()));
596
597         /* Open cursor2 with no-wait txn2 and try to delete record 1. */
598         TransactionConfig noWaitConfig = new TransactionConfig();
599         noWaitConfig.setNoWait(true);
600         Transaction txn2 = env.beginTransaction(null, noWaitConfig);
601         Cursor cursor2 = db.openCursor(txn2, null);
602         key.setData(TestUtils.getTestArray(1));
603         data.setData(TestUtils.getTestArray(1));
604         status = cursor2.getSearchBoth(key, data, null);
605         assertSame(status, OperationStatus.SUCCESS);
606         assertEquals(1, TestUtils.getTestVal(key.getData()));
607         assertEquals(1, TestUtils.getTestVal(data.getData()));
608         try {
609             cursor2.delete();
610             fail("Expected LockNotGrantedException");
611         } catch (LockNotGrantedException expected) {
612         }
613
614         /*
615          * Before the [#15142] bug fix, this could have failed. However, that
616          * failure was not reproducible because all callers of
617          * lockLNDeletedAllowed redudantly release the BIN latches. So this is
618          * just an extra check to ensure such a bug is never introduced.
619          */

620         assertEquals(0, LatchSupport.countLatchesHeld());
621
622         /* Close cursors and txns to release locks. */
623         cursor1.close();
624         cursor2.close();
625         txn1.commit();
626         txn2.commit();
627
628         /* Insert duplicate record 2 to create a DupCountLN. */
629         key.setData(TestUtils.getTestArray(1));
630         data.setData(TestUtils.getTestArray(2));
631         db.put(null, key, data);
632
633         /* Get the cursor count with cursor1/txn1 to lock the DupCountLN. */
634         txn1 = env.beginTransaction(null, null);
635         cursor1 = db.openCursor(txn1, null);
636         key.setData(TestUtils.getTestArray(1));
637         status = cursor1.getSearchKey(key, data, null);
638         assertSame(status, OperationStatus.SUCCESS);
639         assertEquals(1, TestUtils.getTestVal(key.getData()));
640         assertEquals(1, TestUtils.getTestVal(data.getData()));
641         assertEquals(2, cursor1.count());
642
643         /* Try to write lock the DupCountLN with txn2 by deleting record 2. */
644         txn2 = env.beginTransaction(null, noWaitConfig);
645         cursor2 = db.openCursor(txn2, null);
646         key.setData(TestUtils.getTestArray(1));
647         data.setData(TestUtils.getTestArray(2));
648         status = cursor2.getSearchBoth(key, data, null);
649         assertSame(status, OperationStatus.SUCCESS);
650         assertEquals(1, TestUtils.getTestVal(key.getData()));
651         assertEquals(2, TestUtils.getTestVal(data.getData()));
652         try {
653             cursor2.delete();
654             fail("Expected LockNotGrantedException");
655         } catch (LockNotGrantedException expected) {
656         }
657
658         /* Before the [#15142] bug fix, this would fail. */
659         assertEquals(0, LatchSupport.countLatchesHeld());
660
661         /* Close all. */
662         cursor1.close();
663         cursor2.close();
664         txn1.commit();
665         txn2.commit();
666         db.close();
667     }
668 }
669
Popular Tags