/* This file is part of VoltDB.
* Copyright (C) 2008-2017 VoltDB Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb.iv2;
import java.io.IOException;
import org.voltcore.logging.Level;
import org.voltcore.messaging.Mailbox;
import org.voltcore.utils.CoreUtils;
import org.voltcore.utils.LatencyWatchdog;
import org.voltdb.ClientResponseImpl;
import org.voltdb.PartitionDRGateway;
import org.voltdb.SiteProcedureConnection;
import org.voltdb.VoltTable;
import org.voltdb.client.ClientResponse;
import org.voltdb.client.BatchTimeoutOverrideType;
import org.voltdb.messaging.InitiateResponseMessage;
import org.voltdb.messaging.Iv2InitiateTaskMessage;
import org.voltdb.rejoin.TaskLog;
import org.voltdb.utils.LogKeys;
import org.voltdb.utils.MiscUtils;
import org.voltdb.utils.VoltTrace;
/**
* Implements the single partition procedure ProcedureTask.
* Runs single partition procedures against a SiteConnection
*/
public class SpProcedureTask extends ProcedureTask
{
private static final boolean EXEC_TRACE_ENABLED;
private static final boolean HOST_DEBUG_ENABLED;
private static final boolean HOST_TRACE_ENABLED;
static {
EXEC_TRACE_ENABLED = execLog.isTraceEnabled();
HOST_DEBUG_ENABLED = hostLog.isDebugEnabled();
HOST_TRACE_ENABLED = hostLog.isTraceEnabled();
}
public SpProcedureTask(Mailbox initiator, String procName, TransactionTaskQueue queue,
Iv2InitiateTaskMessage msg)
{
super(initiator, procName, new SpTransactionState(msg), queue);
}
@Override
protected void durabilityTraceEnd() {
final VoltTrace.TraceEventBatch traceLog = VoltTrace.log(VoltTrace.Category.SPI);
if (traceLog != null) {
traceLog.add(() -> VoltTrace.endAsync("durability",
MiscUtils.hsIdTxnIdToString(m_initiator.getHSId(), getSpHandle())));
}
}
/** Run is invoked by a run-loop to execute this transaction. */
@Override
public void run(SiteProcedureConnection siteConnection)
{
LatencyWatchdog.pet();
waitOnDurabilityBackpressureFuture();
if (HOST_DEBUG_ENABLED) {
hostLog.debug("STARTING: " + this);
}
final VoltTrace.TraceEventBatch traceLog = VoltTrace.log(VoltTrace.Category.SPI);
if (traceLog != null) {
traceLog.add(() -> VoltTrace.beginDuration("runsptask",
"txnId", TxnEgo.txnIdToString(getTxnId()),
"partition", Integer.toString(siteConnection.getCorrespondingPartitionId())));
}
if (!m_txnState.isReadOnly()) {
m_txnState.setBeginUndoToken(siteConnection.getLatestUndoToken());
}
// cast up here .. ugly.
SpTransactionState txnState = (SpTransactionState)m_txnState;
InitiateResponseMessage response;
int originalTimeout = siteConnection.getBatchTimeout();
int individualTimeout = m_txnState.getInvocation().getBatchTimeout();
try {
// run the procedure with a specific individual timeout
if (BatchTimeoutOverrideType.isUserSetTimeout(individualTimeout) ) {
siteConnection.setBatchTimeout(individualTimeout);
}
response = processInitiateTask(txnState.m_initiationMsg, siteConnection);
} finally {
// reset the deployment timeout value back to its original value
if (BatchTimeoutOverrideType.isUserSetTimeout(individualTimeout) ) {
siteConnection.setBatchTimeout(originalTimeout);
}
}
if (!response.shouldCommit()) {
m_txnState.setNeedsRollback(true);
}
completeInitiateTask(siteConnection);
response.m_sourceHSId = m_initiator.getHSId();
m_initiator.deliver(response);
if (EXEC_TRACE_ENABLED) {
execLog.l7dlog( Level.TRACE, LogKeys.org_voltdb_ExecutionSite_SendingCompletedWUToDtxn.name(), null);
}
if (HOST_DEBUG_ENABLED) {
hostLog.debug("COMPLETE: " + this);
}
if (traceLog != null) {
traceLog.add(VoltTrace::endDuration);
}
logToDR(siteConnection.getDRGateway(), txnState, response);
}
@Override
public void runForRejoin(SiteProcedureConnection siteConnection, TaskLog taskLog)
throws IOException
{
LatencyWatchdog.pet();
if (!m_txnState.isReadOnly()) {
taskLog.logTask(m_txnState.getNotice());
}
SpTransactionState txnState = (SpTransactionState)m_txnState;
final InitiateResponseMessage response =
new InitiateResponseMessage(txnState.m_initiationMsg);
response.m_sourceHSId = m_initiator.getHSId();
response.setRecovering(true);
// add an empty dummy response
response.setResults(new ClientResponseImpl(
ClientResponse.SUCCESS,
new VoltTable[0],
null));
m_initiator.deliver(response);
}
// This is an ugly copy/paste mix of run() and completeInitiateTask()
// that avoids using the mailbox -- since no response should be
// generated...
@Override
public void runFromTaskLog(SiteProcedureConnection siteConnection)
{
LatencyWatchdog.pet();
if (HOST_TRACE_ENABLED) {
hostLog.trace("START replaying txn: " + this);
}
if (!m_txnState.isReadOnly()) {
m_txnState.setBeginUndoToken(siteConnection.getLatestUndoToken());
}
// cast up here .. ugly.
SpTransactionState txnState = (SpTransactionState)m_txnState;
final InitiateResponseMessage response = processInitiateTask(txnState.m_initiationMsg, siteConnection);
if (!response.shouldCommit()) {
m_txnState.setNeedsRollback(true);
}
if (!m_txnState.isReadOnly()) {
assert(siteConnection.getLatestUndoToken() != Site.kInvalidUndoToken) :
"[SP][RW] transaction found invalid latest undo token state in Iv2ExecutionSite.";
assert(siteConnection.getLatestUndoToken() >= m_txnState.getBeginUndoToken()) :
"[SP][RW] transaction's undo log token farther advanced than latest known value.";
assert (m_txnState.getBeginUndoToken() != Site.kInvalidUndoToken) :
"[SP][RW] with invalid undo token in completeInitiateTask.";
// the truncation point token SHOULD be part of m_txn. However, the
// legacy interaces don't work this way and IV2 hasn't changed this
// ownership yet. But truncateUndoLog is written assuming the right
// eventual encapsulation.
siteConnection.truncateUndoLog(m_txnState.needsRollback(),
m_txnState.getBeginUndoToken(),
m_txnState.m_spHandle,
m_txnState.getUndoLog());
}
m_txnState.setDone();
if (EXEC_TRACE_ENABLED) {
execLog.l7dlog( Level.TRACE, LogKeys.org_voltdb_ExecutionSite_SendingCompletedWUToDtxn.name(), null);
}
if (HOST_TRACE_ENABLED) {
hostLog.trace("COMPLETE replaying txn: " + this);
}
logToDR(siteConnection.getDRGateway(), txnState, response);
}
private void logToDR(PartitionDRGateway drGateway, SpTransactionState txnState, InitiateResponseMessage response)
{
// Log invocation to DR
if (drGateway != null && !txnState.isReadOnly() && !txnState.needsRollback()) {
drGateway.onSuccessfulProcedureCall(txnState.txnId, txnState.uniqueId, txnState.getHash(),
txnState.getInvocation(), response.getClientResponseData());
}
}
@Override
void completeInitiateTask(SiteProcedureConnection siteConnection)
{
if (!m_txnState.isReadOnly()) {
assert(siteConnection.getLatestUndoToken() != Site.kInvalidUndoToken) :
"[SP][RW] transaction found invalid latest undo token state in Iv2ExecutionSite.";
assert(siteConnection.getLatestUndoToken() >= m_txnState.getBeginUndoToken()) :
"[SP][RW] transaction's undo log token farther advanced than latest known value.";
assert (m_txnState.getBeginUndoToken() != Site.kInvalidUndoToken) :
"[SP][RW] with invalid undo token in completeInitiateTask.";
// the truncation point token SHOULD be part of m_txn. However, the
// legacy interaces don't work this way and IV2 hasn't changed this
// ownership yet. But truncateUndoLog is written assuming the right
// eventual encapsulation.
siteConnection.truncateUndoLog(m_txnState.needsRollback(),
m_txnState.getBeginUndoToken(),
m_txnState.m_spHandle,
m_txnState.getUndoLog());
}
doCommonSPICompleteActions();
}
@Override
public String toString()
{
StringBuilder sb = new StringBuilder();
sb.append("SpProcedureTask:");
sb.append(" TXN ID: ").append(TxnEgo.txnIdToString(getTxnId()));
sb.append(" SP HANDLE ID: ").append(TxnEgo.txnIdToString(getSpHandle()));
sb.append(" ON HSID: ").append(CoreUtils.hsIdToString(m_initiator.getHSId()));
return sb.toString();
}
}