@Override void completeInitiateTask(SiteProcedureConnection siteConnection) { CompleteTransactionMessage complete = new CompleteTransactionMessage( m_initiator.getHSId(), // who is the "initiator" now?? m_initiator.getHSId(), m_txnState.txnId, m_txnState.isReadOnly(), m_txnState.getHash(), m_txnState.needsRollback(), false, // really don't want to have ack the ack. false, m_msg.isForReplay()); complete.setTruncationHandle(m_msg.getTruncationHandle()); complete.setOriginalTxnId(m_msg.getOriginalTxnId()); m_initiator.send(com.google.common.primitives.Longs.toArray(m_initiatorHSIds), complete); m_txnState.setDone(); m_queue.flush(); }
// SpScheduler expects to see InitiateTaskMessages corresponding to single-partition // procedures only. public void handleIv2InitiateTaskMessage(Iv2InitiateTaskMessage message) { if (!message.isSinglePartition()) { throw new RuntimeException( "SpScheduler.handleIv2InitiateTaskMessage " + "should never receive multi-partition initiations."); } final String procedureName = message.getStoredProcedureName(); long newSpHandle; long uniqueId = Long.MIN_VALUE; Iv2InitiateTaskMessage msg = message; if (m_isLeader || message.isReadOnly()) { /* * A short circuit read is a read where the client interface is local to * this node. The CI will let a replica perform a read in this case and * it does looser tracking of client handles since it can't be * partitioned from the local replica. */ if (!m_isLeader && CoreUtils.getHostIdFromHSId(msg.getInitiatorHSId()) != CoreUtils.getHostIdFromHSId(m_mailbox.getHSId())) { VoltDB.crashLocalVoltDB("Only allowed to do short circuit reads locally", true, null); } /* * If this is for CL replay or DR, update the unique ID generator */ if (message.isForReplay()) { uniqueId = message.getUniqueId(); try { m_uniqueIdGenerator.updateMostRecentlyGeneratedUniqueId(uniqueId); } catch (Exception e) { hostLog.fatal(e.getMessage()); hostLog.fatal("Invocation: " + message); VoltDB.crashLocalVoltDB(e.getMessage(), true, e); } } else if (message.isForDR()) { uniqueId = message.getStoredProcedureInvocation().getOriginalUniqueId(); // @LoadSinglepartitionTable does not have a valid uid if (UniqueIdGenerator.getPartitionIdFromUniqueId(uniqueId) == m_partitionId) { m_uniqueIdGenerator.updateMostRecentlyGeneratedUniqueId(uniqueId); } } /* * If this is CL replay use the txnid from the CL and also * update the txnid to match the one from the CL */ if (message.isForReplay()) { newSpHandle = message.getTxnId(); setMaxSeenTxnId(newSpHandle); } else if (m_isLeader) { TxnEgo ego = advanceTxnEgo(); newSpHandle = ego.getTxnId(); uniqueId = m_uniqueIdGenerator.getNextUniqueId(); } else { /* * The short circuit read case. Since we are not a master * we can't create new transaction IDs, so reuse the last seen * txnid. For a timestamp, might as well give a reasonable one * for a read heavy workload so time isn't bursty. */ uniqueId = UniqueIdGenerator.makeIdFromComponents( Math.max(System.currentTimeMillis(), m_uniqueIdGenerator.lastUsedTime), 0, m_uniqueIdGenerator.partitionId); // Don't think it wise to make a new one for a short circuit read newSpHandle = getCurrentTxnId(); } // Need to set the SP handle on the received message // Need to copy this or the other local sites handling // the same initiate task message will overwrite each // other's memory -- the message isn't copied on delivery // to other local mailboxes. msg = new Iv2InitiateTaskMessage( message.getInitiatorHSId(), message.getCoordinatorHSId(), m_repairLogTruncationHandle, message.getTxnId(), message.getUniqueId(), message.isReadOnly(), message.isSinglePartition(), message.getStoredProcedureInvocation(), message.getClientInterfaceHandle(), message.getConnectionId(), message.isForReplay()); msg.setSpHandle(newSpHandle); // Also, if this is a vanilla single-part procedure, make the TXNID // be the SpHandle (for now) // Only system procedures are every-site, so we'll check through the SystemProcedureCatalog if (SystemProcedureCatalog.listing.get(procedureName) == null || !SystemProcedureCatalog.listing.get(procedureName).getEverysite()) { msg.setTxnId(newSpHandle); msg.setUniqueId(uniqueId); } // Don't replicate reads, this really assumes that DML validation // is going to be integrated soonish if (m_isLeader && !msg.isReadOnly() && m_sendToHSIds.length > 0) { Iv2InitiateTaskMessage replmsg = new Iv2InitiateTaskMessage( m_mailbox.getHSId(), m_mailbox.getHSId(), m_repairLogTruncationHandle, msg.getTxnId(), msg.getUniqueId(), msg.isReadOnly(), msg.isSinglePartition(), msg.getStoredProcedureInvocation(), msg.getClientInterfaceHandle(), msg.getConnectionId(), msg.isForReplay()); // Update the handle in the copy since the constructor doesn't set it replmsg.setSpHandle(newSpHandle); m_mailbox.send(m_sendToHSIds, replmsg); DuplicateCounter counter = new DuplicateCounter( msg.getInitiatorHSId(), msg.getTxnId(), m_replicaHSIds, msg.getStoredProcedureName()); m_duplicateCounters.put(new DuplicateCounterKey(msg.getTxnId(), newSpHandle), counter); } } else { setMaxSeenTxnId(msg.getSpHandle()); newSpHandle = msg.getSpHandle(); uniqueId = msg.getUniqueId(); } Iv2Trace.logIv2InitiateTaskMessage(message, m_mailbox.getHSId(), msg.getTxnId(), newSpHandle); doLocalInitiateOffer(msg); return; }
/** Run is invoked by a run-loop to execute this transaction. */ @Override public void run(SiteProcedureConnection siteConnection) { hostLog.debug("STARTING: " + this); // Cast up. Could avoid ugliness with Iv2TransactionClass baseclass MpTransactionState txn = (MpTransactionState) m_txnState; // Check for restarting sysprocs String spName = txn.m_initiationMsg.getStoredProcedureName(); // certain system procs can and can't be restarted // Right now this is adhoc and catalog update. Since these are treated specially // in a few places (here, recovery, dr), maybe we should add another metadata // property the sysproc registry about whether a proc can be restarted/recovered/dr-ed if (m_isRestart && spName.startsWith("@") && !spName.startsWith("@AdHoc") && !spName.equals("@UpdateApplicationCatalog")) { InitiateResponseMessage errorResp = new InitiateResponseMessage(txn.m_initiationMsg); errorResp.setResults( new ClientResponseImpl( ClientResponse.UNEXPECTED_FAILURE, new VoltTable[] {}, "Failure while running system procedure " + txn.m_initiationMsg.getStoredProcedureName() + ", and system procedures can not be restarted.")); txn.setNeedsRollback(); completeInitiateTask(siteConnection); errorResp.m_sourceHSId = m_initiator.getHSId(); m_initiator.deliver(errorResp); hostLog.debug("SYSPROCFAIL: " + this); return; } // Let's ensure that we flush any previous attempts of this transaction // at the masters we're going to try to use this time around. if (m_isRestart) { CompleteTransactionMessage restart = new CompleteTransactionMessage( m_initiator.getHSId(), // who is the "initiator" now?? m_initiator.getHSId(), m_txnState.txnId, m_txnState.isReadOnly(), 0, true, false, // really don't want to have ack the ack. !m_txnState.isReadOnly(), m_msg.isForReplay()); restart.setTruncationHandle(m_msg.getTruncationHandle()); restart.setOriginalTxnId(m_msg.getOriginalTxnId()); m_initiator.send(com.google.common.primitives.Longs.toArray(m_initiatorHSIds), restart); } final InitiateResponseMessage response = processInitiateTask(txn.m_initiationMsg, siteConnection); // We currently don't want to restart read-only MP transactions because: // 1) We're not writing the Iv2InitiateTaskMessage to the first // FragmentTaskMessage in read-only case in the name of some unmeasured // performance impact, // 2) We don't want to perturb command logging and/or DR this close to the 3.0 release // 3) We don't guarantee the restarted results returned to the client // anyway, so not restarting the read is currently harmless. // We could actually restart this here, since we have the invocation, but let's be consistent? int status = response.getClientResponseData().getStatus(); if (status != ClientResponse.TXN_RESTART || (status == ClientResponse.TXN_RESTART && m_msg.isReadOnly())) { if (!response.shouldCommit()) { txn.setNeedsRollback(); } completeInitiateTask(siteConnection); // Set the source HSId (ugh) to ourselves so we track the message path correctly response.m_sourceHSId = m_initiator.getHSId(); m_initiator.deliver(response); execLog.l7dlog( Level.TRACE, LogKeys.org_voltdb_ExecutionSite_SendingCompletedWUToDtxn.name(), null); hostLog.debug("COMPLETE: " + this); } else { restartTransaction(); hostLog.debug("RESTART: " + this); } }