
oracle.kv.impl.rep.migration.MigrationStreamHandle Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of oracle-nosql-server Show documentation
Show all versions of oracle-nosql-server Show documentation
NoSQL Database Server - supplies build and runtime support for the server (store) side of the Oracle NoSQL Database.
The newest version!
/*-
* Copyright (C) 2011, 2018 Oracle and/or its affiliates. All rights reserved.
*
* This file was distributed by Oracle as part of a version of Oracle NoSQL
* Database made available at:
*
* http://www.oracle.com/technetwork/database/database-technologies/nosqldb/downloads/index.html
*
* Please see the LICENSE file included in the top-level directory of the
* appropriate version of Oracle NoSQL Database for a copy of the license and
* additional information.
*/
package oracle.kv.impl.rep.migration;
import com.sleepycat.je.Cursor;
import com.sleepycat.je.DatabaseEntry;
import com.sleepycat.je.Transaction;
import oracle.kv.impl.rep.RepNode;
import oracle.kv.impl.topo.PartitionId;
/**
* A thread local handle to the migration stream.
*
* This class implements a thread local variable which is accessed during a
* client operation. Specifically, when the client operation creates a
* transaction it should initialize the thread local by calling
* MigrationStreamHandle.initialize(). Then as the client operations are
* executed the handle's addUpdate() and addDelete() methods should be invoked
* for each update and delete operation respectively.
*
* At the end of the client operation but before txn.commit() is invoked, the
* prepare() method on the handle should be invoked. The done() method must
* always be called once client operations are completed.
*
* When initialize() is invoked, a check is made to see if the partition
* that the operation affecting is being migrated. If so, the thread local
* handle will forward the update and delete operations to the migration stream.
* If no migration is active, the handle will do nothing.
*
* The base class does nothing. It provides the interface for the
* normal (non-migrating) case.
*
* The forwarding handle is implemented by the MigratingHandle subclass.
*/
public class MigrationStreamHandle {
private static final ThreadLocal handle =
new ThreadLocal() {
@Override
protected synchronized MigrationStreamHandle initialValue() {
/*
* The thread local must be explicitly set by first invoking
* MigrationStreamHandle.initialize().
*/
throw new IllegalStateException("Handle not initialized");
}
};
/*
* Normal non-forwarding handle. This instance is a no-op and can be
* shared.
*/
private static final MigrationStreamHandle noop =
new MigrationStreamHandle();
/**
* Returns the thread local migration stream handle. Note that this method
* throws an IllegalStateException unless initialize() has been called.
*
* @return a stream handle
*/
public static MigrationStreamHandle get() {
return handle.get();
}
/**
* Initializes the thread local migration stream handle.
*
* @param repNode the rep node
* @param partitionId a partition ID
* @param txn a transaction
* @return a stream handle
*/
public static MigrationStreamHandle initialize(RepNode repNode,
PartitionId partitionId,
Transaction txn) {
assert checkForStaleHandle();
final MigrationService service = repNode.getMigrationManager().
getMigrationService();
final MigrationSource source =
(service == null) ? null :
service.getSource(partitionId);
final MigrationStreamHandle h =
(source == null) ? noop :
new MigratingHandle(source, txn);
handle.set(h);
return h;
}
/**
* Checks if the thread local is stale (left over from a previous stream).
* If so an exception is thrown, otherwise true is returned.
*/
private static boolean checkForStaleHandle() {
try {
MigrationStreamHandle h = handle.get();
throw new IllegalStateException("Handle still around? " + h);
} catch (IllegalStateException ise) {
/* Expected */
return true;
}
}
private MigrationStreamHandle() {
}
/**
* Inserts a PUT record into migration stream if partition migration
* is in progress. Otherwise this method does nothing.
*
* @param key
* @param value
* @param vlsn
* @param expirationTime
*/
public void addPut(DatabaseEntry key, DatabaseEntry value,
long vlsn, long expirationTime) {
/* NOOP */
}
/**
* Inserts a DELETE record into migration stream if partition migration
* is in progress. Otherwise this method does nothing.
*
* @param key
* @param cursor
*/
public void addDelete(DatabaseEntry key, Cursor cursor) {
/* NOOP */
}
/**
* Inserts a PREPARE message into the migration stream if partition
* migration is in progress. Otherwise this method does nothing. This
* method should be invoked before the client transaction is committed.
* The PREPARE message signals that the operations associated with this
* transaction are about to be committed. No further operations can be
* added once prepared.
*/
public void prepare() {
/* NOOP */
}
/**
* Signals that this operations associated with this transaction are done.
* Depending on the transaction's outcome, a COMMIT or ABORT message is
* inserted into the migration stream.
*/
public void done() {
/* NOOP */
handle.remove();
}
@Override
public String toString() {
return "MigrationStreamHandle[]";
}
/**
* Subclass for when migration is taking place.
*/
private static class MigratingHandle extends MigrationStreamHandle {
private final MigrationSource source;
/* Transaction associated with this thread. */
private final Transaction txn;
/*
* Number of DB operations that have been sent. Not all add*() calls
* will result in sent messages due to key filtering.
*/
private int opsSent = 0;
/* True if prepare() has been called. */
private boolean prepared = false;
/* True if done() has been called. */
private boolean done = false;
private MigratingHandle(MigrationSource source, Transaction txn) {
super();
this.source = source;
this.txn = txn;
}
@Override
public void addPut(DatabaseEntry key, DatabaseEntry value,
long vlsn, long expirationTime) {
assert !prepared;
assert key != null;
assert value != null;
if (source.sendPut(txn.getId(), key, value,
vlsn, expirationTime)) {
opsSent++;
}
}
@Override
public void addDelete(DatabaseEntry key, Cursor cursor) {
assert !prepared;
assert key != null;
if (source.sendDelete(txn.getId(), key, cursor)) {
opsSent++;
}
}
@Override
public void prepare() {
assert !prepared;
assert !done;
/*
* Don't bother sending PREPARE (or COMMIT or ABORT) messages if no
* DB operations have been sent.
*/
if (opsSent > 0) {
source.sendPrepare(txn.getId());
}
prepared = true;
}
@Override
public void done() {
assert !done;
try {
if (opsSent > 0) {
source.sendResolution(txn.getId(),
txn.getState().
equals(Transaction.State.COMMITTED),
prepared);
}
} finally {
done = true;
/* Remove handle */
super.done();
}
}
@Override
public String toString() {
return "MigratingHandle[" + prepared + ", " + done +
", " + opsSent + "]";
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy