1 /*- 2 * See the file LICENSE for redistribution information. 3 * 4 * Copyright (c) 2002, 2014 Oracle and/or its affiliates. All rights reserved. 5 * 6 */ 7 8 package com.sleepycat.je.test; 9 10 import static org.junit.Assert.assertEquals; 11 import static org.junit.Assert.assertTrue; 12 import static org.junit.Assert.fail; 13 14 import java.io.File; 15 import java.util.ArrayList; 16 import java.util.List; 17 18 import org.junit.After; 19 import org.junit.Test; 20 import org.junit.runner.RunWith; 21 import org.junit.runners.Parameterized; 22 import org.junit.runners.Parameterized.Parameters; 23 24 import com.sleepycat.bind.tuple.IntegerBinding; 25 import com.sleepycat.je.Cursor; 26 import com.sleepycat.je.CursorConfig; 27 import com.sleepycat.je.Database; 28 import com.sleepycat.je.DatabaseConfig; 29 import com.sleepycat.je.DatabaseEntry; 30 import com.sleepycat.je.DatabaseException; 31 import com.sleepycat.je.Durability; 32 import com.sleepycat.je.Environment; 33 import com.sleepycat.je.EnvironmentConfig; 34 import com.sleepycat.je.EnvironmentStats; 35 import com.sleepycat.je.LockConflictException; 36 import com.sleepycat.je.LockMode; 37 import com.sleepycat.je.OperationStatus; 38 import com.sleepycat.je.StatsConfig; 39 import com.sleepycat.je.Transaction; 40 import com.sleepycat.je.TransactionConfig; 41 import com.sleepycat.je.config.EnvironmentParams; 42 import com.sleepycat.je.junit.JUnitThread; 43 import com.sleepycat.je.util.DualTestCase; 44 import com.sleepycat.je.util.TestUtils; 45 import com.sleepycat.util.test.SharedTestUtils; 46 47 /** 48 * Tests phantom prevention (range locking) added in SR [#10477]. 49 * 50 * <p>We test that with a serializable txn, range locking will prevent phantoms 51 * from appearing. We also test that phantoms *do* appear for non-serializable 52 * isolation levels. These include read-uncommitted, read-committed and 53 * repeatable-read now.</p> 54 * 55 * <p>Test method names have the suffix _Sucess or _NotFound depending on 56 * whether they're testing a read operation with a SUCCESS or NOTFOUND outcome. 57 * If they're testing duplicates, the _Dup suffix is also added. Finally, a 58 * suffix is added for the isolation level at run time.</p> 59 * 60 * <p>All tests are for the case where the reader txn locks a range and then 61 * the writer txn tries to insert into the locked range. The reverse (where 62 * the writer inserts first) works without range locking because the reader 63 * will block on the inserted key, so we don't test that here.</p> 64 * 65 * <p>We test all read operations with and without duplicates (with duplicates 66 * the test name has _Dup appended) except for the following cases which are 67 * meaningless without duplicates because get{Next,Prev}Dup always return 68 * NOTFOUND when duplicates are not configured: 69 * testGetNextDup_Success, testGetNextDup_NotFound, 70 * testGetPrevDup_Success, testGetPrevDup_NotFound.</p> 71 */ 72 @RunWith(Parameterized.class) 73 public class PhantomTest extends DualTestCase { 74 75 private static final TransactionConfig READ_UNCOMMITTED_CONFIG 76 = new TransactionConfig(); 77 private static final TransactionConfig READ_COMMITTED_CONFIG 78 = new TransactionConfig(); 79 private static final TransactionConfig REPEATABLE_READ_CONFIG 80 = new TransactionConfig(); 81 private static final TransactionConfig SERIALIZABLE_CONFIG 82 = new TransactionConfig(); 83 static { 84 READ_UNCOMMITTED_CONFIG.setReadUncommitted(true); 85 READ_COMMITTED_CONFIG.setReadCommitted(true); 86 SERIALIZABLE_CONFIG.setSerializableIsolation(true); 87 } 88 private static final TransactionConfig[] TXN_CONFIGS = { 89 READ_UNCOMMITTED_CONFIG, 90 READ_COMMITTED_CONFIG, 91 REPEATABLE_READ_CONFIG, 92 SERIALIZABLE_CONFIG, 93 }; 94 95 private static final String DB_NAME = "PhantomTest"; 96 97 private static final int MAX_INSERT_MILLIS = 5000; 98 99 private File envHome; 100 private Environment env; 101 private Database db; 102 private final TransactionConfig txnConfig; 103 private JUnitThread writerThread; 104 private final boolean txnSerializable; 105 private boolean dups; 106 private boolean insertFinished; 107 108 @Parameters genParams()109 public static List<Object[]> genParams() { 110 List<Object[]> list = new ArrayList<Object[]>(); 111 112 for (TransactionConfig txnConfig : TXN_CONFIGS) 113 list.add(new Object[]{txnConfig}); 114 115 return list; 116 } 117 PhantomTest(TransactionConfig txnConfig)118 public PhantomTest(TransactionConfig txnConfig) { 119 envHome = SharedTestUtils.getTestDir(); 120 this.txnConfig = txnConfig; 121 txnSerializable = (txnConfig == SERIALIZABLE_CONFIG); 122 String txnType; 123 if (txnConfig == SERIALIZABLE_CONFIG) { 124 txnType = "-Serializable"; 125 } else if (txnConfig == REPEATABLE_READ_CONFIG) { 126 txnType = "-RepeatableRead"; 127 } else if (txnConfig == READ_COMMITTED_CONFIG) { 128 txnType = "-ReadCommitted"; 129 } else if (txnConfig == READ_UNCOMMITTED_CONFIG) { 130 txnType = "-ReadUncommitted"; 131 } else { 132 throw new IllegalStateException(); 133 } 134 customName = txnType; 135 } 136 137 @After tearDown()138 public void tearDown() 139 throws Exception { 140 141 super.tearDown(); 142 envHome = null; 143 env = null; 144 db = null; 145 146 if (writerThread != null) { 147 writerThread.shutdown(); 148 writerThread = null; 149 } 150 } 151 152 /** 153 * Opens the environment and database. 154 */ openEnv(boolean dups)155 private void openEnv(boolean dups) 156 throws DatabaseException { 157 158 openEnv(dups, null); 159 } 160 161 /** 162 * Opens the environment and database. 163 */ openEnv(boolean dups, EnvironmentConfig envConfig)164 private void openEnv(boolean dups, EnvironmentConfig envConfig) 165 throws DatabaseException { 166 167 this.dups = dups; 168 if (envConfig == null) { 169 envConfig = TestUtils.initEnvConfig(); 170 /* Control over isolation level is required by this test. */ 171 TestUtils.clearIsolationLevel(envConfig); 172 } 173 174 /* Disable the daemons so the don't interfere with stats. */ 175 envConfig.setConfigParam 176 (EnvironmentParams.ENV_RUN_EVICTOR.getName(), "false"); 177 envConfig.setConfigParam 178 (EnvironmentParams.ENV_RUN_CLEANER.getName(), "false"); 179 envConfig.setConfigParam 180 (EnvironmentParams.ENV_RUN_CHECKPOINTER.getName(), "false"); 181 envConfig.setConfigParam 182 (EnvironmentParams.ENV_RUN_INCOMPRESSOR.getName(), "false"); 183 184 envConfig.setAllowCreate(true); 185 envConfig.setTransactional(true); 186 env = create(envHome, envConfig); 187 188 DatabaseConfig dbConfig = new DatabaseConfig(); 189 dbConfig.setAllowCreate(true); 190 dbConfig.setTransactional(true); 191 dbConfig.setSortedDuplicates(dups); 192 db = env.openDatabase(null, DB_NAME, dbConfig); 193 } 194 195 /** 196 * Closes the environment and database. 197 */ closeEnv()198 private void closeEnv() 199 throws DatabaseException { 200 201 if (db != null) { 202 db.close(); 203 db = null; 204 } 205 if (env != null) { 206 close(env); 207 env = null; 208 } 209 } 210 211 @Test testGetSearchKey_Success()212 public void testGetSearchKey_Success() 213 throws DatabaseException { 214 215 openEnv(false); 216 217 /* Insert key 2. */ 218 insert(2); 219 220 /* getSearchKey returns key 2. */ 221 Transaction readerTxn = env.beginTransaction(null, txnConfig); 222 Cursor cursor = db.openCursor(readerTxn, null); 223 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2)); 224 225 /* Insertions are never blocked. */ 226 try { 227 insert(1); 228 insert(3); 229 } catch (LockConflictException e) { 230 fail(); 231 } 232 233 cursor.close(); 234 readerTxn.commit(Durability.COMMIT_NO_SYNC); 235 closeEnv(); 236 } 237 238 @Test testGetSearchKey_Success_Dup()239 public void testGetSearchKey_Success_Dup() 240 throws DatabaseException, InterruptedException { 241 242 openEnv(true); 243 244 /* Insert dups. */ 245 insert(1, 2); 246 insert(1, 3); 247 248 /* getSearchKey returns key {1,2}. */ 249 Transaction readerTxn = env.beginTransaction(null, txnConfig); 250 Cursor cursor = db.openCursor(readerTxn, null); 251 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 2)); 252 253 /* Insertions after {1, 2} are never blocked. */ 254 try { 255 insert(1, 4); 256 } catch (LockConflictException e) { 257 fail(); 258 } 259 260 /* Insert {1,1} in a writer thread. */ 261 startInsert(1, 1); 262 263 /* 264 * If serializable, getSearchKey should return {1,2} again, otherwise 265 * getSearchKey should see {1,1}. 266 */ 267 if (txnSerializable) { 268 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 2)); 269 } else { 270 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 1)); 271 } 272 273 /* Close reader to allow writer to finish. */ 274 cursor.close(); 275 readerTxn.commit(Durability.COMMIT_NO_SYNC); 276 waitForInsert(); 277 278 /* getSearchKey returns {1,1}. */ 279 readerTxn = env.beginTransaction(null, txnConfig); 280 cursor = db.openCursor(readerTxn, null); 281 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 1)); 282 cursor.close(); 283 readerTxn.commit(); 284 285 closeEnv(); 286 } 287 288 @Test testGetSearchKey_NotFound()289 public void testGetSearchKey_NotFound() 290 throws DatabaseException, InterruptedException { 291 292 openEnv(false); 293 294 /* Insert key 1. */ 295 insert(1); 296 297 /* getSearchKey for key 2 returns NOTFOUND. */ 298 Transaction readerTxn = env.beginTransaction(null, txnConfig); 299 Cursor cursor = db.openCursor(readerTxn, null); 300 assertEquals(OperationStatus.NOTFOUND, searchKey(cursor, 2)); 301 302 /* Insertions before 2 are never blocked. */ 303 try { 304 insert(0); 305 } catch (LockConflictException e) { 306 fail(); 307 } 308 309 /* Insert key 2 in a writer thread. */ 310 startInsert(2); 311 312 /* 313 * If serializable, getSearchKey should return NOTFOUND again; 314 * otherwise getSearchKey should see key 2. 315 */ 316 if (txnSerializable) { 317 assertEquals(OperationStatus.NOTFOUND, searchKey(cursor, 2)); 318 } else { 319 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2)); 320 } 321 322 /* Close reader to allow writer to finish. */ 323 cursor.close(); 324 readerTxn.commit(Durability.COMMIT_NO_SYNC); 325 waitForInsert(); 326 327 /* getSearchKey returns key 2. */ 328 readerTxn = env.beginTransaction(null, txnConfig); 329 cursor = db.openCursor(readerTxn, null); 330 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2)); 331 cursor.close(); 332 readerTxn.commit(); 333 334 closeEnv(); 335 } 336 337 @Test testGetSearchKey_NotFound_Dup()338 public void testGetSearchKey_NotFound_Dup() 339 throws DatabaseException, InterruptedException { 340 341 openEnv(true); 342 343 /* Insert dups. */ 344 insert(2, 1); 345 insert(2, 2); 346 347 /* getSearchKey for {1,1} returns NOTFOUND. */ 348 Transaction readerTxn = env.beginTransaction(null, txnConfig); 349 Cursor cursor = db.openCursor(readerTxn, null); 350 assertEquals(OperationStatus.NOTFOUND, searchKey(cursor, 1, 1)); 351 352 /* Insertions after {2,2} are never blocked. */ 353 try { 354 insert(2, 3); 355 insert(3, 0); 356 } catch (LockConflictException e) { 357 fail(); 358 } 359 360 /* Insert {1,1} in a writer thread. */ 361 startInsert(1, 1); 362 363 /* 364 * If serializable, getSearchKey should return NOTFOUND again; 365 * otherwise getSearchKey should see {1,1}. 366 */ 367 if (txnSerializable) { 368 assertEquals(OperationStatus.NOTFOUND, searchKey(cursor, 1, 1)); 369 } else { 370 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 1)); 371 } 372 373 /* Close reader to allow writer to finish. */ 374 cursor.close(); 375 readerTxn.commit(Durability.COMMIT_NO_SYNC); 376 waitForInsert(); 377 378 /* getSearchKey returns {1,1}. */ 379 readerTxn = env.beginTransaction(null, txnConfig); 380 cursor = db.openCursor(readerTxn, null); 381 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 1)); 382 cursor.close(); 383 readerTxn.commit(); 384 385 closeEnv(); 386 } 387 388 @Test testGetSearchBoth_Success()389 public void testGetSearchBoth_Success() 390 throws DatabaseException { 391 392 doTestGetSearchBoth_Success(false /*useRangeSearch*/); 393 } 394 395 /** 396 * In a non-duplicates DB, getSearchBoth and getSearchBothRange are 397 * equivalent. 398 */ doTestGetSearchBoth_Success(boolean useRangeSearch)399 private void doTestGetSearchBoth_Success(boolean useRangeSearch) 400 throws DatabaseException { 401 402 openEnv(false); 403 404 /* Insert key 2. */ 405 insert(2); 406 407 /* getSearchBoth[Range] returns {2,0}. */ 408 Transaction readerTxn = env.beginTransaction(null, txnConfig); 409 Cursor cursor = db.openCursor(readerTxn, null); 410 assertEquals(OperationStatus.SUCCESS, 411 searchBoth(cursor, 2, 0, useRangeSearch)); 412 413 /* Insertions are never blocked. */ 414 try { 415 insert(1); 416 insert(3); 417 } catch (LockConflictException e) { 418 fail(); 419 } 420 421 cursor.close(); 422 readerTxn.commit(Durability.COMMIT_NO_SYNC); 423 closeEnv(); 424 } 425 426 @Test testGetSearchBoth_Success_Dup()427 public void testGetSearchBoth_Success_Dup() 428 throws DatabaseException { 429 430 openEnv(true); 431 432 /* Insert dups. */ 433 insert(1, 1); 434 insert(1, 3); 435 436 /* getSearchBoth returns key {1,3}. */ 437 Transaction readerTxn = env.beginTransaction(null, txnConfig); 438 Cursor cursor = db.openCursor(readerTxn, null); 439 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 3)); 440 441 /* Insertions are never blocked. */ 442 try { 443 insert(0, 0); 444 insert(1, 0); 445 insert(1, 2); 446 insert(1, 4); 447 insert(2, 0); 448 } catch (LockConflictException e) { 449 fail(); 450 } 451 452 cursor.close(); 453 readerTxn.commit(Durability.COMMIT_NO_SYNC); 454 closeEnv(); 455 } 456 457 @Test testGetSearchBoth_NotFound()458 public void testGetSearchBoth_NotFound() 459 throws DatabaseException, InterruptedException { 460 461 doTestGetSearchBoth_NotFound(false /*useRangeSearch*/); 462 } 463 464 /** 465 * In a non-duplicates DB, getSearchBoth and getSearchBothRange are 466 * equivalent. 467 */ doTestGetSearchBoth_NotFound(boolean useRangeSearch)468 private void doTestGetSearchBoth_NotFound(boolean useRangeSearch) 469 throws DatabaseException, InterruptedException { 470 471 openEnv(false); 472 473 /* Insert key 1. */ 474 insert(1); 475 476 /* getSearchBoth for key 2 returns NOTFOUND. */ 477 Transaction readerTxn = env.beginTransaction(null, txnConfig); 478 Cursor cursor = db.openCursor(readerTxn, null); 479 assertEquals(OperationStatus.NOTFOUND, 480 searchBoth(cursor, 2, useRangeSearch)); 481 482 /* Insertions before 2 are never blocked. */ 483 try { 484 insert(0); 485 } catch (LockConflictException e) { 486 fail(); 487 } 488 489 /* Insert key 2 in a writer thread. */ 490 startInsert(2); 491 492 /* 493 * If serializable, getSearchBoth should return NOTFOUND again; 494 * otherwise getSearchBoth should see key 2. 495 */ 496 if (txnSerializable) { 497 assertEquals(OperationStatus.NOTFOUND, 498 searchBoth(cursor, 2, useRangeSearch)); 499 } else { 500 assertEquals(OperationStatus.SUCCESS, 501 searchBoth(cursor, 2, useRangeSearch)); 502 } 503 504 /* Close reader to allow writer to finish. */ 505 cursor.close(); 506 readerTxn.commit(Durability.COMMIT_NO_SYNC); 507 waitForInsert(); 508 509 /* getSearchBoth returns key 2. */ 510 readerTxn = env.beginTransaction(null, txnConfig); 511 cursor = db.openCursor(readerTxn, null); 512 assertEquals(OperationStatus.SUCCESS, 513 searchBoth(cursor, 2, useRangeSearch)); 514 cursor.close(); 515 readerTxn.commit(); 516 517 closeEnv(); 518 } 519 520 @Test testGetSearchBoth_NotFound_Dup()521 public void testGetSearchBoth_NotFound_Dup() 522 throws DatabaseException, InterruptedException { 523 524 openEnv(true); 525 526 /* Insert dups. */ 527 insert(1, 1); 528 insert(1, 3); 529 530 /* getSearchBoth for {1,2} returns NOTFOUND. */ 531 Transaction readerTxn = env.beginTransaction(null, txnConfig); 532 Cursor cursor = db.openCursor(readerTxn, null); 533 assertEquals(OperationStatus.NOTFOUND, searchBoth(cursor, 1, 2)); 534 535 /* Insertions before {1,2} or after {1,3} are never blocked. */ 536 try { 537 insert(1, 0); 538 insert(0, 0); 539 insert(1, 4); 540 insert(2, 0); 541 } catch (LockConflictException e) { 542 fail(); 543 } 544 545 /* Insert {1,2} in a writer thread. */ 546 startInsert(1, 2); 547 548 /* 549 * If serializable, getSearchBoth should return NOTFOUND again; 550 * otherwise getSearchBoth should see {1,2}. 551 */ 552 if (txnSerializable) { 553 assertEquals(OperationStatus.NOTFOUND, searchBoth(cursor, 1, 2)); 554 } else { 555 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 2)); 556 } 557 558 /* Close reader to allow writer to finish. */ 559 cursor.close(); 560 readerTxn.commit(Durability.COMMIT_NO_SYNC); 561 waitForInsert(); 562 563 /* getSearchBoth returns {1,2}. */ 564 readerTxn = env.beginTransaction(null, txnConfig); 565 cursor = db.openCursor(readerTxn, null); 566 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 2)); 567 cursor.close(); 568 readerTxn.commit(); 569 570 closeEnv(); 571 } 572 573 @Test testGetSearchKeyRange_Success()574 public void testGetSearchKeyRange_Success() 575 throws DatabaseException, InterruptedException { 576 577 openEnv(false); 578 DatabaseEntry key = new DatabaseEntry(); 579 DatabaseEntry data = new DatabaseEntry(); 580 OperationStatus status; 581 582 /* Insert key 1 and 3. */ 583 insert(1); 584 insert(3); 585 586 /* getSearchKeyRange for key 2 returns key 3. */ 587 Transaction readerTxn = env.beginTransaction(null, txnConfig); 588 Cursor cursor = db.openCursor(readerTxn, null); 589 IntegerBinding.intToEntry(2, key); 590 status = cursor.getSearchKeyRange(key, data, null); 591 assertEquals(OperationStatus.SUCCESS, status); 592 assertEquals(3, IntegerBinding.entryToInt(key)); 593 594 /* Insertions before 2 and after 3 are never blocked. */ 595 try { 596 insert(0); 597 insert(4); 598 } catch (LockConflictException e) { 599 fail(); 600 } 601 602 /* Insert key 2 in a writer thread. */ 603 startInsert(2); 604 605 /* 606 * If serializable, getSearchKeyRange should return key 3 again; 607 * otherwise getSearchKeyRange should see key 2. 608 */ 609 IntegerBinding.intToEntry(2, key); 610 status = cursor.getSearchKeyRange(key, data, null); 611 assertEquals(OperationStatus.SUCCESS, status); 612 if (txnSerializable) { 613 assertEquals(3, IntegerBinding.entryToInt(key)); 614 } else { 615 assertEquals(2, IntegerBinding.entryToInt(key)); 616 } 617 618 /* Close reader to allow writer to finish. */ 619 cursor.close(); 620 readerTxn.commit(Durability.COMMIT_NO_SYNC); 621 waitForInsert(); 622 623 /* getSearchKeyRange returns key 2. */ 624 readerTxn = env.beginTransaction(null, txnConfig); 625 cursor = db.openCursor(readerTxn, null); 626 IntegerBinding.intToEntry(2, key); 627 status = cursor.getSearchKeyRange(key, data, null); 628 assertEquals(OperationStatus.SUCCESS, status); 629 assertEquals(2, IntegerBinding.entryToInt(key)); 630 cursor.close(); 631 readerTxn.commit(); 632 633 closeEnv(); 634 } 635 636 @Test testGetSearchKeyRange_Success_Dup()637 public void testGetSearchKeyRange_Success_Dup() 638 throws DatabaseException, InterruptedException { 639 640 openEnv(true); 641 DatabaseEntry key = new DatabaseEntry(); 642 DatabaseEntry data = new DatabaseEntry(); 643 OperationStatus status; 644 645 /* Insert dups. */ 646 insert(1, 1); 647 insert(1, 2); 648 insert(3, 2); 649 insert(3, 3); 650 651 /* getSearchKeyRange for key 2 returns {3,2}. */ 652 Transaction readerTxn = env.beginTransaction(null, txnConfig); 653 Cursor cursor = db.openCursor(readerTxn, null); 654 IntegerBinding.intToEntry(2, key); 655 status = cursor.getSearchKeyRange(key, data, null); 656 assertEquals(3, IntegerBinding.entryToInt(key)); 657 assertEquals(2, IntegerBinding.entryToInt(data)); 658 assertEquals(OperationStatus.SUCCESS, status); 659 660 /* Insertions before 2 and after {3,3} are never blocked. */ 661 try { 662 insert(1, 0); 663 insert(0, 0); 664 insert(3, 4); 665 insert(4, 0); 666 } catch (LockConflictException e) { 667 fail(); 668 } 669 670 /* Insert {3,1} in a writer thread. */ 671 startInsert(3, 1); 672 673 /* 674 * If serializable, getSearchKeyRange should return {3,2} again; 675 * otherwise getSearchKeyRange should see {3,1}. 676 */ 677 IntegerBinding.intToEntry(2, key); 678 status = cursor.getSearchKeyRange(key, data, null); 679 assertEquals(OperationStatus.SUCCESS, status); 680 if (txnSerializable) { 681 assertEquals(3, IntegerBinding.entryToInt(key)); 682 assertEquals(2, IntegerBinding.entryToInt(data)); 683 } else { 684 assertEquals(3, IntegerBinding.entryToInt(key)); 685 assertEquals(1, IntegerBinding.entryToInt(data)); 686 } 687 688 /* Close reader to allow writer to finish. */ 689 cursor.close(); 690 readerTxn.commit(Durability.COMMIT_NO_SYNC); 691 waitForInsert(); 692 693 /* getSearchKeyRange returns {3,1}. */ 694 readerTxn = env.beginTransaction(null, txnConfig); 695 cursor = db.openCursor(readerTxn, null); 696 IntegerBinding.intToEntry(2, key); 697 status = cursor.getSearchKeyRange(key, data, null); 698 assertEquals(OperationStatus.SUCCESS, status); 699 assertEquals(3, IntegerBinding.entryToInt(key)); 700 assertEquals(1, IntegerBinding.entryToInt(data)); 701 cursor.close(); 702 readerTxn.commit(); 703 704 closeEnv(); 705 } 706 707 @Test testGetSearchKeyRange_NotFound()708 public void testGetSearchKeyRange_NotFound() 709 throws DatabaseException, InterruptedException { 710 711 openEnv(false); 712 DatabaseEntry key = new DatabaseEntry(); 713 DatabaseEntry data = new DatabaseEntry(); 714 OperationStatus status; 715 716 /* Insert key 1. */ 717 insert(1); 718 719 /* getSearchKeyRange for key 2 returns NOTFOUND. */ 720 Transaction readerTxn = env.beginTransaction(null, txnConfig); 721 Cursor cursor = db.openCursor(readerTxn, null); 722 IntegerBinding.intToEntry(2, key); 723 status = cursor.getSearchKeyRange(key, data, null); 724 assertEquals(OperationStatus.NOTFOUND, status); 725 726 /* Insertions before 2 are never blocked. */ 727 try { 728 insert(0); 729 } catch (LockConflictException e) { 730 fail(); 731 } 732 733 /* Insert key 3 in a writer thread. */ 734 startInsert(3); 735 736 /* 737 * If serializable, getSearchKeyRange should return NOTFOUND again; 738 * otherwise getSearchKeyRange should see key 3. 739 */ 740 IntegerBinding.intToEntry(2, key); 741 status = cursor.getSearchKeyRange(key, data, null); 742 if (txnSerializable) { 743 assertEquals(OperationStatus.NOTFOUND, status); 744 } else { 745 assertEquals(OperationStatus.SUCCESS, status); 746 assertEquals(3, IntegerBinding.entryToInt(key)); 747 } 748 749 /* Close reader to allow writer to finish. */ 750 cursor.close(); 751 readerTxn.commit(Durability.COMMIT_NO_SYNC); 752 waitForInsert(); 753 754 /* getSearchKeyRange returns key 3. */ 755 readerTxn = env.beginTransaction(null, txnConfig); 756 cursor = db.openCursor(readerTxn, null); 757 IntegerBinding.intToEntry(2, key); 758 status = cursor.getSearchKeyRange(key, data, null); 759 assertEquals(OperationStatus.SUCCESS, status); 760 assertEquals(3, IntegerBinding.entryToInt(key)); 761 cursor.close(); 762 readerTxn.commit(); 763 764 closeEnv(); 765 } 766 767 @Test testGetSearchKeyRange_NotFound_Dup()768 public void testGetSearchKeyRange_NotFound_Dup() 769 throws DatabaseException, InterruptedException { 770 771 openEnv(true); 772 DatabaseEntry key = new DatabaseEntry(); 773 DatabaseEntry data = new DatabaseEntry(); 774 OperationStatus status; 775 776 /* Insert dups. */ 777 insert(1, 1); 778 insert(1, 2); 779 780 /* getSearchKeyRange for key 2 returns NOTFOUND. */ 781 Transaction readerTxn = env.beginTransaction(null, txnConfig); 782 Cursor cursor = db.openCursor(readerTxn, null); 783 IntegerBinding.intToEntry(2, key); 784 status = cursor.getSearchKeyRange(key, data, null); 785 assertEquals(OperationStatus.NOTFOUND, status); 786 787 /* Insertions before 2 are never blocked. */ 788 try { 789 insert(1, 0); 790 insert(0, 0); 791 } catch (LockConflictException e) { 792 fail(); 793 } 794 795 /* Insert {3,1} in a writer thread. */ 796 startInsert(3, 1); 797 798 /* 799 * If serializable, getSearchKeyRange should return NOTFOUND again; 800 * otherwise getSearchKeyRange should see {3,1}. 801 */ 802 IntegerBinding.intToEntry(2, key); 803 status = cursor.getSearchKeyRange(key, data, null); 804 if (txnSerializable) { 805 assertEquals(OperationStatus.NOTFOUND, status); 806 } else { 807 assertEquals(OperationStatus.SUCCESS, status); 808 assertEquals(3, IntegerBinding.entryToInt(key)); 809 assertEquals(1, IntegerBinding.entryToInt(data)); 810 } 811 812 /* Close reader to allow writer to finish. */ 813 cursor.close(); 814 readerTxn.commit(Durability.COMMIT_NO_SYNC); 815 waitForInsert(); 816 817 /* getSearchKeyRange returns {3,1}. */ 818 readerTxn = env.beginTransaction(null, txnConfig); 819 cursor = db.openCursor(readerTxn, null); 820 IntegerBinding.intToEntry(2, key); 821 status = cursor.getSearchKeyRange(key, data, null); 822 assertEquals(OperationStatus.SUCCESS, status); 823 assertEquals(3, IntegerBinding.entryToInt(key)); 824 assertEquals(1, IntegerBinding.entryToInt(data)); 825 cursor.close(); 826 readerTxn.commit(); 827 828 closeEnv(); 829 } 830 831 /* 832 * A testGetSearchBothRange_Success test case is not possible because it is 833 * not possible to insert a duplicate when only one LN for the key already 834 * exists, without locking the existing LN. Therefore, the insert thread 835 * will deadlock with the reader thread, which has the existing LN locked. 836 * This is a testing anomoly, not a bug. 837 */ 838 839 @Test testGetSearchBothRange_Success_Dup()840 public void testGetSearchBothRange_Success_Dup() 841 throws DatabaseException, InterruptedException { 842 843 openEnv(true); 844 DatabaseEntry key = new DatabaseEntry(); 845 DatabaseEntry data = new DatabaseEntry(); 846 OperationStatus status; 847 848 /* Insert dups. */ 849 insert(1, 1); 850 insert(1, 2); 851 insert(3, 2); 852 insert(3, 3); 853 854 /* getSearchBothRange for {3, 0} returns {3,2}. */ 855 Transaction readerTxn = env.beginTransaction(null, txnConfig); 856 Cursor cursor = db.openCursor(readerTxn, null); 857 IntegerBinding.intToEntry(3, key); 858 IntegerBinding.intToEntry(0, data); 859 status = cursor.getSearchBothRange(key, data, null); 860 assertEquals(OperationStatus.SUCCESS, status); 861 assertEquals(3, IntegerBinding.entryToInt(key)); 862 assertEquals(2, IntegerBinding.entryToInt(data)); 863 864 /* Insertions before {1,1} and after {3,2} are never blocked. */ 865 try { 866 insert(1, 0); 867 insert(0, 0); 868 insert(3, 4); 869 } catch (LockConflictException e) { 870 fail(); 871 } 872 873 /* Insert {3,1} in a writer thread. */ 874 startInsert(3, 1); 875 876 /* 877 * If serializable, getSearchBothRange should return {3,2} again; 878 * otherwise getSearchBothRange should see {3,1}. 879 */ 880 IntegerBinding.intToEntry(3, key); 881 IntegerBinding.intToEntry(0, data); 882 status = cursor.getSearchBothRange(key, data, null); 883 assertEquals(OperationStatus.SUCCESS, status); 884 if (txnSerializable) { 885 assertEquals(3, IntegerBinding.entryToInt(key)); 886 assertEquals(2, IntegerBinding.entryToInt(data)); 887 } else { 888 assertEquals(3, IntegerBinding.entryToInt(key)); 889 assertEquals(1, IntegerBinding.entryToInt(data)); 890 } 891 892 /* Close reader to allow writer to finish. */ 893 cursor.close(); 894 readerTxn.commit(Durability.COMMIT_NO_SYNC); 895 waitForInsert(); 896 897 /* getSearchBothRange returns {3,1}. */ 898 readerTxn = env.beginTransaction(null, txnConfig); 899 cursor = db.openCursor(readerTxn, null); 900 IntegerBinding.intToEntry(3, key); 901 IntegerBinding.intToEntry(0, data); 902 status = cursor.getSearchBothRange(key, data, null); 903 assertEquals(OperationStatus.SUCCESS, status); 904 assertEquals(3, IntegerBinding.entryToInt(key)); 905 assertEquals(1, IntegerBinding.entryToInt(data)); 906 cursor.close(); 907 readerTxn.commit(); 908 909 closeEnv(); 910 } 911 912 @Test testGetSearchBothRange_NotFound()913 public void testGetSearchBothRange_NotFound() 914 throws DatabaseException, InterruptedException { 915 916 doTestGetSearchBoth_NotFound(true /*useRangeSearch*/); 917 } 918 919 @Test testGetSearchBothRange_NotFound_Dup()920 public void testGetSearchBothRange_NotFound_Dup() 921 throws DatabaseException, InterruptedException { 922 923 openEnv(true); 924 DatabaseEntry key = new DatabaseEntry(); 925 DatabaseEntry data = new DatabaseEntry(); 926 OperationStatus status; 927 928 /* Insert dups. */ 929 insert(3, 0); 930 insert(3, 1); 931 932 /* getSearchBothRange for {3, 2} returns NOTFOUND. */ 933 Transaction readerTxn = env.beginTransaction(null, txnConfig); 934 Cursor cursor = db.openCursor(readerTxn, null); 935 IntegerBinding.intToEntry(3, key); 936 IntegerBinding.intToEntry(2, data); 937 status = cursor.getSearchBothRange(key, data, null); 938 assertEquals(OperationStatus.NOTFOUND, status); 939 940 /* Insertions before {3,0} are never blocked. */ 941 try { 942 insert(3, -1); 943 insert(2, 0); 944 } catch (LockConflictException e) { 945 fail(); 946 } 947 948 /* Insert {3,3} in a writer thread. */ 949 startInsert(3, 3); 950 951 /* 952 * If serializable, getSearchBothRange should return NOTFOUND again; 953 * otherwise getSearchBothRange should see {3,3}. 954 */ 955 IntegerBinding.intToEntry(3, key); 956 IntegerBinding.intToEntry(2, data); 957 status = cursor.getSearchBothRange(key, data, null); 958 if (txnSerializable) { 959 assertEquals(OperationStatus.NOTFOUND, status); 960 } else { 961 assertEquals(OperationStatus.SUCCESS, status); 962 assertEquals(3, IntegerBinding.entryToInt(key)); 963 assertEquals(3, IntegerBinding.entryToInt(data)); 964 } 965 966 /* Close reader to allow writer to finish. */ 967 cursor.close(); 968 readerTxn.commit(Durability.COMMIT_NO_SYNC); 969 waitForInsert(); 970 971 /* getSearchBothRange returns {3,3}. */ 972 readerTxn = env.beginTransaction(null, txnConfig); 973 cursor = db.openCursor(readerTxn, null); 974 IntegerBinding.intToEntry(3, key); 975 IntegerBinding.intToEntry(2, data); 976 status = cursor.getSearchBothRange(key, data, null); 977 assertEquals(OperationStatus.SUCCESS, status); 978 assertEquals(3, IntegerBinding.entryToInt(key)); 979 assertEquals(3, IntegerBinding.entryToInt(data)); 980 cursor.close(); 981 readerTxn.commit(); 982 983 closeEnv(); 984 } 985 986 @Test testGetFirst_Success()987 public void testGetFirst_Success() 988 throws DatabaseException, InterruptedException { 989 990 openEnv(false); 991 DatabaseEntry key = new DatabaseEntry(); 992 DatabaseEntry data = new DatabaseEntry(); 993 OperationStatus status; 994 995 /* Insert key 2. */ 996 insert(2); 997 998 /* getFirst returns key 2. */ 999 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1000 Cursor cursor = db.openCursor(readerTxn, null); 1001 status = cursor.getFirst(key, data, null); 1002 assertEquals(OperationStatus.SUCCESS, status); 1003 assertEquals(2, IntegerBinding.entryToInt(key)); 1004 1005 /* Insertions after 2 are never blocked. */ 1006 try { 1007 insert(3); 1008 } catch (LockConflictException e) { 1009 fail(); 1010 } 1011 1012 /* Insert key 1 in a writer thread. */ 1013 startInsert(1); 1014 1015 /* 1016 * If serializable, getFirst should return key 2 again; otherwise 1017 * getFirst should see key 1. 1018 */ 1019 status = cursor.getFirst(key, data, null); 1020 assertEquals(OperationStatus.SUCCESS, status); 1021 if (txnSerializable) { 1022 assertEquals(2, IntegerBinding.entryToInt(key)); 1023 } else { 1024 assertEquals(1, IntegerBinding.entryToInt(key)); 1025 } 1026 1027 /* Close reader to allow writer to finish. */ 1028 cursor.close(); 1029 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1030 waitForInsert(); 1031 1032 /* getFirst returns key 1. */ 1033 readerTxn = env.beginTransaction(null, txnConfig); 1034 cursor = db.openCursor(readerTxn, null); 1035 status = cursor.getFirst(key, data, null); 1036 assertEquals(OperationStatus.SUCCESS, status); 1037 assertEquals(1, IntegerBinding.entryToInt(key)); 1038 cursor.close(); 1039 readerTxn.commit(); 1040 1041 closeEnv(); 1042 } 1043 1044 @Test testGetFirst_Success_Dup()1045 public void testGetFirst_Success_Dup() 1046 throws DatabaseException, InterruptedException { 1047 1048 openEnv(true); 1049 DatabaseEntry key = new DatabaseEntry(); 1050 DatabaseEntry data = new DatabaseEntry(); 1051 OperationStatus status; 1052 1053 /* Insert dups. */ 1054 insert(1, 2); 1055 insert(1, 3); 1056 1057 /* getFirst returns {1,2}. */ 1058 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1059 Cursor cursor = db.openCursor(readerTxn, null); 1060 status = cursor.getFirst(key, data, null); 1061 assertEquals(OperationStatus.SUCCESS, status); 1062 assertEquals(1, IntegerBinding.entryToInt(key)); 1063 assertEquals(2, IntegerBinding.entryToInt(data)); 1064 1065 /* Insertions after {1,3} are never blocked. */ 1066 try { 1067 insert(1, 4); 1068 insert(2, 0); 1069 } catch (LockConflictException e) { 1070 fail(); 1071 } 1072 1073 /* Insert {1,1} in a writer thread. */ 1074 startInsert(1, 1); 1075 1076 /* 1077 * If serializable, getFirst should return {1,2} again; otherwise 1078 * getFirst should see {1,1}. 1079 */ 1080 status = cursor.getFirst(key, data, null); 1081 assertEquals(OperationStatus.SUCCESS, status); 1082 if (txnSerializable) { 1083 assertEquals(1, IntegerBinding.entryToInt(key)); 1084 assertEquals(2, IntegerBinding.entryToInt(data)); 1085 } else { 1086 assertEquals(1, IntegerBinding.entryToInt(key)); 1087 assertEquals(1, IntegerBinding.entryToInt(data)); 1088 } 1089 1090 /* Close reader to allow writer to finish. */ 1091 cursor.close(); 1092 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1093 waitForInsert(); 1094 1095 /* getFirst returns {1,1}. */ 1096 readerTxn = env.beginTransaction(null, txnConfig); 1097 cursor = db.openCursor(readerTxn, null); 1098 status = cursor.getFirst(key, data, null); 1099 assertEquals(OperationStatus.SUCCESS, status); 1100 assertEquals(1, IntegerBinding.entryToInt(key)); 1101 assertEquals(1, IntegerBinding.entryToInt(data)); 1102 cursor.close(); 1103 readerTxn.commit(); 1104 1105 closeEnv(); 1106 } 1107 1108 @Test testGetFirst_NotFound()1109 public void testGetFirst_NotFound() 1110 throws DatabaseException, InterruptedException { 1111 1112 openEnv(false); 1113 DatabaseEntry key = new DatabaseEntry(); 1114 DatabaseEntry data = new DatabaseEntry(); 1115 OperationStatus status; 1116 1117 /* getFirst returns NOTFOUND. */ 1118 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1119 Cursor cursor = db.openCursor(readerTxn, null); 1120 status = cursor.getFirst(key, data, null); 1121 assertEquals(OperationStatus.NOTFOUND, status); 1122 1123 /* Insert key 1 in a writer thread. */ 1124 startInsert(1); 1125 1126 /* 1127 * If serializable, getFirst should return NOTFOUND again; otherwise 1128 * getFirst should see key 1. 1129 */ 1130 status = cursor.getFirst(key, data, null); 1131 if (txnSerializable) { 1132 assertEquals(OperationStatus.NOTFOUND, status); 1133 } else { 1134 assertEquals(OperationStatus.SUCCESS, status); 1135 assertEquals(1, IntegerBinding.entryToInt(key)); 1136 } 1137 1138 /* Close reader to allow writer to finish. */ 1139 cursor.close(); 1140 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1141 waitForInsert(); 1142 1143 /* getFirst returns key 1. */ 1144 readerTxn = env.beginTransaction(null, txnConfig); 1145 cursor = db.openCursor(readerTxn, null); 1146 status = cursor.getFirst(key, data, null); 1147 assertEquals(OperationStatus.SUCCESS, status); 1148 assertEquals(1, IntegerBinding.entryToInt(key)); 1149 cursor.close(); 1150 readerTxn.commit(); 1151 1152 closeEnv(); 1153 } 1154 1155 @Test testGetFirst_NotFound_Dup()1156 public void testGetFirst_NotFound_Dup() 1157 throws DatabaseException, InterruptedException { 1158 1159 openEnv(true); 1160 DatabaseEntry key = new DatabaseEntry(); 1161 DatabaseEntry data = new DatabaseEntry(); 1162 OperationStatus status; 1163 1164 /* getFirst returns NOTFOUND. */ 1165 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1166 Cursor cursor = db.openCursor(readerTxn, null); 1167 status = cursor.getFirst(key, data, null); 1168 assertEquals(OperationStatus.NOTFOUND, status); 1169 1170 /* Insert {1,1} in a writer thread. */ 1171 startInsert(1, 1); 1172 1173 /* 1174 * If serializable, getFirst should return NOTFOUND again; otherwise 1175 * getFirst should see {1,1}. 1176 */ 1177 status = cursor.getFirst(key, data, null); 1178 if (txnSerializable) { 1179 assertEquals(OperationStatus.NOTFOUND, status); 1180 } else { 1181 assertEquals(OperationStatus.SUCCESS, status); 1182 assertEquals(1, IntegerBinding.entryToInt(key)); 1183 assertEquals(1, IntegerBinding.entryToInt(data)); 1184 } 1185 1186 /* Close reader to allow writer to finish. */ 1187 cursor.close(); 1188 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1189 waitForInsert(); 1190 1191 /* getFirst returns {1,1}. */ 1192 readerTxn = env.beginTransaction(null, txnConfig); 1193 cursor = db.openCursor(readerTxn, null); 1194 status = cursor.getFirst(key, data, null); 1195 assertEquals(OperationStatus.SUCCESS, status); 1196 assertEquals(1, IntegerBinding.entryToInt(key)); 1197 cursor.close(); 1198 readerTxn.commit(); 1199 1200 closeEnv(); 1201 } 1202 1203 @Test testGetLast_Success()1204 public void testGetLast_Success() 1205 throws DatabaseException, InterruptedException { 1206 1207 openEnv(false); 1208 DatabaseEntry key = new DatabaseEntry(); 1209 DatabaseEntry data = new DatabaseEntry(); 1210 OperationStatus status; 1211 1212 /* Insert key 1. */ 1213 insert(1); 1214 1215 /* getLast returns key 1. */ 1216 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1217 Cursor cursor = db.openCursor(readerTxn, null); 1218 status = cursor.getLast(key, data, null); 1219 assertEquals(OperationStatus.SUCCESS, status); 1220 assertEquals(1, IntegerBinding.entryToInt(key)); 1221 1222 /* Insertions before current position are never blocked. */ 1223 try { 1224 insert(0); 1225 } catch (LockConflictException e) { 1226 fail(); 1227 } 1228 1229 /* Insert key 2 in a writer thread. */ 1230 startInsert(2); 1231 1232 /* 1233 * If serializable, getLast should return key 1 again; otherwise 1234 * getLast should see key 2. 1235 */ 1236 status = cursor.getLast(key, data, null); 1237 assertEquals(OperationStatus.SUCCESS, status); 1238 if (txnSerializable) { 1239 assertEquals(1, IntegerBinding.entryToInt(key)); 1240 } else { 1241 assertEquals(2, IntegerBinding.entryToInt(key)); 1242 } 1243 1244 /* Close reader to allow writer to finish. */ 1245 cursor.close(); 1246 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1247 waitForInsert(); 1248 1249 /* getLast returns key 2. */ 1250 readerTxn = env.beginTransaction(null, txnConfig); 1251 cursor = db.openCursor(readerTxn, null); 1252 status = cursor.getLast(key, data, null); 1253 assertEquals(OperationStatus.SUCCESS, status); 1254 assertEquals(2, IntegerBinding.entryToInt(key)); 1255 cursor.close(); 1256 readerTxn.commit(); 1257 1258 closeEnv(); 1259 } 1260 1261 @Test testGetLast_Success_Dup()1262 public void testGetLast_Success_Dup() 1263 throws DatabaseException, InterruptedException { 1264 1265 openEnv(true); 1266 DatabaseEntry key = new DatabaseEntry(); 1267 DatabaseEntry data = new DatabaseEntry(); 1268 OperationStatus status; 1269 1270 /* Insert dups. */ 1271 insert(1, 0); 1272 insert(1, 2); 1273 1274 /* getLast returns {1,2}. */ 1275 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1276 Cursor cursor = db.openCursor(readerTxn, null); 1277 status = cursor.getLast(key, data, null); 1278 assertEquals(OperationStatus.SUCCESS, status); 1279 assertEquals(1, IntegerBinding.entryToInt(key)); 1280 assertEquals(2, IntegerBinding.entryToInt(data)); 1281 1282 /* Insertions before current position are never blocked. */ 1283 try { 1284 insert(1, 1); 1285 insert(0, 0); 1286 } catch (LockConflictException e) { 1287 fail(); 1288 } 1289 1290 /* Insert {1,3} in a writer thread. */ 1291 startInsert(1, 3); 1292 1293 /* 1294 * If serializable, getLast should return {1,2} again; otherwise 1295 * getLast should see {1,3}. 1296 */ 1297 status = cursor.getLast(key, data, null); 1298 assertEquals(OperationStatus.SUCCESS, status); 1299 if (txnSerializable) { 1300 assertEquals(1, IntegerBinding.entryToInt(key)); 1301 assertEquals(2, IntegerBinding.entryToInt(data)); 1302 } else { 1303 assertEquals(1, IntegerBinding.entryToInt(key)); 1304 assertEquals(3, IntegerBinding.entryToInt(data)); 1305 } 1306 1307 /* Close reader to allow writer to finish. */ 1308 cursor.close(); 1309 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1310 waitForInsert(); 1311 1312 /* getLast returns {1,3}. */ 1313 readerTxn = env.beginTransaction(null, txnConfig); 1314 cursor = db.openCursor(readerTxn, null); 1315 status = cursor.getLast(key, data, null); 1316 assertEquals(OperationStatus.SUCCESS, status); 1317 assertEquals(1, IntegerBinding.entryToInt(key)); 1318 assertEquals(3, IntegerBinding.entryToInt(data)); 1319 cursor.close(); 1320 readerTxn.commit(); 1321 1322 closeEnv(); 1323 } 1324 1325 @Test testGetLast_NotFound()1326 public void testGetLast_NotFound() 1327 throws DatabaseException, InterruptedException { 1328 1329 openEnv(false); 1330 DatabaseEntry key = new DatabaseEntry(); 1331 DatabaseEntry data = new DatabaseEntry(); 1332 OperationStatus status; 1333 1334 /* getLast returns NOTFOUND. */ 1335 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1336 Cursor cursor = db.openCursor(readerTxn, null); 1337 status = cursor.getLast(key, data, null); 1338 assertEquals(OperationStatus.NOTFOUND, status); 1339 1340 /* Insert key 1 in a writer thread. */ 1341 startInsert(1); 1342 1343 /* 1344 * If serializable, getLast should return NOTFOUND again; otherwise 1345 * getLast should see key 1. 1346 */ 1347 status = cursor.getLast(key, data, null); 1348 if (txnSerializable) { 1349 assertEquals(OperationStatus.NOTFOUND, status); 1350 } else { 1351 assertEquals(OperationStatus.SUCCESS, status); 1352 assertEquals(1, IntegerBinding.entryToInt(key)); 1353 } 1354 1355 /* Close reader to allow writer to finish. */ 1356 cursor.close(); 1357 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1358 waitForInsert(); 1359 1360 /* getLast returns key 1. */ 1361 readerTxn = env.beginTransaction(null, txnConfig); 1362 cursor = db.openCursor(readerTxn, null); 1363 status = cursor.getLast(key, data, null); 1364 assertEquals(OperationStatus.SUCCESS, status); 1365 assertEquals(1, IntegerBinding.entryToInt(key)); 1366 cursor.close(); 1367 readerTxn.commit(); 1368 1369 closeEnv(); 1370 } 1371 1372 @Test testGetLast_NotFound_Dup()1373 public void testGetLast_NotFound_Dup() 1374 throws DatabaseException, InterruptedException { 1375 1376 openEnv(true); 1377 DatabaseEntry key = new DatabaseEntry(); 1378 DatabaseEntry data = new DatabaseEntry(); 1379 OperationStatus status; 1380 1381 /* getLast returns NOTFOUND. */ 1382 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1383 Cursor cursor = db.openCursor(readerTxn, null); 1384 status = cursor.getLast(key, data, null); 1385 assertEquals(OperationStatus.NOTFOUND, status); 1386 1387 /* Insert {1,1} in a writer thread. */ 1388 startInsert(1, 1); 1389 1390 /* 1391 * If serializable, getLast should return NOTFOUND again; otherwise 1392 * getLast should see {1,1}. 1393 */ 1394 status = cursor.getLast(key, data, null); 1395 if (txnSerializable) { 1396 assertEquals(OperationStatus.NOTFOUND, status); 1397 } else { 1398 assertEquals(OperationStatus.SUCCESS, status); 1399 assertEquals(1, IntegerBinding.entryToInt(key)); 1400 assertEquals(1, IntegerBinding.entryToInt(data)); 1401 } 1402 1403 /* Close reader to allow writer to finish. */ 1404 cursor.close(); 1405 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1406 waitForInsert(); 1407 1408 /* getLast returns {1,1}. */ 1409 readerTxn = env.beginTransaction(null, txnConfig); 1410 cursor = db.openCursor(readerTxn, null); 1411 status = cursor.getLast(key, data, null); 1412 assertEquals(OperationStatus.SUCCESS, status); 1413 assertEquals(1, IntegerBinding.entryToInt(key)); 1414 assertEquals(1, IntegerBinding.entryToInt(data)); 1415 cursor.close(); 1416 readerTxn.commit(); 1417 1418 closeEnv(); 1419 } 1420 1421 @Test testGetNext_Success()1422 public void testGetNext_Success() 1423 throws DatabaseException, InterruptedException { 1424 1425 openEnv(false); 1426 DatabaseEntry key = new DatabaseEntry(); 1427 DatabaseEntry data = new DatabaseEntry(); 1428 OperationStatus status; 1429 1430 /* Insert key 1 and 3. */ 1431 insert(1); 1432 insert(3); 1433 1434 /* getNext returns key 3. */ 1435 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1436 Cursor cursor = db.openCursor(readerTxn, null); 1437 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1438 status = cursor.getNext(key, data, null); 1439 assertEquals(OperationStatus.SUCCESS, status); 1440 assertEquals(3, IntegerBinding.entryToInt(key)); 1441 1442 /* Insertions before 1 and after 3 are never blocked. */ 1443 try { 1444 insert(0); 1445 insert(4); 1446 } catch (LockConflictException e) { 1447 fail(); 1448 } 1449 1450 /* Insert key 2 in a writer thread. */ 1451 startInsert(2); 1452 1453 /* 1454 * If serializable, getNext should return key 3 again; otherwise 1455 * getNext should see key 2. 1456 */ 1457 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1458 status = cursor.getNext(key, data, null); 1459 assertEquals(OperationStatus.SUCCESS, status); 1460 if (txnSerializable) { 1461 assertEquals(3, IntegerBinding.entryToInt(key)); 1462 } else { 1463 assertEquals(2, IntegerBinding.entryToInt(key)); 1464 } 1465 1466 /* Close reader to allow writer to finish. */ 1467 cursor.close(); 1468 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1469 waitForInsert(); 1470 1471 /* getNext returns key 2. */ 1472 readerTxn = env.beginTransaction(null, txnConfig); 1473 cursor = db.openCursor(readerTxn, null); 1474 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1475 status = cursor.getNext(key, data, null); 1476 assertEquals(OperationStatus.SUCCESS, status); 1477 assertEquals(2, IntegerBinding.entryToInt(key)); 1478 cursor.close(); 1479 readerTxn.commit(); 1480 1481 closeEnv(); 1482 } 1483 1484 @Test testGetNext_Success_Dup()1485 public void testGetNext_Success_Dup() 1486 throws DatabaseException, InterruptedException { 1487 1488 openEnv(true); 1489 DatabaseEntry key = new DatabaseEntry(); 1490 DatabaseEntry data = new DatabaseEntry(); 1491 OperationStatus status; 1492 1493 /* Insert dups. */ 1494 insert(1, 1); 1495 insert(1, 3); 1496 1497 /* getNext returns {1,3}. */ 1498 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1499 Cursor cursor = db.openCursor(readerTxn, null); 1500 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 1501 status = cursor.getNext(key, data, null); 1502 assertEquals(OperationStatus.SUCCESS, status); 1503 assertEquals(1, IntegerBinding.entryToInt(key)); 1504 assertEquals(3, IntegerBinding.entryToInt(data)); 1505 1506 /* Insertions before {1,1} and after {1,3} are never blocked. */ 1507 try { 1508 insert(1, 0); 1509 insert(0, 0); 1510 insert(1, 4); 1511 insert(2, 0); 1512 } catch (LockConflictException e) { 1513 fail(); 1514 } 1515 1516 /* Insert {1,2} in a writer thread. */ 1517 startInsert(1, 2); 1518 1519 /* 1520 * If serializable, getNext should return {1,3} again; otherwise 1521 * getNext should see {1,2}. 1522 */ 1523 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 1524 status = cursor.getNext(key, data, null); 1525 assertEquals(OperationStatus.SUCCESS, status); 1526 if (txnSerializable) { 1527 assertEquals(1, IntegerBinding.entryToInt(key)); 1528 assertEquals(3, IntegerBinding.entryToInt(data)); 1529 } else { 1530 assertEquals(1, IntegerBinding.entryToInt(key)); 1531 assertEquals(2, IntegerBinding.entryToInt(data)); 1532 } 1533 1534 /* Close reader to allow writer to finish. */ 1535 cursor.close(); 1536 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1537 waitForInsert(); 1538 1539 /* getNext returns {1,2}. */ 1540 readerTxn = env.beginTransaction(null, txnConfig); 1541 cursor = db.openCursor(readerTxn, null); 1542 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 1543 status = cursor.getNext(key, data, null); 1544 assertEquals(OperationStatus.SUCCESS, status); 1545 assertEquals(1, IntegerBinding.entryToInt(key)); 1546 assertEquals(2, IntegerBinding.entryToInt(data)); 1547 cursor.close(); 1548 readerTxn.commit(); 1549 1550 closeEnv(); 1551 } 1552 1553 @Test testGetNext_NotFound()1554 public void testGetNext_NotFound() 1555 throws DatabaseException, InterruptedException { 1556 1557 openEnv(false); 1558 DatabaseEntry key = new DatabaseEntry(); 1559 DatabaseEntry data = new DatabaseEntry(); 1560 OperationStatus status; 1561 1562 /* Insert key 1. */ 1563 insert(1); 1564 1565 /* getNext returns NOTFOUND. */ 1566 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1567 Cursor cursor = db.openCursor(readerTxn, null); 1568 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1569 status = cursor.getNext(key, data, null); 1570 assertEquals(OperationStatus.NOTFOUND, status); 1571 1572 /* Insertions before 1 are never blocked. */ 1573 try { 1574 insert(0); 1575 } catch (LockConflictException e) { 1576 fail(); 1577 } 1578 1579 /* Insert key 2 in a writer thread. */ 1580 startInsert(2); 1581 1582 /* 1583 * If serializable, getNext should return NOTFOUND again; otherwise 1584 * getNext should see key 2. 1585 */ 1586 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1587 status = cursor.getNext(key, data, null); 1588 if (txnSerializable) { 1589 assertEquals(OperationStatus.NOTFOUND, status); 1590 } else { 1591 assertEquals(OperationStatus.SUCCESS, status); 1592 assertEquals(2, IntegerBinding.entryToInt(key)); 1593 } 1594 1595 /* Close reader to allow writer to finish. */ 1596 cursor.close(); 1597 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1598 waitForInsert(); 1599 1600 /* getNext returns key 2. */ 1601 readerTxn = env.beginTransaction(null, txnConfig); 1602 cursor = db.openCursor(readerTxn, null); 1603 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1604 status = cursor.getNext(key, data, null); 1605 assertEquals(OperationStatus.SUCCESS, status); 1606 assertEquals(2, IntegerBinding.entryToInt(key)); 1607 cursor.close(); 1608 readerTxn.commit(); 1609 1610 closeEnv(); 1611 } 1612 1613 @Test testGetNext_NotFound_Dup()1614 public void testGetNext_NotFound_Dup() 1615 throws DatabaseException, InterruptedException { 1616 1617 openEnv(true); 1618 DatabaseEntry key = new DatabaseEntry(); 1619 DatabaseEntry data = new DatabaseEntry(); 1620 OperationStatus status; 1621 1622 /* Insert dups. */ 1623 insert(1, 1); 1624 insert(1, 2); 1625 1626 /* getNext returns NOTFOUND. */ 1627 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1628 Cursor cursor = db.openCursor(readerTxn, null); 1629 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 2)); 1630 status = cursor.getNext(key, data, null); 1631 assertEquals(OperationStatus.NOTFOUND, status); 1632 1633 /* Insertions before {1,1} are never blocked. */ 1634 try { 1635 insert(1, 0); 1636 insert(0, 0); 1637 } catch (LockConflictException e) { 1638 fail(); 1639 } 1640 1641 /* Insert {1,3} in a writer thread. */ 1642 startInsert(1, 3); 1643 1644 /* 1645 * If serializable, getNext should return NOTFOUND again; otherwise 1646 * getNext should see {1,3}. 1647 */ 1648 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 2)); 1649 status = cursor.getNext(key, data, null); 1650 if (txnSerializable) { 1651 assertEquals(OperationStatus.NOTFOUND, status); 1652 } else { 1653 assertEquals(OperationStatus.SUCCESS, status); 1654 assertEquals(1, IntegerBinding.entryToInt(key)); 1655 assertEquals(3, IntegerBinding.entryToInt(data)); 1656 } 1657 1658 /* Close reader to allow writer to finish. */ 1659 cursor.close(); 1660 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1661 waitForInsert(); 1662 1663 /* getNext returns {1,3}. */ 1664 readerTxn = env.beginTransaction(null, txnConfig); 1665 cursor = db.openCursor(readerTxn, null); 1666 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 2)); 1667 status = cursor.getNext(key, data, null); 1668 assertEquals(OperationStatus.SUCCESS, status); 1669 assertEquals(1, IntegerBinding.entryToInt(key)); 1670 assertEquals(3, IntegerBinding.entryToInt(data)); 1671 cursor.close(); 1672 readerTxn.commit(); 1673 1674 closeEnv(); 1675 } 1676 1677 @Test testGetNextDup_Success_Dup()1678 public void testGetNextDup_Success_Dup() 1679 throws DatabaseException, InterruptedException { 1680 1681 openEnv(true); 1682 DatabaseEntry key = new DatabaseEntry(); 1683 DatabaseEntry data = new DatabaseEntry(); 1684 OperationStatus status; 1685 1686 /* Insert dups. */ 1687 insert(1, 1); 1688 insert(1, 3); 1689 1690 /* getNextDup returns {1,3}. */ 1691 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1692 Cursor cursor = db.openCursor(readerTxn, null); 1693 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 1694 status = cursor.getNextDup(key, data, null); 1695 assertEquals(OperationStatus.SUCCESS, status); 1696 assertEquals(1, IntegerBinding.entryToInt(key)); 1697 assertEquals(3, IntegerBinding.entryToInt(data)); 1698 1699 /* Insertions before {1,1} and after {1,3} are never blocked. */ 1700 try { 1701 insert(1, 0); 1702 insert(0, 0); 1703 insert(1, 4); 1704 insert(2, 0); 1705 } catch (LockConflictException e) { 1706 fail(); 1707 } 1708 1709 /* Insert {1,2} in a writer thread. */ 1710 startInsert(1, 2); 1711 1712 /* 1713 * If serializable, getNextDup should return {1,3} again; otherwise 1714 * getNextDup should see {1,2}. 1715 */ 1716 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 1717 status = cursor.getNextDup(key, data, null); 1718 assertEquals(OperationStatus.SUCCESS, status); 1719 if (txnSerializable) { 1720 assertEquals(1, IntegerBinding.entryToInt(key)); 1721 assertEquals(3, IntegerBinding.entryToInt(data)); 1722 } else { 1723 assertEquals(1, IntegerBinding.entryToInt(key)); 1724 assertEquals(2, IntegerBinding.entryToInt(data)); 1725 } 1726 1727 /* Close reader to allow writer to finish. */ 1728 cursor.close(); 1729 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1730 waitForInsert(); 1731 1732 /* getNextDup returns {1,2}. */ 1733 readerTxn = env.beginTransaction(null, txnConfig); 1734 cursor = db.openCursor(readerTxn, null); 1735 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 1736 status = cursor.getNextDup(key, data, null); 1737 assertEquals(OperationStatus.SUCCESS, status); 1738 assertEquals(1, IntegerBinding.entryToInt(key)); 1739 assertEquals(2, IntegerBinding.entryToInt(data)); 1740 cursor.close(); 1741 readerTxn.commit(); 1742 1743 closeEnv(); 1744 } 1745 1746 @Test testGetNextDup_NotFound_Dup()1747 public void testGetNextDup_NotFound_Dup() 1748 throws DatabaseException, InterruptedException { 1749 1750 openEnv(true); 1751 DatabaseEntry key = new DatabaseEntry(); 1752 DatabaseEntry data = new DatabaseEntry(); 1753 OperationStatus status; 1754 1755 /* Insert dups. */ 1756 insert(1, 1); 1757 insert(1, 2); 1758 insert(2, 1); 1759 insert(2, 2); 1760 1761 /* getNextDup returns NOTFOUND. */ 1762 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1763 Cursor cursor = db.openCursor(readerTxn, null); 1764 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 2)); 1765 status = cursor.getNextDup(key, data, null); 1766 assertEquals(OperationStatus.NOTFOUND, status); 1767 1768 /* Insertions before {1,1} and after {2,2} are never blocked. */ 1769 try { 1770 insert(1, 0); 1771 insert(0, 0); 1772 insert(2, 3); 1773 insert(3, 0); 1774 } catch (LockConflictException e) { 1775 fail(); 1776 } 1777 1778 /* Insert {1,3} in a writer thread. */ 1779 startInsert(1, 3); 1780 1781 /* 1782 * If serializable, getNextDup should return NOTFOUND again; otherwise 1783 * getNextDup should see {1,3}. 1784 */ 1785 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 2)); 1786 status = cursor.getNextDup(key, data, null); 1787 if (txnSerializable) { 1788 assertEquals(OperationStatus.NOTFOUND, status); 1789 } else { 1790 assertEquals(OperationStatus.SUCCESS, status); 1791 assertEquals(1, IntegerBinding.entryToInt(key)); 1792 assertEquals(3, IntegerBinding.entryToInt(data)); 1793 } 1794 1795 /* Close reader to allow writer to finish. */ 1796 cursor.close(); 1797 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1798 waitForInsert(); 1799 1800 /* getNextDup returns {1,3}. */ 1801 readerTxn = env.beginTransaction(null, txnConfig); 1802 cursor = db.openCursor(readerTxn, null); 1803 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 2)); 1804 status = cursor.getNextDup(key, data, null); 1805 assertEquals(OperationStatus.SUCCESS, status); 1806 assertEquals(1, IntegerBinding.entryToInt(key)); 1807 assertEquals(3, IntegerBinding.entryToInt(data)); 1808 cursor.close(); 1809 readerTxn.commit(); 1810 1811 closeEnv(); 1812 } 1813 1814 @Test testGetNextNoDup_Success()1815 public void testGetNextNoDup_Success() 1816 throws DatabaseException, InterruptedException { 1817 1818 openEnv(false); 1819 DatabaseEntry key = new DatabaseEntry(); 1820 DatabaseEntry data = new DatabaseEntry(); 1821 OperationStatus status; 1822 1823 /* Insert key 1 and 3. */ 1824 insert(1); 1825 insert(3); 1826 1827 /* getNextNoDup returns key 3. */ 1828 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1829 Cursor cursor = db.openCursor(readerTxn, null); 1830 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1831 status = cursor.getNextNoDup(key, data, null); 1832 assertEquals(OperationStatus.SUCCESS, status); 1833 assertEquals(3, IntegerBinding.entryToInt(key)); 1834 1835 /* Insertions before 1 and after 3 are never blocked. */ 1836 try { 1837 insert(0); 1838 insert(4); 1839 } catch (LockConflictException e) { 1840 fail(); 1841 } 1842 1843 /* Insert key 2 in a writer thread. */ 1844 startInsert(2); 1845 1846 /* 1847 * If serializable, getNextNoDup should return key 3 again; otherwise 1848 * getNextNoDup should see key 2. 1849 */ 1850 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1851 status = cursor.getNextNoDup(key, data, null); 1852 assertEquals(OperationStatus.SUCCESS, status); 1853 if (txnSerializable) { 1854 assertEquals(3, IntegerBinding.entryToInt(key)); 1855 } else { 1856 assertEquals(2, IntegerBinding.entryToInt(key)); 1857 } 1858 1859 /* Close reader to allow writer to finish. */ 1860 cursor.close(); 1861 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1862 waitForInsert(); 1863 1864 /* getNextNoDup returns key 2. */ 1865 readerTxn = env.beginTransaction(null, txnConfig); 1866 cursor = db.openCursor(readerTxn, null); 1867 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1868 status = cursor.getNextNoDup(key, data, null); 1869 assertEquals(OperationStatus.SUCCESS, status); 1870 assertEquals(2, IntegerBinding.entryToInt(key)); 1871 cursor.close(); 1872 readerTxn.commit(); 1873 1874 closeEnv(); 1875 } 1876 1877 @Test testGetNextNoDup_Success_Dup()1878 public void testGetNextNoDup_Success_Dup() 1879 throws DatabaseException, InterruptedException { 1880 1881 openEnv(true); 1882 DatabaseEntry key = new DatabaseEntry(); 1883 DatabaseEntry data = new DatabaseEntry(); 1884 OperationStatus status; 1885 1886 /* Insert dups. */ 1887 insert(1, 1); 1888 insert(1, 2); 1889 insert(3, 1); 1890 insert(3, 2); 1891 1892 /* getNextNoDup returns {3,1}. */ 1893 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1894 Cursor cursor = db.openCursor(readerTxn, null); 1895 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 1896 status = cursor.getNextNoDup(key, data, null); 1897 assertEquals(OperationStatus.SUCCESS, status); 1898 assertEquals(3, IntegerBinding.entryToInt(key)); 1899 assertEquals(1, IntegerBinding.entryToInt(data)); 1900 1901 /* Insertions before {1,1} and after {3,2} are never blocked. */ 1902 try { 1903 insert(1, 0); 1904 insert(0, 0); 1905 insert(3, 3); 1906 insert(4, 0); 1907 } catch (LockConflictException e) { 1908 fail(); 1909 } 1910 1911 /* Insert {2,1} in a writer thread. */ 1912 startInsert(2, 1); 1913 1914 /* 1915 * If serializable, getNextNoDup should return {3,1} again; otherwise 1916 * getNextNoDup should see {2,1}. 1917 */ 1918 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 1919 status = cursor.getNextNoDup(key, data, null); 1920 assertEquals(OperationStatus.SUCCESS, status); 1921 if (txnSerializable) { 1922 assertEquals(3, IntegerBinding.entryToInt(key)); 1923 assertEquals(1, IntegerBinding.entryToInt(data)); 1924 } else { 1925 assertEquals(2, IntegerBinding.entryToInt(key)); 1926 assertEquals(1, IntegerBinding.entryToInt(data)); 1927 } 1928 1929 /* Close reader to allow writer to finish. */ 1930 cursor.close(); 1931 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1932 waitForInsert(); 1933 1934 /* getNextNoDup returns {2,1}. */ 1935 readerTxn = env.beginTransaction(null, txnConfig); 1936 cursor = db.openCursor(readerTxn, null); 1937 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 1938 status = cursor.getNextNoDup(key, data, null); 1939 assertEquals(OperationStatus.SUCCESS, status); 1940 assertEquals(2, IntegerBinding.entryToInt(key)); 1941 assertEquals(1, IntegerBinding.entryToInt(data)); 1942 cursor.close(); 1943 readerTxn.commit(); 1944 1945 closeEnv(); 1946 } 1947 1948 @Test testGetNextNoDup_NotFound()1949 public void testGetNextNoDup_NotFound() 1950 throws DatabaseException, InterruptedException { 1951 1952 openEnv(false); 1953 DatabaseEntry key = new DatabaseEntry(); 1954 DatabaseEntry data = new DatabaseEntry(); 1955 OperationStatus status; 1956 1957 /* Insert key 1. */ 1958 insert(1); 1959 1960 /* getNextNoDup returns NOTFOUND. */ 1961 Transaction readerTxn = env.beginTransaction(null, txnConfig); 1962 Cursor cursor = db.openCursor(readerTxn, null); 1963 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1964 status = cursor.getNextNoDup(key, data, null); 1965 assertEquals(OperationStatus.NOTFOUND, status); 1966 1967 /* Insertions before 1 are never blocked. */ 1968 try { 1969 insert(0); 1970 } catch (LockConflictException e) { 1971 fail(); 1972 } 1973 1974 /* Insert key 2 in a writer thread. */ 1975 startInsert(2); 1976 1977 /* 1978 * If serializable, getNextNoDup should return NOTFOUND again; 1979 * otherwise getNextNoDup should see key 2. 1980 */ 1981 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1982 status = cursor.getNextNoDup(key, data, null); 1983 if (txnSerializable) { 1984 assertEquals(OperationStatus.NOTFOUND, status); 1985 } else { 1986 assertEquals(OperationStatus.SUCCESS, status); 1987 assertEquals(2, IntegerBinding.entryToInt(key)); 1988 } 1989 1990 /* Close reader to allow writer to finish. */ 1991 cursor.close(); 1992 readerTxn.commit(Durability.COMMIT_NO_SYNC); 1993 waitForInsert(); 1994 1995 /* getNextNoDup returns key 2. */ 1996 readerTxn = env.beginTransaction(null, txnConfig); 1997 cursor = db.openCursor(readerTxn, null); 1998 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1)); 1999 status = cursor.getNextNoDup(key, data, null); 2000 assertEquals(OperationStatus.SUCCESS, status); 2001 assertEquals(2, IntegerBinding.entryToInt(key)); 2002 cursor.close(); 2003 readerTxn.commit(); 2004 2005 closeEnv(); 2006 } 2007 2008 @Test testGetNextNoDup_NotFound_Dup()2009 public void testGetNextNoDup_NotFound_Dup() 2010 throws DatabaseException, InterruptedException { 2011 2012 openEnv(true); 2013 DatabaseEntry key = new DatabaseEntry(); 2014 DatabaseEntry data = new DatabaseEntry(); 2015 OperationStatus status; 2016 2017 /* Insert dups. */ 2018 insert(1, 1); 2019 insert(1, 2); 2020 2021 /* getNextNoDup returns NOTFOUND. */ 2022 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2023 Cursor cursor = db.openCursor(readerTxn, null); 2024 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 2025 status = cursor.getNextNoDup(key, data, null); 2026 assertEquals(OperationStatus.NOTFOUND, status); 2027 2028 /* Insertions before {1,1} are never blocked. */ 2029 try { 2030 insert(1, 0); 2031 insert(0, 0); 2032 } catch (LockConflictException e) { 2033 fail(); 2034 } 2035 2036 /* Insert {2,1} in a writer thread. */ 2037 startInsert(2, 1); 2038 2039 /* 2040 * If serializable, getNextNoDup should return NOTFOUND again; 2041 * otherwise getNextNoDup should see {2,1}. 2042 */ 2043 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 2044 status = cursor.getNextNoDup(key, data, null); 2045 if (txnSerializable) { 2046 assertEquals(OperationStatus.NOTFOUND, status); 2047 } else { 2048 assertEquals(OperationStatus.SUCCESS, status); 2049 assertEquals(2, IntegerBinding.entryToInt(key)); 2050 assertEquals(1, IntegerBinding.entryToInt(data)); 2051 } 2052 2053 /* Close reader to allow writer to finish. */ 2054 cursor.close(); 2055 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2056 waitForInsert(); 2057 2058 /* getNextNoDup returns {2,1}. */ 2059 readerTxn = env.beginTransaction(null, txnConfig); 2060 cursor = db.openCursor(readerTxn, null); 2061 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 2062 status = cursor.getNextNoDup(key, data, null); 2063 assertEquals(OperationStatus.SUCCESS, status); 2064 assertEquals(2, IntegerBinding.entryToInt(key)); 2065 assertEquals(1, IntegerBinding.entryToInt(data)); 2066 cursor.close(); 2067 readerTxn.commit(); 2068 2069 closeEnv(); 2070 } 2071 2072 @Test testGetPrev_Success()2073 public void testGetPrev_Success() 2074 throws DatabaseException, InterruptedException { 2075 2076 openEnv(false); 2077 DatabaseEntry key = new DatabaseEntry(); 2078 DatabaseEntry data = new DatabaseEntry(); 2079 OperationStatus status; 2080 2081 /* Insert key 1 and 3. */ 2082 insert(1); 2083 insert(3); 2084 2085 /* getPrev returns key 1. */ 2086 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2087 Cursor cursor = db.openCursor(readerTxn, null); 2088 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 3)); 2089 status = cursor.getPrev(key, data, null); 2090 assertEquals(OperationStatus.SUCCESS, status); 2091 assertEquals(1, IntegerBinding.entryToInt(key)); 2092 2093 /* Insertions before 1 and after 3 are never blocked. */ 2094 try { 2095 insert(0); 2096 insert(4); 2097 } catch (LockConflictException e) { 2098 fail(); 2099 } 2100 2101 /* Insert key 2 in a writer thread. */ 2102 startInsert(2); 2103 2104 /* 2105 * If serializable, getPrev should return key 1 again; otherwise 2106 * getPrev should see key 2. 2107 */ 2108 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 3)); 2109 status = cursor.getPrev(key, data, null); 2110 assertEquals(OperationStatus.SUCCESS, status); 2111 if (txnSerializable) { 2112 assertEquals(1, IntegerBinding.entryToInt(key)); 2113 } else { 2114 assertEquals(2, IntegerBinding.entryToInt(key)); 2115 } 2116 2117 /* Close reader to allow writer to finish. */ 2118 cursor.close(); 2119 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2120 waitForInsert(); 2121 2122 /* getPrev returns key 2. */ 2123 readerTxn = env.beginTransaction(null, txnConfig); 2124 cursor = db.openCursor(readerTxn, null); 2125 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 3)); 2126 status = cursor.getPrev(key, data, null); 2127 assertEquals(OperationStatus.SUCCESS, status); 2128 assertEquals(2, IntegerBinding.entryToInt(key)); 2129 cursor.close(); 2130 readerTxn.commit(); 2131 2132 closeEnv(); 2133 } 2134 2135 @Test testGetPrev_Success_Dup()2136 public void testGetPrev_Success_Dup() 2137 throws DatabaseException, InterruptedException { 2138 2139 openEnv(true); 2140 DatabaseEntry key = new DatabaseEntry(); 2141 DatabaseEntry data = new DatabaseEntry(); 2142 OperationStatus status; 2143 2144 /* Insert dups. */ 2145 insert(1, 1); 2146 insert(1, 3); 2147 2148 /* getPrev returns {1,1}. */ 2149 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2150 Cursor cursor = db.openCursor(readerTxn, null); 2151 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 3)); 2152 status = cursor.getPrev(key, data, null); 2153 assertEquals(OperationStatus.SUCCESS, status); 2154 assertEquals(1, IntegerBinding.entryToInt(key)); 2155 assertEquals(1, IntegerBinding.entryToInt(data)); 2156 2157 /* Insertions before {1,1} and after {1,3} are never blocked. */ 2158 try { 2159 insert(1, 0); 2160 insert(0, 0); 2161 insert(1, 4); 2162 insert(2, 0); 2163 } catch (LockConflictException e) { 2164 fail(); 2165 } 2166 2167 /* Insert {1,2} in a writer thread. */ 2168 startInsert(1, 2); 2169 2170 /* 2171 * If serializable, getPrev should return {1,1} again; otherwise 2172 * getPrev should see {1,2}. 2173 */ 2174 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 3)); 2175 status = cursor.getPrev(key, data, null); 2176 assertEquals(OperationStatus.SUCCESS, status); 2177 if (txnSerializable) { 2178 assertEquals(1, IntegerBinding.entryToInt(key)); 2179 assertEquals(1, IntegerBinding.entryToInt(data)); 2180 } else { 2181 assertEquals(1, IntegerBinding.entryToInt(key)); 2182 assertEquals(2, IntegerBinding.entryToInt(data)); 2183 } 2184 2185 /* Close reader to allow writer to finish. */ 2186 cursor.close(); 2187 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2188 waitForInsert(); 2189 2190 /* getPrev returns {1,2}. */ 2191 readerTxn = env.beginTransaction(null, txnConfig); 2192 cursor = db.openCursor(readerTxn, null); 2193 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 3)); 2194 status = cursor.getPrev(key, data, null); 2195 assertEquals(OperationStatus.SUCCESS, status); 2196 assertEquals(1, IntegerBinding.entryToInt(key)); 2197 assertEquals(2, IntegerBinding.entryToInt(data)); 2198 cursor.close(); 2199 readerTxn.commit(); 2200 2201 closeEnv(); 2202 } 2203 2204 @Test testGetPrev_NotFound()2205 public void testGetPrev_NotFound() 2206 throws DatabaseException, InterruptedException { 2207 2208 openEnv(false); 2209 DatabaseEntry key = new DatabaseEntry(); 2210 DatabaseEntry data = new DatabaseEntry(); 2211 OperationStatus status; 2212 2213 /* Insert key 2. */ 2214 insert(2); 2215 2216 /* getPrev returns NOTFOUND. */ 2217 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2218 Cursor cursor = db.openCursor(readerTxn, null); 2219 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2)); 2220 status = cursor.getPrev(key, data, null); 2221 assertEquals(OperationStatus.NOTFOUND, status); 2222 2223 /* Insertions after 2 are never blocked. */ 2224 try { 2225 insert(3); 2226 } catch (LockConflictException e) { 2227 fail(); 2228 } 2229 2230 /* Insert key 1 in a writer thread. */ 2231 startInsert(1); 2232 2233 /* 2234 * If serializable, getPrev should return NOTFOUND again; otherwise 2235 * getPrev should see key 1. 2236 */ 2237 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2)); 2238 status = cursor.getPrev(key, data, null); 2239 if (txnSerializable) { 2240 assertEquals(OperationStatus.NOTFOUND, status); 2241 } else { 2242 assertEquals(OperationStatus.SUCCESS, status); 2243 assertEquals(1, IntegerBinding.entryToInt(key)); 2244 } 2245 2246 /* Close reader to allow writer to finish. */ 2247 cursor.close(); 2248 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2249 waitForInsert(); 2250 2251 /* getPrev returns key 1. */ 2252 readerTxn = env.beginTransaction(null, txnConfig); 2253 cursor = db.openCursor(readerTxn, null); 2254 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2)); 2255 status = cursor.getPrev(key, data, null); 2256 assertEquals(OperationStatus.SUCCESS, status); 2257 assertEquals(1, IntegerBinding.entryToInt(key)); 2258 cursor.close(); 2259 readerTxn.commit(); 2260 2261 closeEnv(); 2262 } 2263 2264 @Test testGetPrev_NotFound_Dup()2265 public void testGetPrev_NotFound_Dup() 2266 throws DatabaseException, InterruptedException { 2267 2268 openEnv(true); 2269 DatabaseEntry key = new DatabaseEntry(); 2270 DatabaseEntry data = new DatabaseEntry(); 2271 OperationStatus status; 2272 2273 /* Insert dups. */ 2274 insert(2, 2); 2275 insert(2, 3); 2276 2277 /* getPrev returns NOTFOUND. */ 2278 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2279 Cursor cursor = db.openCursor(readerTxn, null); 2280 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 2)); 2281 status = cursor.getPrev(key, data, null); 2282 assertEquals(OperationStatus.NOTFOUND, status); 2283 2284 /* Insertions after {2,3} are never blocked. */ 2285 try { 2286 insert(2, 4); 2287 insert(3, 0); 2288 } catch (LockConflictException e) { 2289 fail(); 2290 } 2291 2292 /* Insert {2,1} in a writer thread. */ 2293 startInsert(2, 1); 2294 2295 /* 2296 * If serializable, getPrev should return NOTFOUND again; otherwise 2297 * getPrev should see {2,1}. 2298 */ 2299 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 2)); 2300 status = cursor.getPrev(key, data, null); 2301 if (txnSerializable) { 2302 assertEquals(OperationStatus.NOTFOUND, status); 2303 } else { 2304 assertEquals(OperationStatus.SUCCESS, status); 2305 assertEquals(2, IntegerBinding.entryToInt(key)); 2306 assertEquals(1, IntegerBinding.entryToInt(data)); 2307 } 2308 2309 /* Close reader to allow writer to finish. */ 2310 cursor.close(); 2311 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2312 waitForInsert(); 2313 2314 /* getPrev returns {2,1}. */ 2315 readerTxn = env.beginTransaction(null, txnConfig); 2316 cursor = db.openCursor(readerTxn, null); 2317 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 2)); 2318 status = cursor.getPrev(key, data, null); 2319 assertEquals(OperationStatus.SUCCESS, status); 2320 assertEquals(2, IntegerBinding.entryToInt(key)); 2321 assertEquals(1, IntegerBinding.entryToInt(data)); 2322 cursor.close(); 2323 readerTxn.commit(); 2324 2325 closeEnv(); 2326 } 2327 2328 @Test testGetPrevDup_Success_Dup()2329 public void testGetPrevDup_Success_Dup() 2330 throws DatabaseException, InterruptedException { 2331 2332 openEnv(true); 2333 DatabaseEntry key = new DatabaseEntry(); 2334 DatabaseEntry data = new DatabaseEntry(); 2335 OperationStatus status; 2336 2337 /* Insert dups. */ 2338 insert(1, 1); 2339 insert(1, 3); 2340 2341 /* getPrevDup returns {1,1}. */ 2342 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2343 Cursor cursor = db.openCursor(readerTxn, null); 2344 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 3)); 2345 status = cursor.getPrevDup(key, data, null); 2346 assertEquals(OperationStatus.SUCCESS, status); 2347 assertEquals(1, IntegerBinding.entryToInt(key)); 2348 assertEquals(1, IntegerBinding.entryToInt(data)); 2349 2350 /* Insertions before {1,1} and after {1,3} are never blocked. */ 2351 try { 2352 insert(1, 0); 2353 insert(0, 0); 2354 insert(1, 4); 2355 insert(2, 0); 2356 } catch (LockConflictException e) { 2357 fail(); 2358 } 2359 2360 /* Insert {1,2} in a writer thread. */ 2361 startInsert(1, 2); 2362 2363 /* 2364 * If serializable, getPrevDup should return {1,1} again; otherwise 2365 * getPrevDup should see {1,2}. 2366 */ 2367 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 3)); 2368 status = cursor.getPrevDup(key, data, null); 2369 assertEquals(OperationStatus.SUCCESS, status); 2370 if (txnSerializable) { 2371 assertEquals(1, IntegerBinding.entryToInt(key)); 2372 assertEquals(1, IntegerBinding.entryToInt(data)); 2373 } else { 2374 assertEquals(1, IntegerBinding.entryToInt(key)); 2375 assertEquals(2, IntegerBinding.entryToInt(data)); 2376 } 2377 2378 /* Close reader to allow writer to finish. */ 2379 cursor.close(); 2380 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2381 waitForInsert(); 2382 2383 /* getPrevDup returns {1,2}. */ 2384 readerTxn = env.beginTransaction(null, txnConfig); 2385 cursor = db.openCursor(readerTxn, null); 2386 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 3)); 2387 status = cursor.getPrevDup(key, data, null); 2388 assertEquals(OperationStatus.SUCCESS, status); 2389 assertEquals(1, IntegerBinding.entryToInt(key)); 2390 assertEquals(2, IntegerBinding.entryToInt(data)); 2391 cursor.close(); 2392 readerTxn.commit(); 2393 2394 closeEnv(); 2395 } 2396 2397 @Test testGetPrevDup_NotFound_Dup()2398 public void testGetPrevDup_NotFound_Dup() 2399 throws DatabaseException, InterruptedException { 2400 2401 openEnv(true); 2402 DatabaseEntry key = new DatabaseEntry(); 2403 DatabaseEntry data = new DatabaseEntry(); 2404 OperationStatus status; 2405 2406 /* Insert dups. */ 2407 insert(2, 2); 2408 insert(2, 3); 2409 2410 /* getPrevDup returns NOTFOUND. */ 2411 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2412 Cursor cursor = db.openCursor(readerTxn, null); 2413 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 2)); 2414 status = cursor.getPrevDup(key, data, null); 2415 assertEquals(OperationStatus.NOTFOUND, status); 2416 2417 /* Insertions after {2,3} are never blocked. */ 2418 try { 2419 insert(2, 4); 2420 insert(3, 0); 2421 } catch (LockConflictException e) { 2422 fail(); 2423 } 2424 2425 /* Insert {2,1} in a writer thread. */ 2426 startInsert(2, 1); 2427 2428 /* 2429 * If serializable, getPrevDup should return NOTFOUND again; otherwise 2430 * getPrevDup should see {2,1}. 2431 */ 2432 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 2)); 2433 status = cursor.getPrevDup(key, data, null); 2434 if (txnSerializable) { 2435 assertEquals(OperationStatus.NOTFOUND, status); 2436 } else { 2437 assertEquals(OperationStatus.SUCCESS, status); 2438 assertEquals(2, IntegerBinding.entryToInt(key)); 2439 assertEquals(1, IntegerBinding.entryToInt(data)); 2440 } 2441 2442 /* Close reader to allow writer to finish. */ 2443 cursor.close(); 2444 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2445 waitForInsert(); 2446 2447 /* getPrevDup returns {2,1}. */ 2448 readerTxn = env.beginTransaction(null, txnConfig); 2449 cursor = db.openCursor(readerTxn, null); 2450 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 2)); 2451 status = cursor.getPrevDup(key, data, null); 2452 assertEquals(OperationStatus.SUCCESS, status); 2453 assertEquals(2, IntegerBinding.entryToInt(key)); 2454 assertEquals(1, IntegerBinding.entryToInt(data)); 2455 cursor.close(); 2456 readerTxn.commit(); 2457 2458 closeEnv(); 2459 } 2460 2461 @Test testGetPrevNoDup_Success()2462 public void testGetPrevNoDup_Success() 2463 throws DatabaseException, InterruptedException { 2464 2465 openEnv(false); 2466 DatabaseEntry key = new DatabaseEntry(); 2467 DatabaseEntry data = new DatabaseEntry(); 2468 OperationStatus status; 2469 2470 /* Insert key 1 and 3. */ 2471 insert(1); 2472 insert(3); 2473 2474 /* getPrevNoDup returns key 1. */ 2475 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2476 Cursor cursor = db.openCursor(readerTxn, null); 2477 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 3)); 2478 status = cursor.getPrevNoDup(key, data, null); 2479 assertEquals(OperationStatus.SUCCESS, status); 2480 assertEquals(1, IntegerBinding.entryToInt(key)); 2481 2482 /* Insertions before 1 and after 3 are never blocked. */ 2483 try { 2484 insert(0); 2485 insert(4); 2486 } catch (LockConflictException e) { 2487 fail(); 2488 } 2489 2490 /* Insert key 2 in a writer thread. */ 2491 startInsert(2); 2492 2493 /* 2494 * If serializable, getPrevNoDup should return key 1 again; otherwise 2495 * getPrevNoDup should see key 2. 2496 */ 2497 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 3)); 2498 status = cursor.getPrevNoDup(key, data, null); 2499 assertEquals(OperationStatus.SUCCESS, status); 2500 if (txnSerializable) { 2501 assertEquals(1, IntegerBinding.entryToInt(key)); 2502 } else { 2503 assertEquals(2, IntegerBinding.entryToInt(key)); 2504 } 2505 2506 /* Close reader to allow writer to finish. */ 2507 cursor.close(); 2508 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2509 waitForInsert(); 2510 2511 /* getPrevNoDup returns key 2. */ 2512 readerTxn = env.beginTransaction(null, txnConfig); 2513 cursor = db.openCursor(readerTxn, null); 2514 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 3)); 2515 status = cursor.getPrevNoDup(key, data, null); 2516 assertEquals(OperationStatus.SUCCESS, status); 2517 assertEquals(2, IntegerBinding.entryToInt(key)); 2518 cursor.close(); 2519 readerTxn.commit(); 2520 2521 closeEnv(); 2522 } 2523 2524 @Test testGetPrevNoDup_Success_Dup()2525 public void testGetPrevNoDup_Success_Dup() 2526 throws DatabaseException, InterruptedException { 2527 2528 openEnv(true); 2529 DatabaseEntry key = new DatabaseEntry(); 2530 DatabaseEntry data = new DatabaseEntry(); 2531 OperationStatus status; 2532 2533 /* Insert dups. */ 2534 insert(1, 0); 2535 insert(1, 2); 2536 insert(3, 1); 2537 insert(3, 2); 2538 2539 /* getPrevNoDup returns {1,2}. */ 2540 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2541 Cursor cursor = db.openCursor(readerTxn, null); 2542 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 3, 2)); 2543 status = cursor.getPrevNoDup(key, data, null); 2544 assertEquals(OperationStatus.SUCCESS, status); 2545 assertEquals(1, IntegerBinding.entryToInt(key)); 2546 assertEquals(2, IntegerBinding.entryToInt(data)); 2547 2548 /* Insertions before {1,2} and after {3,2} are never blocked. */ 2549 try { 2550 insert(1, 1); 2551 insert(0, 0); 2552 insert(3, 3); 2553 insert(4, 0); 2554 } catch (LockConflictException e) { 2555 fail(); 2556 } 2557 2558 /* Insert {2,1} in a writer thread. */ 2559 startInsert(2, 1); 2560 2561 /* 2562 * If serializable, getPrevNoDup should return {1,2} again; otherwise 2563 * getPrevNoDup should see {2,1}. 2564 */ 2565 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 3, 2)); 2566 status = cursor.getPrevNoDup(key, data, null); 2567 assertEquals(OperationStatus.SUCCESS, status); 2568 if (txnSerializable) { 2569 assertEquals(1, IntegerBinding.entryToInt(key)); 2570 assertEquals(2, IntegerBinding.entryToInt(data)); 2571 } else { 2572 assertEquals(2, IntegerBinding.entryToInt(key)); 2573 assertEquals(1, IntegerBinding.entryToInt(data)); 2574 } 2575 2576 /* Close reader to allow writer to finish. */ 2577 cursor.close(); 2578 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2579 waitForInsert(); 2580 2581 /* getPrevNoDup returns {2,1}. */ 2582 readerTxn = env.beginTransaction(null, txnConfig); 2583 cursor = db.openCursor(readerTxn, null); 2584 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 3, 2)); 2585 status = cursor.getPrevNoDup(key, data, null); 2586 assertEquals(OperationStatus.SUCCESS, status); 2587 assertEquals(2, IntegerBinding.entryToInt(key)); 2588 assertEquals(1, IntegerBinding.entryToInt(data)); 2589 cursor.close(); 2590 readerTxn.commit(); 2591 2592 closeEnv(); 2593 } 2594 2595 @Test testGetPrevNoDup_NotFound()2596 public void testGetPrevNoDup_NotFound() 2597 throws DatabaseException, InterruptedException { 2598 2599 openEnv(false); 2600 DatabaseEntry key = new DatabaseEntry(); 2601 DatabaseEntry data = new DatabaseEntry(); 2602 OperationStatus status; 2603 2604 /* Insert key 2. */ 2605 insert(2); 2606 2607 /* getPrevNoDup returns NOTFOUND. */ 2608 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2609 Cursor cursor = db.openCursor(readerTxn, null); 2610 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2)); 2611 status = cursor.getPrevNoDup(key, data, null); 2612 assertEquals(OperationStatus.NOTFOUND, status); 2613 2614 /* Insertions after 2 are never blocked. */ 2615 try { 2616 insert(3); 2617 } catch (LockConflictException e) { 2618 fail(); 2619 } 2620 2621 /* Insert key 1 in a writer thread. */ 2622 startInsert(1); 2623 2624 /* 2625 * If serializable, getPrevNoDup should return NOTFOUND again; 2626 * otherwise getPrevNoDup should see key 1. 2627 */ 2628 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2)); 2629 status = cursor.getPrevNoDup(key, data, null); 2630 if (txnSerializable) { 2631 assertEquals(OperationStatus.NOTFOUND, status); 2632 } else { 2633 assertEquals(OperationStatus.SUCCESS, status); 2634 assertEquals(1, IntegerBinding.entryToInt(key)); 2635 } 2636 2637 /* Close reader to allow writer to finish. */ 2638 cursor.close(); 2639 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2640 waitForInsert(); 2641 2642 /* getPrevNoDup returns key 1. */ 2643 readerTxn = env.beginTransaction(null, txnConfig); 2644 cursor = db.openCursor(readerTxn, null); 2645 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 2)); 2646 status = cursor.getPrevNoDup(key, data, null); 2647 assertEquals(OperationStatus.SUCCESS, status); 2648 assertEquals(1, IntegerBinding.entryToInt(key)); 2649 cursor.close(); 2650 readerTxn.commit(); 2651 2652 closeEnv(); 2653 } 2654 2655 @Test testGetPrevNoDup_NotFound_Dup()2656 public void testGetPrevNoDup_NotFound_Dup() 2657 throws DatabaseException, InterruptedException { 2658 2659 openEnv(true); 2660 DatabaseEntry key = new DatabaseEntry(); 2661 DatabaseEntry data = new DatabaseEntry(); 2662 OperationStatus status; 2663 2664 /* Insert dups. */ 2665 insert(2, 1); 2666 insert(2, 2); 2667 2668 /* getPrevNoDup returns NOTFOUND. */ 2669 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2670 Cursor cursor = db.openCursor(readerTxn, null); 2671 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 2)); 2672 status = cursor.getPrevNoDup(key, data, null); 2673 assertEquals(OperationStatus.NOTFOUND, status); 2674 2675 /* Insertions after {2,2} are never blocked. */ 2676 try { 2677 insert(2, 3); 2678 insert(3, 0); 2679 } catch (LockConflictException e) { 2680 fail(); 2681 } 2682 2683 /* Insert {1,1} in a writer thread. */ 2684 startInsert(1, 1); 2685 2686 /* 2687 * If serializable, getPrevNoDup should return NOTFOUND again; 2688 * otherwise getPrevNoDup should see {1,1}. 2689 */ 2690 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 2)); 2691 status = cursor.getPrevNoDup(key, data, null); 2692 if (txnSerializable) { 2693 assertEquals(OperationStatus.NOTFOUND, status); 2694 } else { 2695 assertEquals(OperationStatus.SUCCESS, status); 2696 assertEquals(1, IntegerBinding.entryToInt(key)); 2697 assertEquals(1, IntegerBinding.entryToInt(data)); 2698 } 2699 2700 /* Close reader to allow writer to finish. */ 2701 cursor.close(); 2702 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2703 waitForInsert(); 2704 2705 /* getPrevNoDup returns {1,1}. */ 2706 readerTxn = env.beginTransaction(null, txnConfig); 2707 cursor = db.openCursor(readerTxn, null); 2708 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 2)); 2709 status = cursor.getPrevNoDup(key, data, null); 2710 assertEquals(OperationStatus.SUCCESS, status); 2711 assertEquals(1, IntegerBinding.entryToInt(key)); 2712 assertEquals(1, IntegerBinding.entryToInt(data)); 2713 cursor.close(); 2714 readerTxn.commit(); 2715 2716 closeEnv(); 2717 } 2718 2719 @Test testIllegalTransactionConfig()2720 public void testIllegalTransactionConfig() 2721 throws DatabaseException { 2722 2723 openEnv(false); 2724 TransactionConfig config = new TransactionConfig(); 2725 config.setSerializableIsolation(true); 2726 config.setReadUncommitted(true); 2727 try { 2728 Transaction txn = env.beginTransaction(null, config); 2729 txn.abort(); 2730 fail(); 2731 } catch (IllegalArgumentException expected) { 2732 } 2733 closeEnv(); 2734 } 2735 2736 /* 2737 * In other tests we test TransactionConfig.setReadUncommitted and 2738 * TransactionConfig.setSerializableIsolation to make sure they result in 2739 * expected non-serializable or serializable behavior. Below we check 2740 * EnvironmentConfig.setSerializableIsolation, 2741 * CursorConfig.setSerializableIsolation, CursorConfig.setReadUncommitted 2742 * and LockMode.READ_UNCOMMITTED, although for a single test case only. 2743 */ 2744 2745 @Test testEnvironmentConfig()2746 public void testEnvironmentConfig() 2747 throws DatabaseException { 2748 2749 EnvironmentConfig config = TestUtils.initEnvConfig(); 2750 /* Control over isolation level is required by this test. */ 2751 TestUtils.clearIsolationLevel(config); 2752 checkSerializable(false, config, null, null); 2753 2754 config.setTxnSerializableIsolation(true); 2755 checkSerializable(true, config, null, null); 2756 } 2757 2758 @Test testCursorConfig()2759 public void testCursorConfig() 2760 throws DatabaseException { 2761 2762 CursorConfig config = new CursorConfig(); 2763 checkSerializable(false, null, config, null); 2764 2765 config.setReadUncommitted(true); 2766 checkSerializable(false, null, config, null); 2767 } 2768 2769 @Test testReadUncommittedLockMode()2770 public void testReadUncommittedLockMode() 2771 throws DatabaseException { 2772 2773 EnvironmentConfig envConfig = TestUtils.initEnvConfig(); 2774 /* Control over isolation level is required by this test. */ 2775 TestUtils.clearIsolationLevel(envConfig); 2776 envConfig.setTxnSerializableIsolation(true); 2777 2778 checkSerializable(false, envConfig, null, LockMode.READ_UNCOMMITTED); 2779 } 2780 checkSerializable(boolean expectSerializable, EnvironmentConfig envConfig, CursorConfig cursorConfig, LockMode lockMode)2781 private void checkSerializable(boolean expectSerializable, 2782 EnvironmentConfig envConfig, 2783 CursorConfig cursorConfig, 2784 LockMode lockMode) 2785 throws DatabaseException { 2786 2787 openEnv(false, envConfig); 2788 DatabaseEntry key = new DatabaseEntry(); 2789 DatabaseEntry data = new DatabaseEntry(); 2790 OperationStatus status; 2791 2792 /* Insert key 2. */ 2793 insert(2); 2794 2795 /* getFirst returns key 2. */ 2796 Transaction readerTxn = env.beginTransaction(null, null); 2797 Cursor cursor = db.openCursor(readerTxn, cursorConfig); 2798 status = cursor.getFirst(key, data, lockMode); 2799 assertEquals(OperationStatus.SUCCESS, status); 2800 assertEquals(2, IntegerBinding.entryToInt(key)); 2801 2802 /* Should deadlock iff serializable. */ 2803 try { 2804 insert(1); 2805 assertTrue(!expectSerializable); 2806 } catch (LockConflictException e) { 2807 assertTrue(expectSerializable); 2808 } 2809 2810 cursor.close(); 2811 readerTxn.commit(); 2812 2813 /* This method is called multiple times so remove the database. */ 2814 db.close(); 2815 db = null; 2816 env.removeDatabase(null, DB_NAME); 2817 2818 closeEnv(); 2819 } 2820 2821 /** 2822 * Tests that with a single degree 3 txn we don't obtain the extra lock 2823 * during insert. 2824 */ 2825 @Test testSingleDegree3TxnOptimization()2826 public void testSingleDegree3TxnOptimization() 2827 throws DatabaseException { 2828 2829 openEnv(false); 2830 2831 /* Insert key 2. */ 2832 insert(2); 2833 2834 StatsConfig clearStats = new StatsConfig(); 2835 clearStats.setClear(true); 2836 2837 /* Clear before inserting. */ 2838 EnvironmentStats stats = env.getStats(clearStats); 2839 2840 /* Insert key 1, which would lock key 2 while inserting. */ 2841 insert(1); 2842 2843 /* Expect a single lock was requested. */ 2844 stats = env.getStats(clearStats); 2845 assertEquals(1, stats.getNRequests()); 2846 2847 closeEnv(); 2848 } 2849 2850 /** 2851 * Tests a particular getSearchBothRange bug that has come up in several 2852 * contexts. This test is probably redundant with GetSearchBothTest but 2853 * I've left it here for good measure. 2854 */ 2855 @Test testSingleDatumBug()2856 public void testSingleDatumBug() 2857 throws DatabaseException { 2858 2859 openEnv(true); 2860 DatabaseEntry key = new DatabaseEntry(); 2861 DatabaseEntry data = new DatabaseEntry(); 2862 OperationStatus status; 2863 2864 insert(1, 1); 2865 insert(2, 2); 2866 2867 /* getSearchBothRange for {2, 1} returns {2, 2}. */ 2868 Transaction readerTxn = env.beginTransaction(null, txnConfig); 2869 Cursor cursor = db.openCursor(readerTxn, null); 2870 IntegerBinding.intToEntry(2, key); 2871 IntegerBinding.intToEntry(1, data); 2872 status = cursor.getSearchBothRange(key, data, null); 2873 assertEquals(OperationStatus.SUCCESS, status); 2874 assertEquals(2, IntegerBinding.entryToInt(key)); 2875 assertEquals(2, IntegerBinding.entryToInt(data)); 2876 2877 /* If serializable, inserting in the locked range should deadlock. */ 2878 try { 2879 insert(1, 2); 2880 if (txnSerializable) { 2881 fail(); 2882 } 2883 } catch (LockConflictException e) { 2884 if (!txnSerializable) { 2885 fail(); 2886 } 2887 } 2888 2889 cursor.close(); 2890 readerTxn.commit(Durability.COMMIT_NO_SYNC); 2891 closeEnv(); 2892 } 2893 2894 /** 2895 * Tests that searchKey returns SUCCESS when it must skip over a deleted 2896 * duplicate. This did not work at one point and was causing warnings 2897 * (Cursor Not Initialized) in duplicate.conf testing. 2898 */ 2899 @Test testSearchKeySkipDeletedDup()2900 public void testSearchKeySkipDeletedDup() 2901 throws DatabaseException { 2902 2903 openEnv(true); 2904 2905 /* Insert {1,1} and {1,2}. */ 2906 insert(1, 1); 2907 insert(1, 2); 2908 2909 /* Delete {1,1}. */ 2910 Transaction txn = env.beginTransaction(null, txnConfig); 2911 Cursor cursor = db.openCursor(txn, null); 2912 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 1, 1)); 2913 OperationStatus status = cursor.delete(); 2914 assertEquals(OperationStatus.SUCCESS, status); 2915 2916 /* Search for key 1 -- should not return NOTFOUND. */ 2917 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 2)); 2918 2919 cursor.close(); 2920 txn.commit(Durability.COMMIT_NO_SYNC); 2921 closeEnv(); 2922 } 2923 2924 /** 2925 * Tests that getNextDup returns NOTFOUND when it skips over a deleted 2926 * duplicate for the following main key. [#19026] 2927 */ 2928 @Test testNextAfterDupDeleteBug()2929 public void testNextAfterDupDeleteBug() 2930 throws DatabaseException, InterruptedException { 2931 2932 openEnv(true); 2933 DatabaseEntry key = new DatabaseEntry(); 2934 DatabaseEntry data = new DatabaseEntry(); 2935 OperationStatus status; 2936 2937 /* Insert dups. */ 2938 insert(1, 1); 2939 insert(2, 1); 2940 insert(2, 2); 2941 2942 /* Delete {2,1}. */ 2943 Transaction txn = env.beginTransaction(null, txnConfig); 2944 Cursor cursor = db.openCursor(txn, null); 2945 assertEquals(OperationStatus.SUCCESS, searchBoth(cursor, 2, 1)); 2946 assertEquals(OperationStatus.SUCCESS, cursor.delete()); 2947 cursor.close(); 2948 txn.commit(Durability.COMMIT_NO_SYNC); 2949 2950 /* 2951 * When positioned on {1,1}, getNextDup should always return NOTFOUND. 2952 * A bug (fixed in [#19026]) caused the cursor to move to {2,2} and 2953 * return SUCCESS. This only occurred with serializable isolation, and 2954 * when the deleted {2,1} record had not been compressed. The 2955 * underlying cause is that CursorImpl.getNextWithKeyChangeStatus was 2956 * not indicating a key change when skipping over the first deleted 2957 * duplicate ({2,1} in this case) in the duplicate set. 2958 */ 2959 txn = env.beginTransaction(null, txnConfig); 2960 cursor = db.openCursor(txn, null); 2961 assertEquals(OperationStatus.SUCCESS, searchKey(cursor, 1, 1)); 2962 status = cursor.getNextDup(key, data, null); 2963 assertEquals(OperationStatus.NOTFOUND, status); 2964 cursor.close(); 2965 txn.commit(Durability.COMMIT_NO_SYNC); 2966 2967 closeEnv(); 2968 } 2969 2970 /** 2971 * Performs getSearchKey on the given key, expects data to be zero. 2972 */ searchKey(Cursor cursor, int keyVal)2973 private OperationStatus searchKey(Cursor cursor, int keyVal) 2974 throws DatabaseException { 2975 2976 return searchKey(cursor, keyVal, 0); 2977 } 2978 2979 /** 2980 * Performs getSearchKey on the given key, expects given data value. 2981 */ searchKey(Cursor cursor, int keyVal, int dataVal)2982 private OperationStatus searchKey(Cursor cursor, int keyVal, int dataVal) 2983 throws DatabaseException { 2984 2985 DatabaseEntry key = new DatabaseEntry(); 2986 DatabaseEntry data = new DatabaseEntry(); 2987 IntegerBinding.intToEntry(keyVal, key); 2988 OperationStatus status = cursor.getSearchKey(key, data, null); 2989 if (status == OperationStatus.SUCCESS) { 2990 assertEquals(keyVal, IntegerBinding.entryToInt(key)); 2991 assertEquals(dataVal, IntegerBinding.entryToInt(data)); 2992 } 2993 return status; 2994 } 2995 2996 /** 2997 * Performs getSearchBoth on the given key and zero data. 2998 */ searchBoth(Cursor cursor, int keyVal)2999 private OperationStatus searchBoth(Cursor cursor, int keyVal) 3000 throws DatabaseException { 3001 3002 return searchBoth(cursor, keyVal, 0, false); 3003 } 3004 3005 /** 3006 * Performs getSearchBoth on the given key and zero data. 3007 * 3008 * getSearchBoth and getSearchBothRange are equivalent for a non-dup DB, so 3009 * we allowing testing either. 3010 */ searchBoth(Cursor cursor, int keyVal, boolean useRangeSearch)3011 private OperationStatus searchBoth(Cursor cursor, 3012 int keyVal, 3013 boolean useRangeSearch) 3014 throws DatabaseException { 3015 3016 return searchBoth(cursor, keyVal, 0, useRangeSearch); 3017 } 3018 3019 /** 3020 * Performs getSearchBoth on the given key and data. 3021 */ searchBoth(Cursor cursor, int keyVal, int dataVal)3022 private OperationStatus searchBoth(Cursor cursor, int keyVal, int dataVal) 3023 throws DatabaseException { 3024 3025 return searchBoth(cursor, keyVal, dataVal, false); 3026 } 3027 3028 /** 3029 * Performs getSearchBoth on the given key and data. 3030 * 3031 * getSearchBoth and getSearchBothRange are equivalent for a non-dup DB, so 3032 * we allowing testing either. 3033 */ searchBoth(Cursor cursor, int keyVal, int dataVal, boolean useRangeSearch)3034 private OperationStatus searchBoth(Cursor cursor, 3035 int keyVal, 3036 int dataVal, 3037 boolean useRangeSearch) 3038 throws DatabaseException { 3039 3040 DatabaseEntry key = new DatabaseEntry(); 3041 DatabaseEntry data = new DatabaseEntry(); 3042 IntegerBinding.intToEntry(keyVal, key); 3043 IntegerBinding.intToEntry(dataVal, data); 3044 OperationStatus status; 3045 if (useRangeSearch) { 3046 status = cursor.getSearchBothRange(key, data, null); 3047 } else { 3048 status = cursor.getSearchBoth(key, data, null); 3049 } 3050 if (status == OperationStatus.SUCCESS) { 3051 assertEquals(keyVal, IntegerBinding.entryToInt(key)); 3052 assertEquals(dataVal, IntegerBinding.entryToInt(data)); 3053 } 3054 return status; 3055 } 3056 3057 /** 3058 * Inserts the given key in a new transaction and commits it. 3059 */ insert(int keyVal)3060 private void insert(int keyVal) 3061 throws DatabaseException { 3062 3063 insert(keyVal, 0); 3064 } 3065 3066 /** 3067 * Inserts the given key and data in a new transaction and commits it. 3068 */ insert(int keyVal, int dataVal)3069 private void insert(int keyVal, int dataVal) 3070 throws DatabaseException { 3071 3072 DatabaseEntry key = new DatabaseEntry(); 3073 DatabaseEntry data = new DatabaseEntry(); 3074 IntegerBinding.intToEntry(keyVal, key); 3075 IntegerBinding.intToEntry(dataVal, data); 3076 OperationStatus status; 3077 Transaction writerTxn = env.beginTransaction(null, txnConfig); 3078 try { 3079 if (dups) { 3080 status = db.putNoDupData(writerTxn, key, data); 3081 } else { 3082 status = db.putNoOverwrite(writerTxn, key, data); 3083 } 3084 } catch (LockConflictException e) { 3085 writerTxn.abort(); 3086 throw e; 3087 } 3088 assertEquals(OperationStatus.SUCCESS, status); 3089 writerTxn.commit(Durability.COMMIT_NO_SYNC); 3090 } 3091 3092 /** 3093 * Starts writer thread and waits for it to start the insert. 3094 */ startInsert(final int keyVal)3095 private void startInsert(final int keyVal) 3096 throws DatabaseException, InterruptedException { 3097 3098 startInsert(keyVal, 0); 3099 } 3100 3101 /** 3102 * Starts writer thread and waits for it to start the insert. 3103 */ startInsert(final int keyVal, final int dataVal)3104 private void startInsert(final int keyVal, final int dataVal) 3105 throws DatabaseException, InterruptedException { 3106 3107 EnvironmentStats origStats = env.getStats(null); 3108 insertFinished = false; 3109 3110 writerThread = new JUnitThread("Writer") { 3111 public void testBody() 3112 throws DatabaseException { 3113 DatabaseEntry key = new DatabaseEntry(); 3114 DatabaseEntry data = new DatabaseEntry(); 3115 OperationStatus status; 3116 IntegerBinding.intToEntry(keyVal, key); 3117 IntegerBinding.intToEntry(dataVal, data); 3118 Transaction writerTxn = env.beginTransaction(null, txnConfig); 3119 if (dups) { 3120 status = db.putNoDupData(writerTxn, key, data); 3121 } else { 3122 status = db.putNoOverwrite(writerTxn, key, data); 3123 } 3124 assertEquals(OperationStatus.SUCCESS, status); 3125 writerTxn.commit(Durability.COMMIT_NO_SYNC); 3126 insertFinished = true; 3127 } 3128 }; 3129 3130 writerThread.start(); 3131 3132 long startTime = System.currentTimeMillis(); 3133 while (true) { 3134 3135 /* Give some time to the writer thread. */ 3136 Thread.yield(); 3137 Thread.sleep(10); 3138 if (System.currentTimeMillis() - startTime > MAX_INSERT_MILLIS) { 3139 fail("Timeout doing insert"); 3140 } 3141 3142 if (txnSerializable) { 3143 3144 /* Wait for the insert to block. */ 3145 EnvironmentStats stats = env.getStats(null); 3146 if (stats.getNWaiters() > origStats.getNWaiters()) { 3147 break; 3148 } 3149 } else { 3150 3151 /* Wait for the operation to complete. */ 3152 if (insertFinished) { 3153 insertFinished = false; 3154 break; 3155 } 3156 } 3157 } 3158 } 3159 3160 /** 3161 * Waits for the writer thread to finish. 3162 */ waitForInsert()3163 private void waitForInsert() { 3164 3165 try { 3166 writerThread.finishTest(); 3167 } catch (Throwable e) { 3168 e.printStackTrace(); 3169 fail(e.toString()); 3170 } finally { 3171 writerThread = null; 3172 } 3173 } 3174 } 3175