1 8 9 package com.sleepycat.je.txn; 10 11 import java.io.File ; 12 import java.io.IOException ; 13 import java.io.RandomAccessFile ; 14 15 import junit.framework.TestCase; 16 17 import com.sleepycat.je.Cursor; 18 import com.sleepycat.je.Database; 19 import com.sleepycat.je.DatabaseConfig; 20 import com.sleepycat.je.DatabaseEntry; 21 import com.sleepycat.je.DatabaseException; 22 import com.sleepycat.je.DbInternal; 23 import com.sleepycat.je.DeadlockException; 24 import com.sleepycat.je.Environment; 25 import com.sleepycat.je.EnvironmentConfig; 26 import com.sleepycat.je.EnvironmentMutableConfig; 27 import com.sleepycat.je.LockMode; 28 import com.sleepycat.je.LockNotGrantedException; 29 import com.sleepycat.je.LockStats; 30 import com.sleepycat.je.OperationStatus; 31 import com.sleepycat.je.Transaction; 32 import com.sleepycat.je.TransactionConfig; 33 import com.sleepycat.je.config.EnvironmentParams; 34 import com.sleepycat.je.dbi.DatabaseImpl; 35 import com.sleepycat.je.dbi.EnvironmentImpl; 36 import com.sleepycat.je.dbi.MemoryBudget; 37 import com.sleepycat.je.log.FileManager; 38 import com.sleepycat.je.tree.ChildReference; 39 import com.sleepycat.je.tree.IN; 40 import com.sleepycat.je.tree.LN; 41 import com.sleepycat.je.tree.WithRootLatched; 42 import com.sleepycat.je.txn.Txn; 43 import com.sleepycat.je.util.TestUtils; 44 45 48 public class TxnTest extends TestCase { 49 private File envHome; 50 private Environment env; 51 private Database db; 52 53 public TxnTest() 54 throws DatabaseException { 55 56 envHome = new File (System.getProperty(TestUtils.DEST_DIR)); 57 } 58 59 public void setUp() 60 throws IOException , DatabaseException { 61 62 IN.ACCUMULATED_LIMIT = 0; 63 Txn.ACCUMULATED_LIMIT = 0; 64 65 TestUtils.removeFiles("Setup", envHome, FileManager.JE_SUFFIX); 66 67 EnvironmentConfig envConfig = TestUtils.initEnvConfig(); 68 envConfig.setConfigParam(EnvironmentParams.NODE_MAX.getName(), "6"); 69 envConfig.setTransactional(true); 70 envConfig.setAllowCreate(true); 71 env = new Environment(envHome, envConfig); 72 73 DatabaseConfig dbConfig = new DatabaseConfig(); 74 dbConfig.setTransactional(true); 75 dbConfig.setAllowCreate(true); 76 db = env.openDatabase(null, "foo", dbConfig); 77 } 78 79 public void tearDown() 80 throws IOException , DatabaseException { 81 82 db.close(); 83 env.close(); 84 TestUtils.removeFiles("TearDown", envHome, FileManager.JE_SUFFIX); 85 } 86 87 90 public void testBasicLocking() 91 throws Throwable { 92 93 try { 94 95 LN ln = new LN(new byte[0]); 96 97 101 EnvironmentImpl envImpl = DbInternal.envGetEnvironmentImpl(env); 102 MemoryBudget mb = envImpl.getMemoryBudget(); 103 104 long beforeLock = mb.getCacheMemoryUsage(); 105 Locker nullTxn = new BasicLocker(envImpl); 106 107 LockGrantType lockGrant = nullTxn.lock 108 (ln.getNodeId(), LockType.READ, false, 109 DbInternal.dbGetDatabaseImpl(db)). 110 getLockGrant(); 111 assertEquals(LockGrantType.NEW, lockGrant); 112 long afterLock = mb.getCacheMemoryUsage(); 113 checkHeldLocks(nullTxn, 1, 0); 114 115 nullTxn.operationEnd(); 116 long afterRelease = mb.getCacheMemoryUsage(); 117 checkHeldLocks(nullTxn, 0, 0); 118 checkCacheUsage(beforeLock, afterLock, afterRelease, 119 LockManager.TOTAL_LOCK_OVERHEAD + 120 MemoryBudget.LOCKINFO_OVERHEAD); 121 122 123 beforeLock = mb.getCacheMemoryUsage(); 124 lockGrant = nullTxn.lock 125 (ln.getNodeId(), LockType.READ, false, 126 DbInternal.dbGetDatabaseImpl(db)). 127 getLockGrant(); 128 afterLock = mb.getCacheMemoryUsage(); 129 assertEquals(LockGrantType.NEW, lockGrant); 130 checkHeldLocks(nullTxn, 1, 0); 131 132 nullTxn.releaseLock(ln.getNodeId()); 133 checkHeldLocks(nullTxn, 0, 0); 134 afterRelease = mb.getCacheMemoryUsage(); 135 checkCacheUsage(beforeLock, afterLock, afterRelease, 136 LockManager.TOTAL_LOCK_OVERHEAD + 137 MemoryBudget.LOCKINFO_OVERHEAD); 138 139 142 beforeLock = mb.getCacheMemoryUsage(); 143 Txn userTxn = new Txn(envImpl, new TransactionConfig()); 144 lockGrant = userTxn.lock 145 (ln.getNodeId(), LockType.READ, false, 146 DbInternal.dbGetDatabaseImpl(db)). 147 getLockGrant(); 148 afterLock = mb.getCacheMemoryUsage(); 149 150 assertEquals(LockGrantType.NEW, lockGrant); 151 checkHeldLocks(userTxn, 1, 0); 152 153 154 try { 155 userTxn.demoteLock(ln.getNodeId()); 156 fail("exception not thrown on phoney demoteLock"); 157 } catch (AssertionError e){ 158 } 159 checkHeldLocks(userTxn, 1, 0); 160 long afterDemotion = mb.getCacheMemoryUsage(); 161 assertEquals(afterLock, afterDemotion); 162 163 164 lockGrant = userTxn.lock 165 (ln.getNodeId(), LockType.WRITE, false, 166 DbInternal.dbGetDatabaseImpl(db)). 167 getLockGrant(); 168 assertEquals(LockGrantType.PROMOTION, lockGrant); 169 long afterWriteLock = mb.getCacheMemoryUsage(); 170 assertTrue(afterWriteLock > afterLock); 171 assertTrue(afterLock > beforeLock); 172 173 checkHeldLocks(userTxn, 0, 1); 174 userTxn.demoteLock(ln.getNodeId()); 175 checkHeldLocks(userTxn, 1, 0); 176 177 178 userTxn.operationEnd(); 179 checkHeldLocks(userTxn, 1, 0); 180 181 userTxn.releaseLock(ln.getNodeId()); 182 checkHeldLocks(userTxn, 0, 0); 183 userTxn.commit(Txn.TXN_SYNC); 184 afterRelease = mb.getCacheMemoryUsage(); 185 assertTrue(afterLock > beforeLock); 186 } catch (Throwable t) { 187 188 t.printStackTrace(); 189 throw t; 190 } 191 } 192 193 private void checkHeldLocks(Locker txn, 194 int numReadLocks, 195 int numWriteLocks) 196 throws DatabaseException { 197 198 LockStats stat = txn.collectStats(new LockStats()); 199 assertEquals(numReadLocks, stat.getNReadLocks()); 200 assertEquals(numWriteLocks, stat.getNWriteLocks()); 201 } 202 203 206 public void testCommit() 207 throws Throwable { 208 209 try { 210 LN ln1 = new LN(new byte[0]); 211 LN ln2 = new LN(new byte[0]); 212 213 EnvironmentImpl envImpl = DbInternal.envGetEnvironmentImpl(env); 214 Txn userTxn = new Txn(envImpl, new TransactionConfig()); 215 216 217 LockGrantType lockGrant = userTxn.lock 218 (ln1.getNodeId(), LockType.READ, false, 219 DbInternal.dbGetDatabaseImpl(db)). 220 getLockGrant(); 221 assertEquals(LockGrantType.NEW, lockGrant); 222 checkHeldLocks(userTxn, 1, 0); 223 224 225 lockGrant = userTxn.lock 226 (ln2.getNodeId(), LockType.READ, false, 227 DbInternal.dbGetDatabaseImpl(db)). 228 getLockGrant(); 229 assertEquals(LockGrantType.NEW, lockGrant); 230 checkHeldLocks(userTxn, 2, 0); 231 232 233 lockGrant = userTxn.lock 234 (ln2.getNodeId(), LockType.WRITE, false, 235 DbInternal.dbGetDatabaseImpl(db)). 236 getLockGrant(); 237 assertEquals(LockGrantType.PROMOTION, lockGrant); 238 checkHeldLocks(userTxn, 1, 1); 239 240 241 lockGrant = userTxn.lock 242 (ln1.getNodeId(), LockType.READ, false, 243 DbInternal.dbGetDatabaseImpl(db)). 244 getLockGrant(); 245 assertEquals(LockGrantType.EXISTING, lockGrant); 246 checkHeldLocks(userTxn, 1, 1); 247 248 249 long commitLsn = userTxn.commit(Txn.TXN_SYNC); 250 checkHeldLocks(userTxn, 0, 0); 251 252 TxnCommit commitRecord = 253 (TxnCommit) envImpl.getLogManager().get(commitLsn); 254 255 assertEquals(userTxn.getId(), commitRecord.getId()); 256 assertEquals(userTxn.getLastLsn(), commitRecord.getLastLsn()); 257 } catch (Throwable t) { 258 259 t.printStackTrace(); 260 throw t; 261 } 262 } 263 264 267 public void testAbortNoSplit() 268 throws Throwable { 269 270 try { 271 Transaction txn = env.beginTransaction(null, null); 272 273 DatabaseEntry keyDbt = new DatabaseEntry(); 274 DatabaseEntry dataDbt = new DatabaseEntry(); 275 dataDbt.setData(new byte[1]); 276 277 278 int numForSplit = 25; 279 for (int i = 0; i < numForSplit; i++) { 280 keyDbt.setData(TestUtils.getTestArray(i)); 281 db.put(txn, keyDbt, dataDbt); 282 } 283 284 285 DatabaseImpl database = DbInternal.dbGetDatabaseImpl(db); 286 CheckReadyToSplit splitChecker = new CheckReadyToSplit(database); 287 database.getTree().withRootLatchedShared(splitChecker); 288 assertTrue(splitChecker.getReadyToSplit()); 289 290 296 Transaction txnSpoiler = env.beginTransaction(null, null); 297 DatabaseConfig dbConfig = new DatabaseConfig(); 298 dbConfig.setTransactional(true); 299 Database dbSpoiler = env.openDatabase(txnSpoiler, "foo", dbConfig); 300 301 txn.abort(); 302 303 306 Cursor cursor = dbSpoiler.openCursor(txnSpoiler, null); 307 308 assertTrue(cursor.getFirst(keyDbt, dataDbt, LockMode.DEFAULT) != 309 OperationStatus.SUCCESS); 310 cursor.close(); 311 txnSpoiler.abort(); 312 } catch (Throwable t) { 313 314 t.printStackTrace(); 315 throw t; 316 } 317 } 318 319 public void testTransactionName() 320 throws Throwable { 321 322 try { 323 Transaction txn = env.beginTransaction(null, null); 324 txn.setName("blort"); 325 assertEquals("blort", txn.getName()); 326 txn.abort(); 327 328 332 String s = txn.toString(); 333 } catch (Throwable t) { 334 335 t.printStackTrace(); 336 throw t; 337 } 338 } 339 340 344 345 346 private static class SyncCombo { 347 private boolean envNoSync; 348 private boolean envWriteNoSync; 349 private boolean txnNoSync; 350 private boolean txnWriteNoSync; 351 private boolean txnSync; 352 boolean expectSync; 353 boolean expectWrite; 354 355 SyncCombo(int envWriteNoSync, 356 int envNoSync, 357 int txnSync, 358 int txnWriteNoSync, 359 int txnNoSync, 360 boolean expectSync, 361 boolean expectWrite) { 362 this.envNoSync = (envNoSync == 0) ? false : true; 363 this.envWriteNoSync = (envWriteNoSync == 0) ? false : true; 364 this.txnNoSync = (txnNoSync == 0) ? false : true; 365 this.txnWriteNoSync = (txnWriteNoSync == 0) ? false : true; 366 this.txnSync = (txnSync == 0) ? false : true; 367 this.expectSync = expectSync; 368 this.expectWrite = expectWrite; 369 } 370 371 TransactionConfig getTxnConfig() { 372 TransactionConfig txnConfig = new TransactionConfig(); 373 txnConfig.setSync(txnSync); 374 txnConfig.setWriteNoSync(txnWriteNoSync); 375 txnConfig.setNoSync(txnNoSync); 376 return txnConfig; 377 } 378 379 void setEnvironmentMutableConfig(Environment env) 380 throws DatabaseException { 381 EnvironmentMutableConfig config = env.getMutableConfig(); 382 config.setTxnNoSync(envNoSync); 383 config.setTxnWriteNoSync(envWriteNoSync); 384 env.setMutableConfig(config); 385 } 386 } 387 388 public void testSyncCombo() 389 throws Throwable { 390 391 RandomAccessFile logFile = 392 new RandomAccessFile (new File (envHome, "00000000.jdb"), "r"); 393 try { 394 SyncCombo [] testCombinations = { 395 397 new SyncCombo( 0, 0, 0, 0, 0, true, true), 398 new SyncCombo( 0, 0, 0, 0, 1, false, false), 399 new SyncCombo( 0, 0, 0, 1, 0, false, true), 400 new SyncCombo( 0, 0, 0, 1, 1, false, true), 401 new SyncCombo( 0, 0, 1, 0, 0, true, true), 402 new SyncCombo( 0, 0, 1, 0, 1, true, true), 403 new SyncCombo( 0, 0, 1, 1, 0, true, true), 404 new SyncCombo( 0, 0, 1, 1, 1, true, true), 405 new SyncCombo( 0, 1, 0, 0, 0, false, false), 406 new SyncCombo( 0, 1, 0, 0, 1, false, false), 407 new SyncCombo( 0, 1, 0, 1, 0, false, true), 408 new SyncCombo( 0, 1, 0, 1, 1, false, true), 409 new SyncCombo( 0, 1, 1, 0, 0, true, true), 410 new SyncCombo( 0, 1, 1, 0, 1, true, true), 411 new SyncCombo( 0, 1, 1, 1, 0, true, true), 412 new SyncCombo( 0, 1, 1, 1, 1, true, true), 413 new SyncCombo( 1, 0, 0, 0, 0, false, true), 414 new SyncCombo( 1, 0, 0, 0, 1, false, false), 415 new SyncCombo( 1, 0, 0, 1, 0, false, true), 416 new SyncCombo( 1, 0, 0, 1, 1, false, true), 417 new SyncCombo( 1, 0, 1, 0, 0, true, true), 418 new SyncCombo( 1, 0, 1, 0, 1, true, true), 419 new SyncCombo( 1, 0, 1, 1, 0, true, true), 420 new SyncCombo( 1, 0, 1, 1, 1, true, true), 421 new SyncCombo( 1, 1, 0, 0, 0, false, true), 422 new SyncCombo( 1, 1, 0, 0, 1, false, false), 423 new SyncCombo( 1, 1, 0, 1, 0, false, true), 424 new SyncCombo( 1, 1, 0, 1, 1, false, true), 425 new SyncCombo( 1, 1, 1, 0, 0, true, true), 426 new SyncCombo( 1, 1, 1, 0, 1, true, true), 427 new SyncCombo( 1, 1, 1, 1, 0, true, true), 428 new SyncCombo( 1, 1, 1, 1, 1, true, true)}; 429 430 431 assertTrue(!env.getMutableConfig().getTxnNoSync()); 432 433 434 assertTrue(!env.getMutableConfig().getTxnWriteNoSync()); 435 436 442 for (int i = 0; i < testCombinations.length; i++) { 443 SyncCombo combo = testCombinations[i]; 444 TransactionConfig txnConfig = combo.getTxnConfig(); 445 combo.setEnvironmentMutableConfig(env); 446 syncExplicit(logFile, txnConfig, 447 combo.expectSync, combo.expectWrite); 448 } 449 450 SyncCombo [] autoCommitCombinations = { 451 453 new SyncCombo( 0, 0, 0, 0, 0, true, true), 454 new SyncCombo( 0, 1, 0, 0, 0, false, false), 455 new SyncCombo( 1, 0, 0, 0, 0, false, true), 456 new SyncCombo( 1, 1, 0, 0, 0, false, true)}; 457 458 for (int i = 0; i < autoCommitCombinations.length; i++) { 459 SyncCombo combo = autoCommitCombinations[i]; 460 combo.setEnvironmentMutableConfig(env); 461 syncAutoCommit(logFile, combo.expectSync, combo.expectWrite); 462 } 463 } catch (Throwable t) { 464 465 t.printStackTrace(); 466 throw t; 467 } finally { 468 logFile.close(); 469 } 470 } 471 472 475 private void syncExplicit(RandomAccessFile lastLogFile, 476 TransactionConfig config, 477 boolean expectSync, 478 boolean expectWrite) 479 throws DatabaseException, IOException { 480 481 DatabaseEntry key = new DatabaseEntry(new byte[1]); 482 DatabaseEntry data = new DatabaseEntry(new byte[1]); 483 484 long beforeSyncs = getNSyncs(); 485 Transaction txn = env.beginTransaction(null, config); 486 db.put(txn, key, data); 487 long beforeLength = lastLogFile.length(); 488 txn.commit(); 489 long afterSyncs = getNSyncs(); 490 long afterLength = lastLogFile.length(); 491 boolean syncOccurred = afterSyncs > beforeSyncs; 492 boolean writeOccurred = afterLength > beforeLength; 493 assertEquals(expectSync, syncOccurred); 494 assertEquals(expectWrite, writeOccurred); 495 496 499 500 501 beforeSyncs = getNSyncs(); 502 beforeLength = lastLogFile.length(); 503 txn = env.beginTransaction(null, config); 504 db.put(txn, key, data); 505 txn.commitSync(); 506 afterSyncs = getNSyncs(); 507 afterLength = lastLogFile.length(); 508 assert(afterSyncs > beforeSyncs); 509 assert(afterLength > beforeLength); 510 511 512 beforeSyncs = getNSyncs(); 513 beforeLength = lastLogFile.length(); 514 txn = env.beginTransaction(null, config); 515 db.put(txn, key, data); 516 txn.commitNoSync(); 517 afterSyncs = getNSyncs(); 518 afterLength = lastLogFile.length(); 519 assert(afterSyncs == beforeSyncs); 520 assert(afterLength == beforeLength); 521 522 523 beforeSyncs = getNSyncs(); 524 beforeLength = lastLogFile.length(); 525 txn = env.beginTransaction(null, config); 526 db.put(txn, key, data); 527 txn.commitWriteNoSync(); 528 afterSyncs = getNSyncs(); 529 afterLength = lastLogFile.length(); 530 assert(afterSyncs == beforeSyncs); 531 assert(afterLength > beforeLength); 532 } 533 534 537 private void syncAutoCommit(RandomAccessFile lastLogFile, 538 boolean expectSync, 539 boolean expectWrite) 540 throws DatabaseException, IOException { 541 542 DatabaseEntry key = new DatabaseEntry(new byte[1]); 543 DatabaseEntry data = new DatabaseEntry(new byte[1]); 544 long beforeSyncs = getNSyncs(); 545 long beforeLength = lastLogFile.length(); 546 db.put(null, key, data); 547 long afterLength = lastLogFile.length(); 548 long afterSyncs = getNSyncs(); 549 boolean syncOccurred = afterSyncs > beforeSyncs; 550 assertEquals(expectSync, syncOccurred); 551 assertEquals(expectWrite, (afterLength > beforeLength)); 552 } 553 554 557 private long getNSyncs() { 558 return DbInternal.envGetEnvironmentImpl(env) 559 .getFileManager() 560 .getNFSyncs(); 561 } 562 563 public void testNoWaitConfig() 564 throws Throwable { 565 566 try { 567 TransactionConfig defaultConfig = new TransactionConfig(); 568 TransactionConfig noWaitConfig = new TransactionConfig(); 569 noWaitConfig.setNoWait(true); 570 Transaction txn; 571 572 573 574 assertTrue(!isNoWaitTxn(null)); 575 576 txn = env.beginTransaction(null, null); 577 assertTrue(!isNoWaitTxn(txn)); 578 txn.abort(); 579 580 txn = env.beginTransaction(null, defaultConfig); 581 assertTrue(!isNoWaitTxn(txn)); 582 txn.abort(); 583 584 585 586 txn = env.beginTransaction(null, noWaitConfig); 587 assertTrue(isNoWaitTxn(txn)); 588 txn.abort(); 589 590 } catch (Throwable t) { 591 592 t.printStackTrace(); 593 throw t; 594 } 595 } 596 597 601 private boolean isNoWaitTxn(Transaction txn) 602 throws DatabaseException { 603 604 DatabaseEntry key = new DatabaseEntry(new byte[1]); 605 DatabaseEntry data = new DatabaseEntry(new byte[1]); 606 607 608 Transaction txn2 = env.beginTransaction(null, null); 609 db.put(txn2, key, data); 610 611 try { 612 db.put(txn, key, data); 613 throw new IllegalStateException 614 ("Lock should not have been granted"); 615 } catch (LockNotGrantedException e) { 616 return true; 617 } catch (DeadlockException e) { 618 return false; 619 } finally { 620 txn2.abort(); 621 } 622 } 623 624 628 private void checkCacheUsage(long beforeLock, 629 long afterLock, 630 long afterRelease, 631 long expectedSize) { 632 assertEquals(beforeLock, afterRelease); 633 assertEquals(afterLock, (beforeLock + expectedSize)); 634 } 635 636 class CheckReadyToSplit implements WithRootLatched { 637 private boolean readyToSplit; 638 private DatabaseImpl database; 639 640 CheckReadyToSplit(DatabaseImpl database) { 641 readyToSplit = false; 642 this.database = database; 643 } 644 645 public boolean getReadyToSplit() { 646 return readyToSplit; 647 } 648 649 public IN doWork(ChildReference root) 650 throws DatabaseException { 651 652 IN rootIN = (IN) root.fetchTarget(database, null); 653 readyToSplit = rootIN.needsSplitting(); 654 return null; 655 } 656 } 657 } 658 | Popular Tags |