org.jsimpledb.kv.KVTransaction Maven / Gradle / Ivy
Show all versions of jsimpledb-kv Show documentation
/*
* Copyright (C) 2015 Archie L. Cobbs. All rights reserved.
*/
package org.jsimpledb.kv;
import java.util.concurrent.Future;
/**
* {@link KVDatabase} transaction API.
*
*
* Provides a transactional view of a {@link KVStore}.
*
*
* Instances may throw {@link KVTransactionException} during any operation if the transaction cannot be continued.
* In particular, {@link StaleTransactionException} is thrown by a transaction that is no longer open, and
* {@link RetryTransactionException} is thrown when the transaction should be retried due to a transient
* problem (such as a write conflict with another transaction).
*
*
* When {@link RetryTransactionException} is thrown by {@link #commit}, the transaction may have actually been committed.
* Therefore, transactions should be written to be idempotent.
*
*
* No matter what state it is in, instances must support invoking {@link #rollback} at any time.
*
*
* If an instance throws a {@link KVTransactionException}, the transaction should be implicitly rolled back.
* Any subsequent operation other than {@link #rollback} should throw {@link StaleTransactionException}.
*
*
* Except for {@link #rollback} and methods that just query status, implementations must throw {@link StaleTransactionException}
* if {@link #commit} or {@link #rollback} has already been invoked, or if the {@link KVTransaction} instance is no longer usable
* for some other reason. In particular, implementations should throw {@link TransactionTimeoutException} if an operation
* is attempted on a transaction that has been held open past some maximum allowed time limit.
*
*
* Implementations are responsible for ensuring modifications to {@code byte[]} arrays after method
* invocations do no harm. This usually means {@code byte[]} array parameters and return values must be copied.
*
*
* Implementations are not required to support accessing keys that start with {@code 0xff},
* and if not may throw {@link IllegalArgumentException} if such keys are accessed.
*
*
* Note: for some implementations, the data read from a transaction that is never {@link #commit}'ed is
* not guaranteed to be up to date.
*/
public interface KVTransaction extends KVStore {
/**
* Get the {@link KVDatabase} with which this instance is associated.
*
* @return associated database
*/
KVDatabase getKVDatabase();
/**
* Change the timeout for this transaction from its default value (optional operation).
*
* @param timeout transaction timeout in milliseconds, or zero for unlimited
* @throws UnsupportedOperationException if this transaction does not support timeouts
* @throws IllegalArgumentException if {@code timeout} is negative
* @throws StaleTransactionException if this transaction is no longer usable
*/
void setTimeout(long timeout);
/**
* Determine whether this transaction is read-only.
*
*
* Default is false.
*
* @return true if this instance is read-only
* @throws StaleTransactionException if this transaction is no longer usable
*/
boolean isReadOnly();
/**
* Enable or disable read-only mode.
*
*
* Read-only transactions allow mutations, but all changes are discarded on {@link #commit}.
*
*
* Some implementations may impose one or more of the following restrictions on this method:
*
* - {@link #setReadOnly setReadOnly()} may only be invoked prior to accessing data;
* - {@link #setReadOnly setReadOnly()} may only be invoked prior to mutating data; and/or
* - Once set to read-only, a transaction may not be set back to read-write
*
*
*
* Note: for some implementations, the data read from a transaction that is never {@link #commit}'ed is
* not guaranteed to be up to date, even if that transaction is read-only.
*
*
* Default is false.
*
* @param readOnly read-only setting
* @throws IllegalStateException if the implementation doesn't support changing read-only status at this time
* @throws StaleTransactionException if this transaction is no longer usable
*/
void setReadOnly(boolean readOnly);
/**
* Watch a key to monitor for changes in its value.
*
*
* When this method is invoked, {@code key}'s current value (if any) as read by this transaction is remembered. The returned
* {@link Future} completes if and when a different value for {@code key} is subsequently committed by some transaction,
* including possibly this one. This includes creation or deletion of the key.
*
*
* Key watches outlive the transaction in which they are created, persisting until they complete or are
* {@link Future#cancel cancel()}'ed. When a {@link KVDatabase} is {@link KVDatabase#stop}'ed, all outstanding
* key watches are implicitly {@link Future#cancel cancel()}'ed.
*
*
Caveats
*
*
* Key watches are not without overhead; applications should avoid overuse. For example, consider creating a
* single key that is used to consolidate modifications to some set of keys; at the JSimpleDB layer, modification
* to multiple objects and/or fields can detected and consolidated using an
* {@link org.jsimpledb.annotation.OnChange @OnChange} method that increments a single {@link org.jsimpledb.Counter}
* field, whose key is then watched (to determine the key corresponding to a Java model object field, use
* {@link org.jsimpledb.JTransaction#getKey(org.jsimpledb.JObject, String) JTransaction.getKey()}).
*
*
* Conceptually, detection of changes behaves as if by a background thread that periodically creates a new transaction
* and reads the key's value (the actual implementation will likely be more efficient). This means a change that is
* quickly reverted could be missed, and that multiple changes could occur before notification. In addition, spurious
* notifications may occur, where the key's value has not changed.
*
*
* A key watch is only guaranteed to be valid if the transaction in which it was created successfully commits.
* In particular, nothing is specified about how or whether {@link Future}s associated with failed transactions complete,
* so the {@link Future}s returned by this method should not be relied on until after a successful commit (perhaps with
* the help of a {@linkplain org.jsimpledb.core.Transaction#addCallback transaction callback}).
*
*
* Key watch support is optional; instances that don't support key watches throw {@link UnsupportedOperationException}.
* Some implementations may only support watching a key that already exists.
*
*
* Note: many {@link KVDatabase} implementations actually return a
* {@link com.google.common.util.concurrent.ListenableFuture}. However, listeners must not perform any
* long running or blocking operations. Also, because the semantics of {@link RetryTransactionException} allow for
* the possibility that the transaction actually did commit, "duplicate" listener notifications could occur.
*
*
* Key watch {@link Future}s that have not completed yet, but are no longer needed, must be {@link Future#cancel cancel()}'ed
* to avoid memory leaks.
*
* @param key the key to watch
* @return a {@link Future} that returns {@code key} when the value associated with {@code key} is modified
* @throws StaleTransactionException if this transaction is no longer usable
* @throws RetryTransactionException if this transaction must be retried and is no longer usable
* @throws KVDatabaseException if an unexpected error occurs
* @throws UnsupportedOperationException if this instance does not support key watches
* @throws IllegalArgumentException if {@code key} starts with {@code 0xff} and such keys are not supported
* @throws IllegalArgumentException if {@code key} is null
* @see org.jsimpledb.JTransaction#getKey(org.jsimpledb.JObject, String) JTransaction.getKey()
*/
Future watchKey(byte[] key);
/**
* Commit this transaction.
*
*
* Note that if this method throws a {@link RetryTransactionException},
* the transaction was either successfully committed or rolled back. In either case,
* this instance is no longer usable.
*
*
* Note also for some implementations, even read-only transactions must be {@link #commit}'ed in order for the
* data accessed during the transaction to be guaranteed to be up to date.
*
* @throws StaleTransactionException if this transaction is no longer usable
* @throws RetryTransactionException if this transaction must be retried and is no longer usable
*/
void commit();
/**
* Cancel this transaction, if not already canceled.
*
*
* After this method returns, this instance is no longer usable.
*
*
* Note: for some implementations, rolling back a transaction invalidates guarantees about the the data read
* during the transaction being up to date, even if the transaction was {@link #setReadOnly setReadOnly()}.
*
*
* This method may be invoked at any time, even after a previous invocation of
* {@link #commit} or {@link #rollback}, in which case the invocation will be ignored.
* In particular, this method should not throw {@link StaleTransactionException}.
*/
void rollback();
/**
* Create a mutable copy of the database content represented by this transaction.
*
*
* The returned {@link CloseableKVStore} should be mutable, but all changes should remain private until
* {@link CloseableKVStore#close close()} is invoked, at which time they should be discarded.
* That is, the {@link CloseableKVStore} it is completely independent from this transaction
* (subsequent changes to either one do not affect the other).
*
*
* Note that as with any other information extracted from a {@link KVTransaction}, the returned content
* should not be considered valid until this transaction has been successfully committed.
*
*
* The returned {@link CloseableKVStore} should be promply {@link CloseableKVStore#close close()}'d when no longer
* needed to release any underlying resources. In particular, the caller must ensure that the {@link CloseableKVStore}
* is {@link CloseableKVStore#close close()}'d even if this transaction's commit fails. This may require
* adding a transaction synchronization callback, etc.
*
*
* This is an optional method; only some underlying key/value store technologies can efficiently support it.
* Implementations should throw {@link UnsupportedOperationException} if not supported.
*
* @return independent, mutable copy of this transaction's entire database content
* @throws UnsupportedOperationException if this method is not supported
* @throws StaleTransactionException if this transaction is no longer usable
* @throws RetryTransactionException if this transaction must be retried and is no longer usable
*/
CloseableKVStore mutableSnapshot();
}