1 8 9 package com.sleepycat.je; 10 11 import java.io.File ; 12 import java.io.IOException ; 13 import java.util.Arrays ; 14 15 import junit.framework.TestCase; 16 17 import com.sleepycat.je.DbInternal; 18 import com.sleepycat.je.config.EnvironmentParams; 19 import com.sleepycat.je.junit.JUnitThread; 20 import com.sleepycat.je.util.TestUtils; 21 22 public class CursorTest extends TestCase { 23 private static final boolean DEBUG = false; 24 private static final int NUM_RECS = 257; 25 26 30 private static final long LOCK_TIMEOUT = 50000000L; 31 32 private static final String DUPKEY = "DUPKEY"; 33 34 private Environment env; 35 private Database db; 36 private PhantomTestConfiguration config; 37 38 private File envHome; 39 40 private volatile int sequence; 41 42 public CursorTest() { 43 envHome = new File (System.getProperty(TestUtils.DEST_DIR)); 44 } 45 46 public void setUp() 47 throws IOException { 48 49 TestUtils.removeLogFiles("Setup", envHome, false); 50 } 51 52 public void tearDown() 53 throws IOException { 54 55 if (env != null) { 56 try { 57 env.close(); 58 } catch (Throwable e) { 59 System.out.println("tearDown: " + e); 60 } 61 } 62 db = null; 63 env = null; 64 65 TestUtils.removeLogFiles("TearDown", envHome, false); 66 } 67 68 public void testGetConfig() 69 throws DatabaseException { 70 71 EnvironmentConfig envConfig = TestUtils.initEnvConfig(); 72 envConfig.setTransactional(true); 73 envConfig.setAllowCreate(true); 74 envConfig.setTxnNoSync(Boolean.getBoolean(TestUtils.NO_SYNC)); 75 env = new Environment(envHome, envConfig); 76 Transaction txn = env.beginTransaction(null, null); 77 DatabaseConfig dbConfig = new DatabaseConfig(); 78 dbConfig.setTransactional(true); 79 dbConfig.setSortedDuplicates(true); 80 dbConfig.setAllowCreate(true); 81 db = env.openDatabase(txn, "testDB", dbConfig); 82 txn.commit(); 83 Cursor cursor = null; 84 Transaction txn1 = 85 env.beginTransaction(null, TransactionConfig.DEFAULT); 86 try { 87 cursor = db.openCursor(txn1, CursorConfig.DEFAULT); 88 CursorConfig config = cursor.getConfig(); 89 if (config == CursorConfig.DEFAULT) { 90 fail("didn't clone"); 91 } 92 } catch (DatabaseException DBE) { 93 DBE.printStackTrace(); 94 fail("caught DatabaseException " + DBE); 95 } finally { 96 if (cursor != null) { 97 cursor.close(); 98 } 99 txn1.abort(); 100 db.close(); 101 env.close(); 102 env = null; 103 } 104 } 105 106 110 public void testBasic() 111 throws Throwable { 112 113 try { 114 insertMultiDb(1); 115 } catch (Throwable t) { 116 t.printStackTrace(); 117 throw t; 118 } 119 } 120 121 public void testMulti() 122 throws Throwable { 123 124 try { 125 insertMultiDb(4); 126 } catch (Throwable t) { 127 t.printStackTrace(); 128 throw t; 129 } 130 } 131 132 136 class PhantomTestConfiguration { 137 String testName; 138 String thread1EntryToLock; 139 String thread1OpArg; 140 String thread2Start; 141 String expectedResult; 142 boolean doInsert; 143 boolean doGetNext; 144 boolean doCommit; 145 146 PhantomTestConfiguration(String testName, 147 String thread1EntryToLock, 148 String thread1OpArg, 149 String thread2Start, 150 String expectedResult, 151 boolean doInsert, 152 boolean doGetNext, 153 boolean doCommit) { 154 this.testName = testName; 155 this.thread1EntryToLock = thread1EntryToLock; 156 this.thread1OpArg = thread1OpArg; 157 this.thread2Start = thread2Start; 158 this.expectedResult = expectedResult; 159 this.doInsert = doInsert; 160 this.doGetNext = doGetNext; 161 this.doCommit = doCommit; 162 } 163 } 164 165 194 195 198 public void testPhantomInsertGetNextCommit() 199 throws Throwable { 200 201 try { 202 phantomWorker 203 (new PhantomTestConfiguration 204 ("testPhantomInsertGetNextCommit", 205 "F", "D", "C", "D", 206 true, true, true)); 207 } catch (Exception e) { 208 e.printStackTrace(); 209 throw e; 210 } 211 } 212 213 216 public void testPhantomInsertGetNextAbort() 217 throws Throwable { 218 219 phantomWorker 220 (new PhantomTestConfiguration 221 ("testPhantomInsertGetNextAbort", 222 "F", "D", "C", "F", 223 true, true, false)); 224 } 225 226 229 public void testPhantomInsertGetPrevCommit() 230 throws Throwable { 231 232 phantomWorker 233 (new PhantomTestConfiguration 234 ("testPhantomInsertGetPrevCommit", 235 "C", "F", "G", "F", 236 true, false, true)); 237 } 238 239 242 public void testPhantomInsertGetPrevAbort() 243 throws Throwable { 244 245 phantomWorker 246 (new PhantomTestConfiguration 247 ("testPhantomInsertGetPrevAbort", 248 "C", "F", "G", "C", 249 true, false, false)); 250 } 251 252 256 public void testPhantomDeleteGetNextCommit() 257 throws Throwable { 258 259 phantomWorker 260 (new PhantomTestConfiguration 261 ("testPhantomDeleteGetNextCommit", 262 "F", "F", "C", "G", 263 false, true, true)); 264 } 265 266 270 public void testPhantomDeleteGetNextAbort() 271 throws Throwable { 272 273 phantomWorker 274 (new PhantomTestConfiguration 275 ("testPhantomDeleteGetNextAbort", 276 "F", "F", "C", "F", 277 false, true, false)); 278 } 279 280 284 public void testPhantomDeleteGetPrevCommit() 285 throws Throwable { 286 287 phantomWorker 288 (new PhantomTestConfiguration 289 ("testPhantomDeleteGetPrevCommit", 290 "F", "F", "G", "C", 291 false, false, true)); 292 } 293 294 298 public void testPhantomDeleteGetPrevAbort() 299 throws Throwable { 300 301 phantomWorker 302 (new PhantomTestConfiguration 303 ("testPhantomDeleteGetPrevAbort", 304 "F", "F", "G", "F", 305 false, false, false)); 306 } 307 308 312 public void testPhantomDupInsertGetNextCommit() 313 throws Throwable { 314 315 try { 316 phantomDupWorker 317 (new PhantomTestConfiguration 318 ("testPhantomDupInsertGetNextCommit", 319 "F", "D", "C", "D", 320 true, true, true)); 321 } catch (Exception e) { 322 e.printStackTrace(); 323 throw e; 324 } 325 } 326 327 330 public void testPhantomDupInsertGetNextAbort() 331 throws Throwable { 332 333 phantomDupWorker 334 (new PhantomTestConfiguration 335 ("testPhantomDupInsertGetNextAbort", 336 "F", "D", "C", "F", 337 true, true, false)); 338 } 339 340 344 public void testPhantomDupInsertGetPrevCommit() 345 throws Throwable { 346 347 phantomDupWorker 348 (new PhantomTestConfiguration 349 ("testPhantomDupInsertGetPrevCommit", 350 "C", "F", "G", "F", 351 true, false, true)); 352 } 353 354 357 public void testPhantomDupInsertGetPrevAbort() 358 throws Throwable { 359 360 phantomDupWorker 361 (new PhantomTestConfiguration 362 ("testPhantomDupInsertGetPrevAbort", 363 "C", "F", "G", "C", 364 true, false, false)); 365 } 366 367 371 public void testPhantomDupDeleteGetNextCommit() 372 throws Throwable { 373 374 phantomDupWorker 375 (new PhantomTestConfiguration 376 ("testPhantomDupDeleteGetNextCommit", 377 "F", "F", "C", "G", 378 false, true, true)); 379 } 380 381 385 public void testPhantomDupDeleteGetNextAbort() 386 throws Throwable { 387 388 phantomDupWorker 389 (new PhantomTestConfiguration 390 ("testPhantomDupDeleteGetNextAbort", 391 "F", "F", "C", "F", 392 false, true, false)); 393 } 394 395 399 public void testPhantomDupDeleteGetPrevCommit() 400 throws Throwable { 401 402 phantomDupWorker 403 (new PhantomTestConfiguration 404 ("testPhantomDupDeleteGetPrevCommit", 405 "F", "F", "G", "C", 406 false, false, true)); 407 } 408 409 413 public void testPhantomDupDeleteGetPrevAbort() 414 throws Throwable { 415 416 phantomDupWorker 417 (new PhantomTestConfiguration 418 ("testPhantomDupDeleteGetPrevAbort", 419 "F", "F", "G", "F", 420 false, false, false)); 421 } 422 423 private void phantomWorker(PhantomTestConfiguration c) 424 throws Throwable { 425 426 try { 427 this.config = c; 428 setupDatabaseAndEnv(false); 429 430 if (config.doInsert && 431 !config.doGetNext) { 432 433 Transaction txnDel = 434 env.beginTransaction(null, TransactionConfig.DEFAULT); 435 436 442 assertEquals(OperationStatus.SUCCESS, 443 db.delete(txnDel, 444 new DatabaseEntry("F".getBytes()))); 445 txnDel.commit(); 446 } 447 448 JUnitThread tester1 = 449 new JUnitThread(config.testName + "1") { 450 public void testBody() 451 throws Throwable { 452 453 Cursor cursor = null; 454 try { 455 Transaction txn1 = 456 env.beginTransaction(null, null); 457 cursor = db.openCursor(txn1, CursorConfig.DEFAULT); 458 OperationStatus status = 459 cursor.getSearchKey 460 (new DatabaseEntry 461 (config.thread1EntryToLock.getBytes()), 462 new DatabaseEntry(), 463 LockMode.RMW); 464 assertEquals(OperationStatus.SUCCESS, status); 465 sequence++; 467 while (sequence < 2) { 468 Thread.yield(); 469 } 470 471 478 try { 479 Thread.sleep(250); 480 } catch (InterruptedException IE) { 481 } 482 483 if (config.doInsert) { 484 status = db.put 485 (txn1, 486 new DatabaseEntry 487 (config.thread1OpArg.getBytes()), 488 new DatabaseEntry(new byte[10])); 489 } else { 490 status = db.delete 491 (txn1, 492 new DatabaseEntry 493 (config.thread1OpArg.getBytes())); 494 } 495 assertEquals(OperationStatus.SUCCESS, status); 496 sequence++; 498 try { 499 Thread.sleep(1000); 500 } catch (InterruptedException IE) { 501 } 502 503 cursor.close(); 504 cursor = null; 505 if (config.doCommit) { 506 txn1.commit(); 507 } else { 508 txn1.abort(); 509 } 510 } catch (DatabaseException DBE) { 511 if (cursor != null) { 512 cursor.close(); 513 } 514 DBE.printStackTrace(); 515 fail("caught DatabaseException " + DBE); 516 } 517 } 518 }; 519 520 JUnitThread tester2 = 521 new JUnitThread(config.testName + "2") { 522 public void testBody() 523 throws Throwable { 524 525 Cursor cursor = null; 526 try { 527 Transaction txn2 = 528 env.beginTransaction(null, null); 529 txn2.setLockTimeout(LOCK_TIMEOUT); 530 cursor = db.openCursor(txn2, CursorConfig.DEFAULT); 531 532 while (sequence < 1) { 533 Thread.yield(); 534 } 535 536 OperationStatus status = 537 cursor.getSearchKey 538 (new DatabaseEntry 539 (config.thread2Start.getBytes()), 540 new DatabaseEntry(), 541 LockMode.DEFAULT); 542 assertEquals(OperationStatus.SUCCESS, status); 543 544 sequence++; DatabaseEntry nextKey = new DatabaseEntry(); 546 try { 547 548 551 if (config.doGetNext) { 552 status = 553 cursor.getNext(nextKey, 554 new DatabaseEntry(), 555 LockMode.DEFAULT); 556 } else { 557 status = 558 cursor.getPrev(nextKey, 559 new DatabaseEntry(), 560 LockMode.DEFAULT); 561 } 562 } catch (DatabaseException DBE) { 563 System.out.println("t2 caught " + DBE); 564 } 565 assertEquals(3, sequence); 566 assertEquals(config.expectedResult, 567 new String (nextKey.getData())); 568 cursor.close(); 569 cursor = null; 570 txn2.commit(); 571 } catch (DatabaseException DBE) { 572 if (cursor != null) { 573 cursor.close(); 574 } 575 DBE.printStackTrace(); 576 fail("caught DatabaseException " + DBE); 577 } 578 } 579 }; 580 581 tester1.start(); 582 tester2.start(); 583 584 tester1.finishTest(); 585 tester2.finishTest(); 586 } finally { 587 db.close(); 588 env.close(); 589 env = null; 590 } 591 } 592 593 private void phantomDupWorker(PhantomTestConfiguration c) 594 throws Throwable { 595 596 Cursor cursor = null; 597 try { 598 this.config = c; 599 setupDatabaseAndEnv(true); 600 601 if (config.doInsert && 602 !config.doGetNext) { 603 604 Transaction txnDel = 605 env.beginTransaction(null, TransactionConfig.DEFAULT); 606 cursor = db.openCursor(txnDel, CursorConfig.DEFAULT); 607 608 614 assertEquals(OperationStatus.SUCCESS, cursor.getSearchBoth 615 (new DatabaseEntry(DUPKEY.getBytes()), 616 new DatabaseEntry("F".getBytes()), 617 LockMode.DEFAULT)); 618 assertEquals(OperationStatus.SUCCESS, cursor.delete()); 619 cursor.close(); 620 cursor = null; 621 txnDel.commit(); 622 } 623 624 JUnitThread tester1 = 625 new JUnitThread(config.testName + "1") { 626 public void testBody() 627 throws Throwable { 628 629 Cursor cursor = null; 630 Cursor c = null; 631 try { 632 Transaction txn1 = 633 env.beginTransaction(null, null); 634 cursor = db.openCursor(txn1, CursorConfig.DEFAULT); 635 OperationStatus status = 636 cursor.getSearchBoth 637 (new DatabaseEntry(DUPKEY.getBytes()), 638 new DatabaseEntry 639 (config.thread1EntryToLock.getBytes()), 640 LockMode.RMW); 641 assertEquals(OperationStatus.SUCCESS, status); 642 cursor.close(); 643 cursor = null; 644 sequence++; 646 while (sequence < 2) { 647 Thread.yield(); 648 } 649 650 657 try { 658 Thread.sleep(250); 659 } catch (InterruptedException IE) { 660 } 661 662 if (config.doInsert) { 663 status = db.put 664 (txn1, 665 new DatabaseEntry(DUPKEY.getBytes()), 666 new DatabaseEntry 667 (config.thread1OpArg.getBytes())); 668 } else { 669 c = db.openCursor(txn1, CursorConfig.DEFAULT); 670 assertEquals(OperationStatus.SUCCESS, 671 c.getSearchBoth 672 (new DatabaseEntry 673 (DUPKEY.getBytes()), 674 new DatabaseEntry 675 (config.thread1OpArg.getBytes()), 676 LockMode.DEFAULT)); 677 assertEquals(OperationStatus.SUCCESS, 678 c.delete()); 679 c.close(); 680 c = null; 681 } 682 assertEquals(OperationStatus.SUCCESS, status); 683 sequence++; 685 try { 686 Thread.sleep(1000); 687 } catch (InterruptedException IE) { 688 } 689 690 if (config.doCommit) { 691 txn1.commit(); 692 } else { 693 txn1.abort(); 694 } 695 } catch (DatabaseException DBE) { 696 if (cursor != null) { 697 cursor.close(); 698 } 699 if (c != null) { 700 c.close(); 701 } 702 DBE.printStackTrace(); 703 fail("caught DatabaseException " + DBE); 704 } 705 } 706 }; 707 708 JUnitThread tester2 = 709 new JUnitThread("testPhantomInsert2") { 710 public void testBody() 711 throws Throwable { 712 713 Cursor cursor = null; 714 try { 715 Transaction txn2 = 716 env.beginTransaction(null, null); 717 txn2.setLockTimeout(LOCK_TIMEOUT); 718 cursor = db.openCursor(txn2, CursorConfig.DEFAULT); 719 720 while (sequence < 1) { 721 Thread.yield(); 722 } 723 724 OperationStatus status = 725 cursor.getSearchBoth 726 (new DatabaseEntry(DUPKEY.getBytes()), 727 new DatabaseEntry 728 (config.thread2Start.getBytes()), 729 LockMode.DEFAULT); 730 assertEquals(OperationStatus.SUCCESS, status); 731 732 sequence++; DatabaseEntry nextKey = new DatabaseEntry(); 734 DatabaseEntry nextData = new DatabaseEntry(); 735 try { 736 737 740 if (config.doGetNext) { 741 status = 742 cursor.getNextDup(nextKey, nextData, 743 LockMode.DEFAULT); 744 } else { 745 status = 746 cursor.getPrevDup(nextKey, nextData, 747 LockMode.DEFAULT); 748 } 749 } catch (DatabaseException DBE) { 750 System.out.println("t2 caught " + DBE); 751 } 752 assertEquals(3, sequence); 753 byte[] data = nextData.getData(); 754 assertEquals(config.expectedResult, 755 new String (data)); 756 cursor.close(); 757 cursor = null; 758 txn2.commit(); 759 } catch (DatabaseException DBE) { 760 if (cursor != null) { 761 cursor.close(); 762 } 763 DBE.printStackTrace(); 764 fail("caught DatabaseException " + DBE); 765 } 766 } 767 }; 768 769 tester1.start(); 770 tester2.start(); 771 772 tester1.finishTest(); 773 tester2.finishTest(); 774 } finally { 775 if (cursor != null) { 776 cursor.close(); 777 } 778 db.close(); 779 env.close(); 780 env = null; 781 } 782 } 783 784 788 private void setupDatabaseAndEnv(boolean writeAsDuplicateData) 789 throws DatabaseException { 790 791 EnvironmentConfig envConfig = TestUtils.initEnvConfig(); 792 793 794 TestUtils.clearIsolationLevel(envConfig); 795 796 DbInternal.disableParameterValidation(envConfig); 797 envConfig.setTransactional(true); 798 envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(), 799 "6"); 800 envConfig.setConfigParam(EnvironmentParams.NODE_MAX_DUPTREE.getName(), 801 "6"); 802 envConfig.setConfigParam(EnvironmentParams.LOG_FILE_MAX.getName(), 803 "1024"); 804 envConfig.setConfigParam(EnvironmentParams.ENV_CHECK_LEAKS.getName(), 805 "true"); 806 envConfig.setAllowCreate(true); 807 envConfig.setTxnNoSync(Boolean.getBoolean(TestUtils.NO_SYNC)); 808 env = new Environment(envHome, envConfig); 809 Transaction txn = env.beginTransaction(null, null); 810 DatabaseConfig dbConfig = new DatabaseConfig(); 811 dbConfig.setTransactional(true); 812 dbConfig.setSortedDuplicates(true); 813 dbConfig.setAllowCreate(true); 814 db = env.openDatabase(txn, "testDB", dbConfig); 815 816 if (writeAsDuplicateData) { 817 writeDuplicateData(db, txn); 818 } else { 819 writeData(db, txn); 820 } 821 822 txn.commit(); 823 } 824 825 String [] dataStrings = { 826 "A", "B", "C", "F", "G", "H", "I" 827 }; 828 829 private void writeData(Database db, Transaction txn) 830 throws DatabaseException { 831 832 for (int i = 0; i < dataStrings.length; i++) { 833 db.put(txn, new DatabaseEntry(dataStrings[i].getBytes()), 834 new DatabaseEntry(new byte[10])); 835 } 836 } 837 838 private void writeDuplicateData(Database db, Transaction txn) 839 throws DatabaseException { 840 841 for (int i = 0; i < dataStrings.length; i++) { 842 db.put(txn, new DatabaseEntry(DUPKEY.getBytes()), 843 new DatabaseEntry(dataStrings[i].getBytes())); 844 } 845 } 846 847 850 private void insertMultiDb(int numDbs) 851 throws DatabaseException { 852 853 EnvironmentConfig envConfig = TestUtils.initEnvConfig(); 854 855 856 TestUtils.clearIsolationLevel(envConfig); 857 858 DbInternal.disableParameterValidation(envConfig); 859 envConfig.setTransactional(true); 860 envConfig.setConfigParam 861 (EnvironmentParams.LOG_FILE_MAX.getName(), "1024"); 862 envConfig.setConfigParam 863 (EnvironmentParams.ENV_CHECK_LEAKS.getName(), "true"); 864 envConfig.setConfigParam 865 (EnvironmentParams.NODE_MAX.getName(), "6"); 866 envConfig.setConfigParam 867 (EnvironmentParams.NODE_MAX_DUPTREE.getName(), "6"); 868 envConfig.setTxnNoSync(Boolean.getBoolean(TestUtils.NO_SYNC)); 869 envConfig.setAllowCreate(true); 870 Environment env = new Environment(envHome, envConfig); 871 872 Database[] myDb = new Database[numDbs]; 873 Cursor[] cursor = new Cursor[numDbs]; 874 Transaction txn = 875 env.beginTransaction(null, TransactionConfig.DEFAULT); 876 877 DatabaseConfig dbConfig = new DatabaseConfig(); 878 dbConfig.setTransactional(true); 879 dbConfig.setAllowCreate(true); 880 dbConfig.setSortedDuplicates(true); 881 for (int i = 0; i < numDbs; i++) { 882 myDb[i] = env.openDatabase(txn, "testDB" + i, dbConfig); 883 884 cursor[i] = myDb[i].openCursor(txn, CursorConfig.DEFAULT); 885 } 886 887 888 DatabaseEntry key = new DatabaseEntry(); 889 DatabaseEntry data = new DatabaseEntry(); 890 for (int i = NUM_RECS; i > 0; i--) { 891 for (int c = 0; c < numDbs; c++) { 892 key.setData(TestUtils.getTestArray(i + c)); 893 data.setData(TestUtils.getTestArray(i + c)); 894 if (DEBUG) { 895 System.out.println("i = " + i + 896 TestUtils.dumpByteArray(key.getData())); 897 } 898 cursor[c].put(key, data); 899 } 900 } 901 902 for (int i = 0; i < numDbs; i++) { 903 cursor[i].close(); 904 myDb[i].close(); 905 } 906 txn.commit(); 907 908 assertTrue(env.verify(null, System.err)); 909 env.close(); 910 env = null; 911 912 envConfig.setAllowCreate(false); 913 env = new Environment(envHome, envConfig); 914 915 920 env.cleanLog(); 921 922 env.verify(null, System.err); 923 924 925 dbConfig.setTransactional(false); 926 dbConfig.setAllowCreate(false); 927 for (int d = 0; d < numDbs; d++) { 928 Database checkDb = env.openDatabase(null, "testDB" + d, 929 dbConfig); 930 Cursor myCursor = checkDb.openCursor(null, CursorConfig.DEFAULT); 931 932 OperationStatus status = 933 myCursor.getFirst(key, data, LockMode.DEFAULT); 934 935 int i = 1; 936 while (status == OperationStatus.SUCCESS) { 937 byte[] expectedKey = TestUtils.getTestArray(i + d); 938 byte[] expectedData = TestUtils.getTestArray(i + d); 939 940 if (DEBUG) { 941 System.out.println("Database " + d + " Key " + i + 942 " expected = " + 943 TestUtils.dumpByteArray(expectedKey) + 944 " seen = " + 945 TestUtils.dumpByteArray(key.getData())); 946 } 947 948 assertTrue("Database " + d + " Key " + i + " expected = " + 949 TestUtils.dumpByteArray(expectedKey) + 950 " seen = " + 951 TestUtils.dumpByteArray(key.getData()), 952 Arrays.equals(expectedKey, key.getData())); 953 assertTrue("Data " + i, Arrays.equals(expectedData, 954 data.getData())); 955 i++; 956 957 status = myCursor.getNext(key, data, LockMode.DEFAULT); 958 } 959 myCursor.close(); 960 assertEquals("Number recs seen", NUM_RECS, i-1); 961 checkDb.close(); 962 } 963 env.close(); 964 env = null; 965 } 966 } 967 | Popular Tags |