com.bigdata.service.ITxCommitProtocol Maven / Gradle / Ivy
/**
Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016. All rights reserved.
Contact:
SYSTAP, LLC DBA Blazegraph
2501 Calvert ST NW #106
Washington, DC 20008
[email protected]
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
* Created on Mar 22, 2007
*/
package com.bigdata.service;
import java.io.IOException;
import java.rmi.Remote;
import java.util.concurrent.ExecutionException;
import com.bigdata.btree.ITuple;
import com.bigdata.journal.ITransactionService;
import com.bigdata.journal.ValidationError;
/**
* Remote interface by which the {@link ITransactionService} manages the state
* of transactions on the distributed {@link IDataService}s.
*
* @author Bryan Thompson
* @version $Id$
*/
public interface ITxCommitProtocol extends Remote {
/**
* Notify a data service that it MAY release data required to support views
* for up to the specified releaseTime . This is the mechanism by
* which read locks are released. In effect, a read lock is a requirement
* that the releaseTime not be advanced as far as the start time of the
* transaction holding that read lock. Periodically and as transactions
* complete, the transaction manager will advance the releaseTime, thereby
* releasing read locks.
*
* @param releaseTime
* The new release time (strictly advanced by the transaction
* manager).
*
* @throws IllegalStateException
* if the read lock is set to a time earlier than its current
* value.
* @throws IOException
* if there is an RMI problem.
*/
public void setReleaseTime(long releaseTime) throws IOException;
/**
* Request abort of the transaction by the data service. This message is
* sent in response to {@link ITransactionService#abort(long)} to each
* {@link IDataService} on which the transaction has written. It is NOT sent
* for read-only transactions since they have no local state on the
* {@link IDataService}s.
*
* @param tx
* The transaction identifier.
*
* @throws IllegalArgumentException
* if the transaction has not been started on this data service.
* @throws IOException
* if there is an RMI problem.
*/
public void abort(long tx) throws IOException;
/**
* Request commit of the transaction by the data service. In the case where
* the transaction is entirely contained on the data service this method may
* be used to both prepare (validate) and commit the transaction (a single
* phase commit). Otherwise a 2-/3- phase commit is required and a separate
* {@link #prepare(long)} message MUST be used.
*
* @param tx
* The transaction identifier.
*
* @return The commit time assigned to that transaction.
*
* @throws IllegalArgumentException
* if the transaction is read-only.
* @throws IllegalStateException
* if the transaction is not known to the data service.
* @throws InterruptedException
* if interrupted.
* @throws ExecutionException
* This will wrap a {@link ValidationError} if validation fails.
* @throws IOException
* if there is an RMI problem.
*/
public long singlePhaseCommit(long tx) throws InterruptedException,
ExecutionException, IOException;
/**
* Request that the {@link IDataService} participate in a 3-phase commit.
*
* When the {@link IDataService} is sent the {@link #prepare(long, long)}
* message it executes a task which will handle commit processing for the
* transaction. That task MUST hold exclusive locks for the unisolated
* indices to which the transaction write sets will be applied. While
* holding those locks, the task must first validate the transaction's write
* set and then merge down the write set onto the corresponding unisolated
* indices using the specified revisionTime and checkpoint the
* indices in order to reduce all possible sources of latency. Note that
* each {@link IDataService} is able to independently prepare exactly those
* parts of the transaction's write set which are mapped onto index
* partitions hosted by a given {@link IDataService}.
*
* Once validation is complete and all possible steps have been taken to
* reduce sources of latency (e.g., checkpoint the indices and pre-extending
* the store if necessary), the task notifies the
* {@link ITransactionService} that it has prepared using
* {@link ITransactionService#prepared(long)}. The
* {@link ITransactionService} will wait until all tasks have prepared. If a
* task CAN NOT prepare the transaction, then it MUST throw an exception out
* of its {@link #prepare(long, long)} method.
*
* Once all tasks have send an {@link ITransactionService#prepared(long)}
* message to the {@link ITransactionService}, it will assign a commitTime
* to the transaction and permit those methods to return that commitTime to
* the {@link IDataService}s. Once the task receives the assigned commit
* time, it must obtain an exclusive write lock for the live journal (this
* is a higher requirement than just an exclusive lock on the necessary
* indices and will lock out all other write requests for the journal),
* register the checkpointed indices on the commit list and then request a
* commit of the journal using the specified commitTime. The task then
* notifies the transaction service that it has completed its commit using
* {@link ITransactionService#committed(long)} and awaits a response. If the
* {@link ITransactionService} indicates that the commit was not successful,
* the task rolls back the live journal to the prior commit point and throws
* an exception out of {@link #prepare(long, long)}.
*
* A sample flow for successful a distributed transaction commit is shown
* below. This example shows two {@link IDataService}s on which the client
* has written. (If the client only writes on a single data service then we
* use a single-phase commit protocol).
*
*
* client -------+----txService----+--dataService1--+--dataService2--+...
* | [1]
* | commit(tx) -------- + [2]
* | | prepare(tx,rev) +
* | | [3] |
* | | prepare(tx,rev) ------------------+
* | | | |
* | | <--prepared(tx) + |
* | | |
* | | <------------------- prepared(tx) +
* | |
* | "prepared" barrier [4]
* | |
* | | -- (commitTime) +
* | | -------------------- (commitTime) +
* | | [5] | |
* | | <--committed(tx)------------------+
* | | [6] |
* | | <--committed(tx)+
* | |
* | "committed" barrier [7]
* | | [8]
* | | ------ (success)+
* | | [9] |
* | | (void)----------+
* | | halt
* | | [10]
* | | ------------------------ (success)+
* | | [11] |
* | | (void)----------------------------+
* | [12] | halt
* | (commitTime)--------+
* |
*
*
*
* - [1] The client issues an {@link ITransactionService#commit(long)}
* request, in which it specifies the transaction identifier (tx).
* - [2,3] The transaction service issues concurrent
* {@link #prepare(long, long)} requests to the participating
* {@link IDataService}s, specifying the transaction identifier (tx) and
* the revision timestamp (rev) to be used and then waits at a barrier until
* it receives {@link ITransactionService#prepared(long)} messages from
* those {@link IDataService}s.
* - [4] When all participants have prepared, the barrier breaks and the
* {@link ITransactionService} assigns a commitTime and returns that
* commitTime as the return value for the prepared messages.
* - [5,6] Once the {@link IDataService} obtains that commitTime, it
* proceeds with its atomic commit using the specified commitTime and then
* sends an {@link ITransactionService#committed(long)} message to the
* {@link ITransactionService}.
* - [7] The {@link ITransactionService} waits at another barrier.
* - [8,10] Once it has received an
* {@link ITransactionService#committed(long)} message from each
* participating {@link IDataService} the transaction has been successfully
* committed and the barrier breaks. The {@link ITransactionService} now
* lets the {@link ITransactionService#committed(long)} messages return
*
true
, indicating success.
* - [9,11] The {@link IDataService}s return (void) from their
* {@link #prepare(long, long)} message and the threads running their side
* of the commit protocol halt.
* - [12] The {@link ITransactionService} returns the commit time which
* it assigned and which was used by each participating {@link IDataService}
* to the client.
*
* There are many points in the protocol where commit processing can fail.
* However, there are two primary failure classifications that are of
* interest for error handling. Up until the first barrier is satisified,
* there is no side-effect on the persistent state so error handling need
* only halt processing on the {@link IDataService}s and discard any local
* state associated with the transaction and throw an exception out of
* {@link #prepare(long, long)}. Once the first barrier has been
* satisfied, persistent side-effects MAY occur. Error handling in this
* case must rollback the state of the live journal for each of the
* participating {@link IDataService}s. If error handling was performed in
* response to a local error, then the {@link IDataService} must throw that
* error out of {@link #prepare(long, long)}. However, if error handling
* was initiated because {@link ITransactionService#committed(long)}
* returned false
then it should return normally (after
* rolling back the journal).
*
* @param tx
* The transaction identifier.
* @param revisionTime
* The timestamp that will be written into the {@link ITuple}s
* when the write set of the validated transaction is merged down
* onto the unisolated indices.
*
* @throws Throwable
* if there is a problem during the execution of the commit
* protocol by the {@link IDataService}.
* @throws IOException
* if there is an RMI problem.
*
* @todo it may be possible to set the desired commit time on the abstract
* task (or a subclass specific to the distributed commit protocol)
* and then use that timestamp rather than requesting one from the
* {@link ITransactionService} in the group commit. This would allow
* us to use the normal commit processing.
*
* If each distributed transaction gets its own commit time then we
* can not allow more than one distributed transaction into a given
* commit group. Therefore it seems that the
* {@link ITransactionService} would have to be able to assign the
* same commitTime to a set of distributed transactions that it knew
* were prepared together and would commit together. I can't quite see
* how that would work.
*
* Failing that, we will need to exclude other tasks (or at least
* other distributed commit processing tasks) from the commit group.
*/
public void prepare(long tx, long revisionTime) throws Throwable, IOException;
// /**
// * Cancel a 2-phase commit.
// *
// * @param tx
// * The transaction identifier.
// *
// * @throws IllegalArgumentException
// * if the transaction has not been started on this data service.
// * @throws IllegalStateException
// * if the transaction is not participating in a 2-phase commit.
// * @throws IOException
// * if there is an RMI problem.
// */
// public void twoPhaseCancel(long tx) throws IOException;
}