blob: 94fbd211d990521fc19acfc8937dbd45a9064369 [file] [log] [blame]
/*-
* Copyright (C) 2002, 2018, Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle Berkeley
* DB Java Edition made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/berkeleydb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle Berkeley DB Java Edition for a copy of the
* license and additional information.
*/
package com.sleepycat.je;
/**
* Record lock modes for read operations. Lock mode parameters may be specified
* for all operations that retrieve data.
*
* <p><strong>Locking Rules</strong></p>
*
* <p>Together with {@link CursorConfig}, {@link TransactionConfig} and {@link
* EnvironmentConfig} settings, lock mode parameters determine how records are
* locked during read operations. Record locking is used to enforce the
* isolation modes that are configured. Record locking is summarized below for
* read and write operations. For more information on isolation levels and
* transactions, see <a
* href="{@docRoot}/../TransactionGettingStarted/index.html"
* target="_top">Writing Transactional Applications</a>.</p>
*
* <p>With one exception, a record lock is always acquired when a record is
* read or written, and a cursor will always hold the lock as long as it is
* positioned on the record. The exception is when {@link #READ_UNCOMMITTED}
* is specified, which allows a record to be read without any locking.</p>
*
* <p>Both read (shared) and write (exclusive) locks are used. Read locks are
* normally acquired on read ({@code get} method) operations and write locks on
* write ({@code put} method) operations. The only exception is that a write
* lock will be acquired on a read operation if {@link #RMW} is specified.</p>
*
* <p>Because read locks are shared, multiple accessors may read the same
* record. Because write locks are exclusive, if a record is written by one
* accessor it may not be read or written by another accessor. An accessor is
* either a transaction or a thread (for non-transactional operations).</p>
*
* <p>Whether additional locking is performed and how locks are released depend
* on whether the operation is transactional and other configuration
* settings.</p>
*
* <p><strong>Transactional Locking</strong></p>
*
* <p>Transactional operations include all write operations for a transactional
* database, and read operations when a non-null {@link Transaction} parameter
* is passed. When a null transaction parameter is passed for a write
* operation for a transactional database, an auto-commit transaction is
* automatically used.</p>
*
* <p>With transactions, read and write locks are normally held until the end
* of the transaction (commit or abort). Write locks are always held until the
* end of the transaction. However, if {@link #READ_COMMITTED} is configured,
* then read locks for cursor operations are only held during the operation and
* while the cursor is positioned on the record. The read lock is released
* when the cursor is moved to a different record or closed. When {@link
* #READ_COMMITTED} is used for a database (non-cursor) operation, the read
* lock is released before the method returns.</p>
*
* <p>When neither {@link #READ_UNCOMMITTED} nor {@link #READ_COMMITTED} is
* specified, read and write locking as described above provide Repeatable Read
* isolation, which is the default transactional isolation level. If
* Serializable isolation is configured, additional "next key" locking is
* performed to prevent "phantoms" -- records that are not visible at one point
* in a transaction but that become visible at a later point after being
* inserted by another transaction. Serializable isolation is configured via
* {@link TransactionConfig#setSerializableIsolation} or {@link
* EnvironmentConfig#setTxnSerializableIsolation}.</p>
*
* <p><strong>Non-Transactional Locking</strong></p>
*
* <p>Non-transactional operations include all operations for a
* non-transactional database (including a Deferred Write database), and read
* operations for a transactional database when a null {@link Transaction}
* parameter is passed.</p>
*
* <p>For non-transactional operations, both read and write locks are only held
* while a cursor is positioned on the record, and are released when the cursor
* is moved to a different record or closed. For database (non-cursor)
* operations, the read or write lock is released before the method
* returns.</p>
*
* <p>This behavior is similar to {@link #READ_COMMITTED}, except that both
* read and write locks are released. Configuring {@link #READ_COMMITTED} for
* a non-transactional database cursor has no effect.</p>
*
* <p>Because the current thread is the accessor (locker) for non-transactional
* operations, a single thread may have multiple cursors open without locking
* conflicts. Two non-transactional cursors in the same thread may access the
* same record via write or read operations without conflicts, and the changes
* make by one cursor will be visible to the other cursor.</p>
*
* <p>However, a non-transactional operation will conflict with a transactional
* operation for the same record even when performed in the same thread. When
* using a transaction in a particular thread for a particular database, to
* avoid conflicts you should use that transaction for all access to that
* database in that thread. In other words, to avoid conflicts always pass the
* transaction parameter, not null, for all operations. If you don't wish to
* hold the read lock for the duration of the transaction, specify {@link
* #READ_COMMITTED}.</p>
*
* <p><strong>Read Uncommitted (Dirty-Read)</strong></p>
*
* <p>When {@link #READ_UNCOMMITTED} is configured, no locking is performed
* by a read operation. {@code READ_UNCOMMITTED} does not apply to write
* operations.</p>
*
* <p>{@code READ_UNCOMMITTED} is sometimes called dirty-read because records
* are visible to the caller in their current state in the Btree at the time of
* the read, even when that state is due to operations performed using a
* transaction that has not yet committed. In addition, because no lock is
* acquired by the dirty read operation, the record's state may change at any
* time, even while a cursor used to do the dirty-read is still positioned on
* the record.</p>
*
* <p>To illustrate this, let's say a record is read with dirty-read
* ({@code READ_UNCOMMITTED}) by calling {@link Cursor#getNext Cursor.getNext}
* with a cursor C, and changes to the record are also being made in another
* thread using transaction T. When a locking (non-dirty-read) call to {@link
* Cursor#getCurrent Cursor.getCurrent} is subsequently made to read the same
* record again with C at the current position, a result may be returned that
* is different than the result returned by the earlier call to {@code
* getNext}. For example:
* <ul>
* <li>If the record is updated by T after the dirty-read {@code getNext}
* call, and T is committed, a subsequent call to {@code getCurrent} will
* return the data updated by T.</li>
*
* <li>If the record is updated by T before the dirty-read {@code getNext}
* call, the {@code getNext} will returned the data updated by T. But if
* call, the {@code getNext} will return the data updated by T. But if
* T is then aborted, a subsequent call to {@code getCurrent} will return
* the version of the data before it was updated by T.</li>
*
* <li>If the record was inserted by T before the dirty-read {@code
* getNext} call, the {@code getNext} call will return the inserted record.
* But if T is aborted, a subsequent call to {@code getCurrent} will return
* {@link OperationStatus#KEYEMPTY}.</li>
*
* <li>If the record is deleted by T after the dirty-read {@code getNext}
* call, and T is committed, a subsequent call to {@code getCurrent} will
* return {@link OperationStatus#KEYEMPTY}.</li>
* </ul>
*
* <p>Note that deleted records are handled specially in JE. Deleted records
* remain in the Btree until after the deleting transaction is committed, and
* they are removed from the Btree asynchronously (not immediately at commit
* time). When using {@code #READ_UNCOMMITTED}, any record encountered in the
* Btree that was previously deleted, whether or not the deleting transaction
* has been committed, will be ignored (skipped over) by the read operation.
* Of course, if the deleting transaction is aborted, the record will no longer
* be deleted. If the application is scanning records, for example, this means
* that such records may be skipped by the scan. If this behavior is not
* desirable, {@link #READ_UNCOMMITTED_ALL} may be used instead. This mode
* ensures that records deleted by a transaction that is later aborted will not
* be skipped by a read operation. This is accomplished in two different ways
* depending on the type of database and whether the record's data is requested
* by the operation.
* <ol>
* <li>If the DB is configured for duplicates or the record's data
* is not requested, then a record that has been deleted by an open
* transaction is returned by the read operation.</li>
*
* <li>If the DB is not configured for duplicates and the record's data is
* requested, then the read operation must wait for the deleting
* transaction to close (commit or abort). After the transaction is
* closed, the record will be returned if it is actually not deleted and
* otherwise will be skipped.</li>
* </ol>
*
* <p>By "record data" we mean both the {@code data} parameter for a regular or
* primary DB, and the {@code pKey} parameter for a secondary DB. By "record
* data requested" we mean that all or part of the {@code DatabaseEntry} will
* be returned by the read operation. Unless explicitly <em>not</em>
* requested, the complete {@code DatabaseEntry} is returned. See
* <a href="Cursor.html#partialEntry">Using Partial DatabaseEntry
* Parameters</a> for more information.</p>
*
* <p>Because of this difference in behavior, although {@code
* #READ_UNCOMMITTED} is fully non-blocking, {@code #READ_UNCOMMITTED_ALL} is
* not (under the conditions described). As a result, when using {@code
* #READ_UNCOMMITTED_ALL} under these conditions, a {@link
* LockConflictException} will be thrown when blocking results in a deadlock or
* lock timeout.</p>
*
* <p>To summarize, callers that use {@code READ_UNCOMMITTED} or {@code
* #READ_UNCOMMITTED_ALL} should be prepared for the following behaviors.
* <ul>
* <li>After a successful dirty-read operation, because no lock is acquired
* the record can be changed by another transaction, even when the cursor
* used to perform the dirty-read operation is still positioned on the
* record.</li>
*
* <li>After a successful dirty-read operation using a cursor C, say that
* another transaction T deletes the record, and T is committed. In this
* case, {@link OperationStatus#KEYEMPTY} will be returned by the following
* methods if they are called while C is still positioned on the deleted
* record: {@link Cursor#getCurrent Cursor.getCurrent}, {@link
* Cursor#putCurrent Cursor.putCurrent} and {@link Cursor#delete
* Cursor.delete}.</li>
*
* <li>When using {@code READ_UNCOMMITTED}, deleted records will be skipped
* even when the deleting transaction is still open. No blocking will occur
* and {@link LockConflictException} is never thrown when using this
* mode.</li>
*
* <li>When using {@code #READ_UNCOMMITTED_ALL}, deleted records will not
* be skipped even when the deleting transaction is open. If the DB is a
* duplicates DB or the record's data is not requested, the deleted record
* will be returned. If the DB is not a duplicates DB and the record's
* data is requested, blocking will occur until the deleting transaction is
* closed. In the latter case, {@link LockConflictException} will be thrown
* when this blocking results in a deadlock or a lock timeout.</li>
* </ul>
*/
public enum LockMode {
/**
* Uses the default lock mode and is equivalent to passing {@code null} for
* the lock mode parameter.
*
* <p>The default lock mode is {@link #READ_UNCOMMITTED} when this lock
* mode is configured via {@link CursorConfig#setReadUncommitted} or {@link
* TransactionConfig#setReadUncommitted}, or when using a {@link
* DiskOrderedCursor}. The Read Uncommitted mode overrides any other
* configuration settings.</p>
*
* <p>Otherwise, the default lock mode is {@link #READ_COMMITTED} when this
* lock mode is configured via {@link CursorConfig#setReadCommitted} or
* {@link TransactionConfig#setReadCommitted}. The Read Committed mode
* overrides other configuration settings except for {@link
* #READ_UNCOMMITTED}.</p>
*
* <p>Otherwise, the default lock mode is to acquire read locks and release
* them according to the {@link LockMode default locking rules} for
* transactional and non-transactional operations.</p>
*/
DEFAULT,
/**
* Reads modified but not yet committed data.
*
* <p>The Read Uncommitted mode is used if this lock mode is explicitly
* passed for the lock mode parameter, or if null or {@link #DEFAULT} is
* passed and Read Uncommitted is the default -- see {@link #DEFAULT} for
* details.</p>
*
* <p>Unlike {@link #READ_UNCOMMITTED_ALL}, deleted records will be skipped
* even when the deleting transaction is still open. No blocking will occur
* and {@link LockConflictException} is never thrown when using this
* mode.</p>
*
* <p>See the {@link LockMode locking rules} for information on how Read
* Uncommitted impacts transactional and non-transactional locking.</p>
*/
READ_UNCOMMITTED,
/**
* Reads modified but not yet committed data, ensuring that records are not
* skipped due to transaction aborts.
*
* <p>The Read Uncommitted mode is used only when this lock mode is
* explicitly passed for the lock mode parameter.</p>
*
* <p>Unlike {@link #READ_UNCOMMITTED}, deleted records will not be skipped
* even when the deleting transaction is open. If the DB is a duplicates DB
* or the record's data is not requested, the deleted record will be
* returned. If the DB is not a duplicates DB and the record's data is
* requested, blocking will occur until the deleting transaction is closed.
* In the latter case, {@link LockConflictException} will be thrown when
* this blocking results in a deadlock or a lock timeout.</p>
*
* <p>See the {@link LockMode locking rules} for information on how Read
* Uncommitted impacts transactional and non-transactional locking.</p>
*/
READ_UNCOMMITTED_ALL,
/**
* Read committed isolation provides for cursor stability but not
* repeatable reads. Data items which have been previously read by this
* transaction may be deleted or modified by other transactions before the
* cursor is closed or the transaction completes.
*
* <p>Note that this LockMode may only be passed to {@link Database} get
* methods, not to {@link Cursor} methods. To configure a cursor for Read
* Committed isolation, use {@link CursorConfig#setReadCommitted}.</p>
*
* <p>See the {@link LockMode locking rules} for information on how Read
* Committed impacts transactional and non-transactional locking.</p>
*
* @see <a href="EnvironmentStats.html#cacheUnexpectedSizes">Cache
* Statistics: Unexpected Sizes</a>
*/
READ_COMMITTED,
/**
* Acquire write locks instead of read locks when doing the retrieval.
*
* <p>Because it causes a write lock to be acquired, specifying this lock
* mode as a {@link Cursor} or {@link Database} {@code get} (read) method
* parameter will override the Read Committed or Read Uncommitted isolation
* mode that is configured using {@link CursorConfig} or {@link
* TransactionConfig}. The write lock will acquired and held until the end
* of the transaction. For non-transactional use, the write lock will be
* released when the cursor is moved to a new position or closed.</p>
*
* <p>Setting this flag can eliminate deadlock during a read-modify-write
* cycle by acquiring the write lock during the read part of the cycle so
* that another thread of control acquiring a read lock for the same item,
* in its own read-modify-write cycle, will not result in deadlock.</p>
*/
RMW;
private final ReadOptions readOptions;
LockMode() {
readOptions = new ReadOptions().setLockMode(this);
}
/**
* Returns a ReadOptions with this LockMode property, and default values
* for all other properties.
*
* <p>WARNING: Do not modify the returned object, since it is a singleton.
*
* @since 7.0
*/
public ReadOptions toReadOptions() {
return readOptions;
}
public String toString() {
return "LockMode." + name();
}
}