
io.github.hse_project.hse.KvdbTransaction Maven / Gradle / Ivy
/* SPDX-License-Identifier: Apache-2.0
*
* Copyright (C) 2021-2022 Micron Technology, Inc. All rights reserved.
*/
package io.github.hse_project.hse;
/**
* The HSE KVDB provides transactions with operations spanning KVSs within a
* single KVDB. These transactions have snapshot isolation (a specific form of
* MVCC) with the normal semantics (see "Concurrency Control and Recovery in
* Database Systems" by PA Bernstein).
*
*
* One unusual aspect of the API as it relates to transactions is that the data
* object that is used to hold client-level transaction state is allocated
* separately from the transaction being initiated. As a result, the same object
* handle should be reused again and again.
*
*
*
* In addition, there is very limited coupling between threading and
* transactions. A single thread may have many transactions in flight
* simultaneously. Also operations within a transaction can be performed by
* multiple threads. The latter mode of operation must currently restrict calls
* so that only one thread is actively performing an operation in the context of
* a particular transaction at any particular time.
*
*
*
* The general lifecycle of a transaction is as follows:
*
*
*
* +----------+
* | INVALID |
* +----------+
* |
* v
* +----------+
* +---------------->| ACTIVE |<----------------+
* | +----------+ |
* | +-----------+ | | +----------+ |
* +--| COMMITTED |<---+ +---->| ABORTED |--+
* +-----------+ +----------+
*
*
*
* When a transaction is initially allocated, it starts in the INVALID state.
* When {@link #begin()} is called with transaction in the INVALID, COMMITTED,
* or ABORTED states, it moves to the ACTIVE state. It is an error to call the
* {@link #begin()} function on a transaction in the ACTIVE state. For a
* transaction in the ACTIVE state, only the functions {@link #commit()},
* {@link #abort()}, or {@link #close()} may be called (with the last doing an
* abort prior to the free).
*
*
*
* When a transaction becomes ACTIVE, it establishes an ephemeral snapshot view
* of the state of the KVDB. Any data mutations outside of the transaction's
* context after that point are not visible to the transaction. Similarly, any
* mutations performed within the context of the transaction are not visible
* outside of the transaction unless and until it is committed. All such
* mutations become visible atomically when the transaction commits.
*
*
*
* Within a transaction whenever a write operation e.g., put, delete, etc.,
* encounters a write conflict, that operation returns an error code of
* ECANCELED. The caller is then expected to re-try the operation in a new
* transaction.
*
*/
public final class KvdbTransaction extends NativeObject implements AutoCloseable {
/** KVDB the transaction is associated with. */
private final Kvdb kvdb;
KvdbTransaction(final Kvdb kvdb) {
this.kvdb = kvdb;
this.handle = alloc(kvdb.handle);
}
private static native long alloc(long kvdbHandle);
private native void abort(long kvdbHandle, long txnHandle) throws HseException;
private native void begin(long kvdbHandle, long txnHandle) throws HseException;
private native void commit(long kvdbHandle, long txnHandle) throws HseException;
private native void free(long kvdbHandle, long txnHandle);
private native State getState(long kvdbHandle, long txnHandle);
/**
* Abort/rollback transaction.
*
*
* The call fails if the referenced transaction is not in the ACTIVE state.
*
*
* This function is thread safe.
*
* @throws HseException Underlying C function returned a non-zero value.
*/
public void abort() throws HseException {
abort(kvdb.handle, this.handle);
}
/**
* Initiate transaction.
*
*
* The call fails if the transaction handle refers to an ACTIVE transaction.
*
*
* This function is thread safe.
*
* @throws HseException Underlying C function returned a non-zero value.
*/
public void begin() throws HseException {
begin(kvdb.handle, this.handle);
}
/**
* Close transaction.
*
*
* Commits the transaction if it is in the ACTIVE state. Otherwise, the
* transaction is aborted.
*
*
* This function is thread safe.
*
* @throws HseException Underlying C function returned a non-zero value.
*/
@Override
public void close() throws HseException {
if (this.handle == 0) {
return;
}
final State state = getState();
if (state == State.ACTIVE) {
commit();
} else {
abort();
}
free(kvdb.handle, this.handle);
this.handle = 0;
}
/**
* Commit all the mutations of the referenced transaction.
*
*
* The call fails if the referenced transaction is not in the ACTIVE state.
*
*
* This function is thread safe.
*
* @throws HseException Underlying C function returned a non-zero value.
*/
public void commit() throws HseException {
commit(kvdb.handle, this.handle);
}
/**
* Get the state of the transaction.
*
* This function is thread safe.
*
* @return Transaction's state.
*/
public State getState() {
return getState(kvdb.handle, this.handle);
}
/** Transaction state. */
public enum State {
/** Invalid state. */
INVALID,
/** Active state. */
ACTIVE,
/** Committed state. */
COMMITTED,
/** Aborted state. */
ABORTED,
}
}