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.txn; 9 10 import java.util.HashMap; 11 import java.util.Map; 12 13 import com.sleepycat.je.Database; 14 import com.sleepycat.je.DatabaseException; 15 import com.sleepycat.je.EnvironmentFailureException; 16 import com.sleepycat.je.LockConflictException; 17 import com.sleepycat.je.LockNotAvailableException; 18 import com.sleepycat.je.OperationFailureException; 19 import com.sleepycat.je.OperationStatus; 20 import com.sleepycat.je.Transaction; 21 import com.sleepycat.je.dbi.CursorImpl; 22 import com.sleepycat.je.dbi.DatabaseImpl; 23 import com.sleepycat.je.dbi.EnvironmentImpl; 24 import com.sleepycat.je.tree.BIN; 25 import com.sleepycat.je.tree.BINReference; 26 import com.sleepycat.je.utilint.DbLsn; 27 import com.sleepycat.je.utilint.StatGroup; 28 29 /** 30 * Locker instances are JE's route to locking and transactional support. This 31 * class is the abstract base class for BasicLocker, ThreadLocker, Txn, 32 * MasterTxn and ReadonlyTxn. Locker instances are in fact only a transaction 33 * shell to get to the lock manager, and don't guarantee transactional 34 * semantics. 35 * 36 * Txn (includes Txns marked autoTxn) MasterTxn and ReadonlyTxn instances are 37 * truly transactional. They have potentially different transaction begin and 38 * end behaviors. 39 */ 40 public abstract class Locker { 41 42 protected EnvironmentImpl envImpl; 43 protected LockManager lockManager; 44 45 protected long id; // transaction id 46 protected boolean readUncommittedDefault; // read-uncommitted is default 47 48 /* Timeouts */ 49 protected final boolean defaultNoWait; // true for non-blocking 50 private long lockTimeoutMillis; // timeout period for lock, in ms 51 private long txnTimeoutMillis; // timeout period for txns, in ms 52 private long txnStartMillis; // for txn timeout determination 53 54 private Lock waitingFor; // The lock that this txn is 55 // waiting for. 56 57 /* 58 * DeleteInfo refers to BINReferences that should be sent to the 59 * INCompressor for asynchronous compressing after the transaction ends. 60 */ 61 protected Map<Long,BINReference> deleteInfo; 62 63 /** 64 * The thread that created this locker. Used for debugging, and by the 65 * ThreadLocker subclass. Note that thread may be null if the Locker is 66 * instantiated by reading the log. 67 */ 68 protected Thread thread; 69 70 /** 71 * Set to false when close() is called. After that point no other locker 72 * operations should occur. 73 */ 74 private boolean isOpen = true; 75 76 /** 77 * True if my locks can be preempted/stolen. 78 */ 79 private boolean preemptable = true; 80 81 /** 82 * Non-null if a lock has been stolen from this locker by the HA replayer. 83 */ 84 private RuntimeException preemptedCause; 85 86 /** 87 * Non-null if this locker is replacing another locker that is in the 88 * process of closing because a cursor is being moved. 89 */ 90 private Locker closingLocker; 91 92 /** 93 * Create a locker id. This constructor is called very often, so it should 94 * be as streamlined as possible. It should never be called directly, 95 * because the mandatedId mechanism only works if the generateId() method 96 * is overridden to use the mandatedId value. 97 * 98 * @param readUncommittedDefault if true, this transaction does 99 * read-uncommitted by default 100 * @param noWait if true, non-blocking lock requests are used. 101 */ Locker(EnvironmentImpl envImpl, boolean readUncommittedDefault, boolean noWait, long mandatedId)102 protected Locker(EnvironmentImpl envImpl, 103 boolean readUncommittedDefault, 104 boolean noWait, 105 long mandatedId) { 106 107 TxnManager txnManager = envImpl.getTxnManager(); 108 this.lockManager = txnManager.getLockManager(); 109 this.id = generateId(txnManager, mandatedId); 110 this.envImpl = envImpl; 111 this.readUncommittedDefault = readUncommittedDefault; 112 this.waitingFor = null; 113 114 /* get the default lock timeout. */ 115 defaultNoWait = noWait; 116 lockTimeoutMillis = getInitialLockTimeout(); 117 118 /* 119 * Check the default txn timeout. If non-zero, remember the txn start 120 * time. 121 */ 122 txnTimeoutMillis = envImpl.getTxnTimeout(); 123 124 if (txnTimeoutMillis != 0) { 125 txnStartMillis = System.currentTimeMillis(); 126 } else { 127 txnStartMillis = 0; 128 } 129 130 /* Save the thread used to create the locker. */ 131 thread = Thread.currentThread(); 132 133 /* Do lazy initialization of deleteInfo, to conserve memory. */ 134 } 135 136 /** 137 * For reading from the log. 138 */ Locker()139 Locker() { 140 defaultNoWait = false; 141 } 142 getInitialLockTimeout()143 protected long getInitialLockTimeout() { 144 return envImpl.getLockTimeout(); 145 } 146 getEnvironment()147 public EnvironmentImpl getEnvironment() { 148 return envImpl; 149 } 150 151 /** 152 * A Locker has to generate its next id. Some subtypes, like BasicLocker, 153 * have a single id for all instances because they are never used for 154 * recovery. Other subtypes ask the txn manager for an id or use a 155 * specific, mandated id. 156 */ generateId(TxnManager txnManager, long mandatedId)157 protected abstract long generateId(TxnManager txnManager, long mandatedId); 158 159 /** 160 * @return the transaction's id. 161 */ getId()162 public long getId() { 163 return id; 164 } 165 166 /** 167 * @return the default no-wait (non-blocking) setting. 168 */ getDefaultNoWait()169 public boolean getDefaultNoWait() { 170 return defaultNoWait; 171 } 172 173 /** 174 * Get the lock timeout period for this locker, in milliseconds 175 * 176 * WARNING: Be sure to always access the timeout with this accessor, since 177 * it is overridden in BuddyLocker. 178 */ getLockTimeout()179 public synchronized long getLockTimeout() { 180 return lockTimeoutMillis; 181 } 182 183 /** 184 * Set the lock timeout period for any locks in this transaction, 185 * in milliseconds. 186 * 187 * @param timeout The timeout value for the transaction lifetime, in 188 * milliseconds. A value of 0 disables timeouts for the transaction. 189 * 190 * @throws IllegalArgumentException via Transaction.setLockTimeout 191 */ setLockTimeout(long timeout)192 public synchronized void setLockTimeout(long timeout) { 193 194 if (timeout < 0) { 195 throw new IllegalArgumentException 196 ("the timeout value cannot be negative"); 197 } else if (timeout > Math.pow(2, 32)) { 198 throw new IllegalArgumentException 199 ("the timeout value cannot be greater than 2^32"); 200 } 201 202 lockTimeoutMillis = timeout; 203 } 204 205 /** 206 * Set the timeout period for this transaction, in milliseconds. 207 * 208 * @param timeout The timeout value for the transaction lifetime, in 209 * microseconds. A value of 0 disables timeouts for the transaction. 210 * 211 * @throws IllegalArgumentException via Transaction.setLockTimeout 212 */ setTxnTimeout(long timeout)213 public synchronized void setTxnTimeout(long timeout) { 214 215 if (timeout < 0) { 216 throw new IllegalArgumentException 217 ("the timeout value cannot be negative"); 218 } else if (timeout > Math.pow(2, 32)) { 219 throw new IllegalArgumentException 220 ("the timeout value cannot be greater than 2^32"); 221 } 222 223 txnTimeoutMillis = timeout; 224 if (txnTimeoutMillis != 0) { 225 txnStartMillis = System.currentTimeMillis(); 226 } else { 227 txnStartMillis = 0; 228 } 229 } 230 231 /** 232 * @return true if transaction was created with read-uncommitted as a 233 * default. 234 */ isReadUncommittedDefault()235 public boolean isReadUncommittedDefault() { 236 return readUncommittedDefault; 237 } 238 getWaitingFor()239 Lock getWaitingFor() { 240 return waitingFor; 241 } 242 setWaitingFor(Lock lock)243 void setWaitingFor(Lock lock) { 244 waitingFor = lock; 245 } 246 247 /** 248 * Set the state of a transaction to abort-only. Should ONLY be called 249 * by OperationFailureException. 250 */ setOnlyAbortable(OperationFailureException cause)251 public void setOnlyAbortable(OperationFailureException cause) { 252 /* no-op unless Txn. */ 253 } 254 255 /** 256 * Set the state of a transaction's IMPORTUNATE bit. 257 */ setImportunate(boolean importunate)258 public void setImportunate(boolean importunate) { 259 /* no-op unless Txn. */ 260 } 261 262 /** 263 * Get the state of a transaction's IMPORTUNATE bit. 264 */ getImportunate()265 public boolean getImportunate() { 266 return false; 267 } 268 269 /** 270 * Allows/disallows my locks from being stolen/preemted. 271 */ setPreemptable(boolean preemptable)272 public void setPreemptable(boolean preemptable) { 273 this.preemptable = preemptable; 274 } 275 276 /** 277 * Returns whether my locks can be stolen/preemted. 278 */ getPreemptable()279 public boolean getPreemptable() { 280 return preemptable; 281 } 282 283 /** 284 * Called when a lock is stolen from this locker by the HA replayer. 285 */ setPreempted()286 public void setPreempted() { 287 288 /* 289 * Record the stack trace when a lock is stolen. This will provide 290 * more "cause" information when it is wrapped in a 291 * LockPreemptedException that is thrown later -- see checkPreempted. 292 */ 293 preemptedCause = new RuntimeException 294 ("Lock was preempted by the replication replayer"); 295 } 296 297 /** 298 * Called when obtaining a lock to cause a LockPreemptedException to be 299 * thrown if a lock was preempted earlier. 300 * 301 * This operation is split into two methods, checkPreempted and 302 * throwIfPreempted, so that Txn.checkPreempted can call throwIfPreempted 303 * for all its BuddyLockers without causing an infinite recursion. This 304 * method is overridden by BuddyLocker to forward the call to its parent 305 * buddy (the Txn), and by Txn to check all its child buddies. 306 * 307 * @param allowPreemptedLocker is a locker that is being closed as the 308 * result of a cursor move operation. If the operation is successful then 309 * allowPreemptedLocker will be closed, and the fact that a lock has been 310 * stolen from allowPreemptedLocker can be ignored. 311 */ checkPreempted(final Locker allowPreemptedLocker)312 public void checkPreempted(final Locker allowPreemptedLocker) 313 throws OperationFailureException { 314 315 throwIfPreempted(allowPreemptedLocker); 316 } 317 318 /** 319 * Called by checkPreempted to cause a LockPreemptedException to be thrown 320 * if a lock was preempted earlier. Creating the LockPreemptedException 321 * sets the txn to abort-only. 322 * 323 * @see #checkPreempted 324 */ throwIfPreempted(final Locker allowPreemptedLocker)325 final void throwIfPreempted(final Locker allowPreemptedLocker) 326 throws OperationFailureException { 327 328 if (this != allowPreemptedLocker && 329 preemptedCause != null) { 330 throw envImpl.createLockPreemptedException(this, preemptedCause); 331 } 332 } 333 334 /** For unit testing. */ isPreempted()335 final boolean isPreempted() { 336 return (preemptedCause != null); 337 } 338 339 /** 340 * This method is called to set the closingLocker when a cursor has been 341 * duplicated prior to being moved. The new locker is informed of the old 342 * locker, so that a preempted lock taken by the old locker can be ignored. 343 * When the operation is complete, this method is called to clear the 344 * closingLocker so that a reference to the old closed locker is not held 345 * by this object. [#16513] 346 * 347 * @param closingLocker the old locker that will be closed if the new 348 * cursor (using this locker) is moved successfully. 349 */ setClosingLocker(final Locker closingLocker)350 public void setClosingLocker(final Locker closingLocker) { 351 this.closingLocker = closingLocker; 352 } 353 354 /** 355 * See ThreadLocker.allowMultithreadedAccess. 356 */ setAllowMultithreadedAccess(boolean allow)357 boolean setAllowMultithreadedAccess(boolean allow) { 358 /* Do nothing by default. Is overridden by ThreadLocker. */ 359 return false; 360 } 361 checkState(boolean ignoreCalledByAbort)362 protected abstract void checkState(boolean ignoreCalledByAbort) 363 throws DatabaseException; 364 365 /** 366 * Overridden to perform actions in a non-transactional cursor when it is 367 * opened, for example, ReplicaThreadLocker performs consistency checks. 368 */ openCursorHook(DatabaseImpl dbImpl)369 public void openCursorHook(DatabaseImpl dbImpl) { 370 /* Do nothing. */ 371 } 372 373 /** 374 * Returns whether a transaction is method indicates whether the txn is 375 * part of the rep stream. 376 * 377 * A replicated txn must be used for writing to a replicated DB, and a 378 * non-replicated txn must be used for writing to a non-replicated DB. 379 * This is critical for avoiding corruption when HA failovers occur 380 * [#23234] [#23330]. 381 * 382 * See guard in LN.logInternal. 383 */ isReplicated()384 public boolean isReplicated() { 385 return TxnManager.isReplicatedTxn(id); 386 } 387 388 /** 389 * Returns true if writes may only be to non-replicated DBs using this 390 * locker, or false if writes may only be to replicated DBs. 391 * 392 * By default (this implementation) local-write is true, since it is 393 * allowed for all non-txnal lockers and for all lockers in a standalone 394 * environment. This method is overridden and returns false for 395 * for user transactions in a replicated environment that are not 396 * explicitly configured for local-write. 397 * 398 * This method is used to describe a locker's configured usage for checking 399 * the validity of an API write operation. This is checked by Cursor 400 * methods at the beginning of each write operation. 401 */ isLocalWrite()402 public boolean isLocalWrite() { 403 return true; 404 } 405 406 /** 407 * Returns whether writes are prohibited using this locker. 408 */ isReadOnly()409 public boolean isReadOnly() { 410 return false; 411 } 412 413 /* 414 * Obtain and release locks. 415 */ 416 417 /** 418 * Abstract method to a blocking or non-blocking lock of the given type on 419 * the given LSN. Unlike the lock() method, this method does not throw 420 * LockNotAvailableException and can therefore be used by nonBlockingLock 421 * to probe for a lock without the overhead of an exception stack trace. 422 * 423 * @param lsn is the node to lock. 424 * 425 * @param lockType is the type of lock to request. 426 * 427 * @param noWait is true to override the defaultNoWait setting. If true, 428 * or if defaultNoWait is true, throws LockNotAvailableException if the 429 * lock cannot be granted without waiting. 430 * 431 * @param jumpAheadOfWaiters grant the lock before other waiters, if any. 432 * 433 * @param database is the database containing lsn. 434 * 435 * @throws LockConflictException if a blocking lock could not be acquired. 436 */ lockInternal(long lsn, LockType lockType, boolean noWait, boolean jumpAheadOfWaiters, DatabaseImpl database)437 abstract LockResult lockInternal(long lsn, 438 LockType lockType, 439 boolean noWait, 440 boolean jumpAheadOfWaiters, 441 DatabaseImpl database) 442 throws LockConflictException, DatabaseException; 443 444 /** 445 * Request a blocking or non-blocking lock of the given type on the given 446 * LSN. 447 * 448 * @param lsn is the node to lock. 449 * 450 * @param lockType is the type of lock to request. 451 * 452 * @param noWait is true to override the defaultNoWait setting. If true, 453 * or if defaultNoWait is true, throws LockNotAvailableException if the 454 * lock cannot be granted without waiting. 455 * 456 * @param database is the database containing lsn. 457 * 458 * @throws LockNotAvailableException if a non-blocking lock was denied. 459 * 460 * @throws LockConflictException if a blocking lock could not be acquired. 461 */ lock(long lsn, LockType lockType, boolean noWait, DatabaseImpl database)462 public LockResult lock(long lsn, 463 LockType lockType, 464 boolean noWait, 465 DatabaseImpl database) 466 throws LockNotAvailableException, LockConflictException { 467 468 final LockResult result = lockInternal 469 (lsn, lockType, noWait, false /*jumpAheadOfWaiters*/, database); 470 471 if (result.getLockGrant() == LockGrantType.DENIED) { 472 /* DENIED can only be returned for a non-blocking lock. */ 473 throw lockManager.newLockNotAvailableException 474 (this, "Non-blocking lock was denied."); 475 } else { 476 checkPreempted(closingLocker); 477 return result; 478 } 479 } 480 481 /** 482 * Request a non-blocking lock of the given type on the given LSN. 483 * 484 * <p>Unlike lock(), this method returns LockGrantType.DENIED if the lock 485 * is denied rather than throwing LockNotAvailableException. This method 486 * should therefore not be used as the final lock for a user operation, 487 * since in that case LockNotAvailableException should be thrown for a 488 * denied lock. It is normally used only to probe for a lock internally, 489 * and other recourse is taken if the lock is denied.</p> 490 * 491 * @param lsn is the node to lock. 492 * 493 * @param lockType is the type of lock to request. 494 * 495 * @param jumpAheadOfWaiters grant the lock before other waiters, if any. 496 * 497 * @param database is the database containing LSN. 498 */ nonBlockingLock(long lsn, LockType lockType, boolean jumpAheadOfWaiters, DatabaseImpl database)499 public LockResult nonBlockingLock(long lsn, 500 LockType lockType, 501 boolean jumpAheadOfWaiters, 502 DatabaseImpl database) { 503 final LockResult result = lockInternal 504 (lsn, lockType, true /*noWait*/, jumpAheadOfWaiters, database); 505 if (result.getLockGrant() != LockGrantType.DENIED) { 506 checkPreempted(closingLocker); 507 } 508 return result; 509 } 510 511 /** 512 * Release the lock on this LN and remove from the transaction's owning 513 * set. 514 */ releaseLock(long lsn)515 public synchronized boolean releaseLock(long lsn) 516 throws DatabaseException { 517 518 boolean ret = lockManager.release(lsn, this); 519 removeLock(lsn); 520 return ret; 521 } 522 523 /** 524 * Revert this lock from a write lock to a read lock. 525 */ demoteLock(long lsn)526 public void demoteLock(long lsn) 527 throws DatabaseException { 528 529 /* 530 * If successful, the lock manager will call back to the transaction 531 * and adjust the location of the lock in the lock collection. 532 */ 533 lockManager.demote(lsn, this); 534 } 535 536 /** 537 * Called when an LN is logged by an operation that will not hold the lock 538 * such as eviction/checkpoint deferred-write logging or cleaner LN 539 * migration. We must acquire a lock on the new LSN on behalf of every 540 * locker that currently holds a lock on the old LSN. 541 * 542 * Lock is non-blocking because no contention is possible on the new LSN. 543 * 544 * Because this locker is being used by multiple threads, this method may 545 * be called for a locker that has been closed or for which the lock on the 546 * old LSN has been released. Unlike other locking methods, in this case 547 * we simply return rather than report an error. 548 */ lockAfterLsnChange(long oldLsn, long newLsn, DatabaseImpl dbImpl)549 public synchronized void lockAfterLsnChange(long oldLsn, 550 long newLsn, 551 DatabaseImpl dbImpl) { 552 if (!isValid()) { 553 /* Locker was recently closed, made abort-only, etc. */ 554 return; 555 } 556 557 final LockType lockType = lockManager.getOwnedLockType(oldLsn, this); 558 if (lockType == null) { 559 /* Lock was recently released. */ 560 return; 561 } 562 563 final LockResult lockResult = nonBlockingLock 564 (newLsn, lockType, false /*jumpAheadOfWaiters*/, dbImpl); 565 566 if (lockResult.getLockGrant() == LockGrantType.DENIED) { 567 throw EnvironmentFailureException.unexpectedState 568 ("No contention is possible on new LSN: " + 569 DbLsn.getNoFormatString(newLsn) + 570 " old LSN: " + DbLsn.getNoFormatString(oldLsn) + 571 " LockType: " + lockType); 572 } 573 } 574 575 /** 576 * In the case where logging occurs before locking, allow lockers to reject 577 * the operation (e.g., if writing on a replica) and also prepare to undo 578 * in the (very unlikely) event that logging succeeds but locking fails. 579 */ preLogWithoutLock(DatabaseImpl database)580 public abstract void preLogWithoutLock(DatabaseImpl database); 581 582 /** 583 * Throws ReplicaWriteException if called for a locker on a Replica. This 584 * implementation does nothing but is overridden by replication lockers. 585 * [#20543] 586 */ disallowReplicaWrite()587 public void disallowReplicaWrite() { 588 } 589 590 /** 591 * Returns whether this locker is transactional. 592 */ isTransactional()593 public abstract boolean isTransactional(); 594 595 /** 596 * Returns whether the isolation level of this locker is serializable. 597 */ isSerializableIsolation()598 public abstract boolean isSerializableIsolation(); 599 600 /** 601 * Returns whether the isolation level of this locker is read-committed. 602 */ isReadCommittedIsolation()603 public abstract boolean isReadCommittedIsolation(); 604 605 /** 606 * Returns the underlying Txn if the locker is transactional, or null if 607 * the locker is non-transactional. For a Txn-based locker, this method 608 * returns 'this'. For a BuddyLocker, this method may return the buddy. 609 */ getTxnLocker()610 public abstract Txn getTxnLocker(); 611 612 /** 613 * Returns a Transaction if the locker is transctional, or null otherwise. 614 */ getTransaction()615 public Transaction getTransaction() { 616 return null; 617 } 618 619 /** 620 * Only BuddyLockers have buddies. 621 */ getBuddy()622 Locker getBuddy() { 623 return null; 624 } 625 626 /** 627 * Creates a fresh non-transactional locker, while retaining any 628 * transactional locks held by this locker. This method is called when the 629 * cursor for this locker is cloned. 630 * 631 * <p>This method must return a locker that shares locks with this 632 * locker, e.g., a ThreadLocker.</p> 633 * 634 * <p>In general, transactional lockers return 'this' when this method is 635 * called, while non-transactional lockers return a new instance.</p> 636 */ newNonTxnLocker()637 public abstract Locker newNonTxnLocker() 638 throws DatabaseException; 639 640 /** 641 * Releases any non-transactional locks held by this locker. This method 642 * is called when the cursor moves to a new position or is closed. 643 * 644 * <p>In general, transactional lockers do nothing when this method is 645 * called, while non-transactional lockers release all locks as if 646 * operationEnd were called.</p> 647 */ releaseNonTxnLocks()648 public abstract void releaseNonTxnLocks() 649 throws DatabaseException; 650 651 /** 652 * Releases locks and closes the locker at the end of a non-transactional 653 * cursor operation. For a transctional cursor this method should do 654 * nothing, since locks must be held until transaction end. 655 */ nonTxnOperationEnd()656 public abstract void nonTxnOperationEnd() 657 throws DatabaseException; 658 659 /** 660 * By default the set of buddy lockers is not maintained. This is 661 * overridden by Txn. 662 */ addBuddy(BuddyLocker buddy)663 void addBuddy(BuddyLocker buddy) { 664 } 665 666 /** 667 * By default the set of buddy lockers is not maintained. This is 668 * overridden by Txn. 669 */ removeBuddy(BuddyLocker buddy)670 void removeBuddy(BuddyLocker buddy) { 671 } 672 673 /** 674 * Returns whether this locker can share locks with the given locker. 675 */ sharesLocksWith(Locker other)676 public boolean sharesLocksWith(Locker other) { 677 return false; 678 } 679 680 /** 681 * The equivalent of calling operationEnd(true). 682 */ operationEnd()683 public final void operationEnd() 684 throws DatabaseException { 685 686 operationEnd(true); 687 } 688 689 /** 690 * A SUCCESS status equals operationOk. 691 */ operationEnd(OperationStatus status)692 public final void operationEnd(OperationStatus status) 693 throws DatabaseException { 694 695 operationEnd(status == OperationStatus.SUCCESS); 696 } 697 698 /** 699 * Different types of transactions do different things when the operation 700 * ends. Txn does nothing, auto Txn commits or aborts, and BasicLocker (and 701 * its subclasses) just releases locks. 702 * 703 * @param operationOK is whether the operation succeeded, since 704 * that may impact ending behavior. (i.e for an auto Txn) 705 */ operationEnd(boolean operationOK)706 public abstract void operationEnd(boolean operationOK) 707 throws DatabaseException; 708 709 /** 710 * Should be called by all subclasses when the locker is no longer used. 711 * For Txns and auto Txns this is at commit or abort. For 712 * non-transactional lockers it is at operationEnd. 713 */ close()714 void close() 715 throws DatabaseException { 716 717 isOpen = false; 718 } 719 720 /** 721 * Used to determine whether the locker is usable. 722 * 723 * FUTURE: Note that this method is overridden by Txn, and Txn.abort sets 724 * the state to closed when it begins rather than when it ends, but calls 725 * close() (the method above) when it ends. This is not ideal and deserves 726 * attention in the future. 727 */ isValid()728 public boolean isValid() { 729 return isOpen; 730 } 731 732 /** 733 * @see Txn#addOpenedDatabase 734 */ addOpenedDatabase(Database dbHandle)735 public void addOpenedDatabase(Database dbHandle) { 736 } 737 738 /** 739 * @see HandleLocker#allowReleaseLockAfterLsnChange 740 */ allowReleaseLockAfterLsnChange()741 public boolean allowReleaseLockAfterLsnChange() { 742 return false; 743 } 744 745 /** 746 * Tell this transaction about a cursor. 747 */ registerCursor(CursorImpl cursor)748 public abstract void registerCursor(CursorImpl cursor); 749 750 /** 751 * Remove a cursor from this txn. 752 */ unRegisterCursor(CursorImpl cursor)753 public abstract void unRegisterCursor(CursorImpl cursor); 754 755 /** 756 * Returns true if locking is required for this Locker. All Txnal lockers 757 * require it; most BasicLockers do not, but BasicLockers on internal dbs 758 * do. 759 */ lockingRequired()760 public abstract boolean lockingRequired(); 761 762 /* 763 * Transactional support 764 */ 765 766 /** 767 * @return the WriteLockInfo for this node. 768 */ getWriteLockInfo(long lsn)769 public abstract WriteLockInfo getWriteLockInfo(long lsn); 770 771 /** 772 * Database operations like remove and truncate leave behind 773 * residual DatabaseImpls that must be purged at transaction 774 * commit or abort. 775 */ markDeleteAtTxnEnd(DatabaseImpl db, boolean deleteAtCommit)776 public abstract void markDeleteAtTxnEnd(DatabaseImpl db, 777 boolean deleteAtCommit) 778 throws DatabaseException; 779 780 /** 781 * Add delete information, to be added to the inCompressor queue when the 782 * transaction ends. 783 */ addDeleteInfo(BIN bin)784 public void addDeleteInfo(BIN bin) { 785 786 /* 787 * Skip queue addition if a delta will be logged. In this case the 788 * slot compression will occur in BIN.beforeLog, when a full version is 789 * logged. 790 */ 791 if (bin.shouldLogDelta()) { 792 return; 793 } 794 795 synchronized (this) { 796 /* Maintain only one binRef per node. */ 797 if (deleteInfo == null) { 798 deleteInfo = new HashMap<Long,BINReference>(); 799 } 800 Long nodeId = Long.valueOf(bin.getNodeId()); 801 if (deleteInfo.containsKey(nodeId)) { 802 return; 803 } 804 deleteInfo.put(nodeId, bin.createReference()); 805 } 806 } 807 808 /* 809 * Manage locks owned by this transaction. Note that transactions that will 810 * be multithreaded must override these methods and provide synchronized 811 * implementations. 812 */ 813 814 /** 815 * Add a lock to set owned by this transaction. 816 */ addLock(Long lsn, LockType type, LockGrantType grantStatus)817 protected abstract void addLock(Long lsn, 818 LockType type, 819 LockGrantType grantStatus) 820 throws DatabaseException; 821 822 /** 823 * Remove the lock from the set owned by this transaction. If specified to 824 * LockManager.release, the lock manager will call this when its releasing 825 * a lock. 826 */ removeLock(long lsn)827 abstract void removeLock(long lsn) 828 throws DatabaseException; 829 830 /** 831 * A lock is being demoted. Move it from the write collection into the read 832 * collection. 833 */ moveWriteToReadLock(long lsn, Lock lock)834 abstract void moveWriteToReadLock(long lsn, Lock lock); 835 836 /** 837 * Get lock count, for per transaction lock stats, for internal debugging. 838 */ collectStats()839 public abstract StatGroup collectStats() 840 throws DatabaseException; 841 842 /* 843 * Check txn timeout, if set. Called by the lock manager when blocking on a 844 * lock. 845 */ isTimedOut()846 public boolean isTimedOut() { 847 long timeout = getTxnTimeout(); 848 if (timeout != 0) { 849 long diff = System.currentTimeMillis() - txnStartMillis; 850 if (diff > timeout) { 851 return true; 852 } 853 } 854 return false; 855 } 856 857 /** 858 * Get the transaction timeout period for this locker, in milliseconds 859 * 860 * public for jca/ra/JELocalTransaction. 861 * 862 * WARNING: Be sure to always access the timeout with this accessor, since 863 * it is overridden in BuddyLocker. 864 */ getTxnTimeout()865 public synchronized long getTxnTimeout() { 866 return txnTimeoutMillis; 867 } 868 getTxnStartMillis()869 long getTxnStartMillis() { 870 return txnStartMillis; 871 } 872 873 /** 874 * @return if this locker has ever been rolled back. 875 */ isRolledBack()876 public boolean isRolledBack() { 877 return false; // most Locker types will never roll back. 878 } 879 880 /** 881 * This method is safe to call without synchronizing and this fact is 882 * relied on by LockManager when creating exception messages. 883 */ 884 @Override toString()885 public String toString() { 886 String className = getClass().getName(); 887 className = className.substring(className.lastIndexOf('.') + 1); 888 889 return System.identityHashCode(this) + " " + Long.toString(id) + "_" + 890 ((thread == null) ? "" : thread.getName()) + "_" + 891 className; 892 } 893 894 /** 895 * Dump lock table, for debugging 896 */ dumpLockTable()897 public void dumpLockTable() 898 throws DatabaseException { 899 900 lockManager.dump(); 901 } 902 } 903