/** * Dedupe initiate task messages. Check if the initiate task message is seen before. * * @param inTxnId The txnId of the message * @param in The initiate task message * @return A client response to return if it's a duplicate, otherwise null. */ public InitiateResponseMessage dedupe(long inTxnId, TransactionInfoBaseMessage in) { if (in instanceof Iv2InitiateTaskMessage) { final Iv2InitiateTaskMessage init = (Iv2InitiateTaskMessage) in; final StoredProcedureInvocation invocation = init.getStoredProcedureInvocation(); final String procName = invocation.getProcName(); /* * Ning - @LoadSinglepartTable and @LoadMultipartTable always have the same txnId * which is the txnId of the snapshot. */ if (!(procName.equalsIgnoreCase("@LoadSinglepartitionTable") || procName.equalsIgnoreCase("@LoadMultipartitionTable")) && inTxnId <= m_lastSeenTxnId) { // already sequenced final InitiateResponseMessage resp = new InitiateResponseMessage(init); resp.setResults( new ClientResponseImpl( ClientResponseImpl.UNEXPECTED_FAILURE, new VoltTable[0], ClientResponseImpl.IGNORED_TRANSACTION)); return resp; } } return null; }
/* * The timestamp field is no longer really a timestamp, it is a unique id that is time based * and is similar to a pre-IV2 transaction ID except the less significant bits are set * to allow matching partition counts with IV2. It's OK, still can do 512k txns/second per * partition so plenty of headroom. */ public long getUniqueId() { StoredProcedureInvocation invocation = m_txnState.getInvocation(); if (invocation != null && invocation.getType() == ProcedureInvocationType.REPLICATED) { return invocation.getOriginalUniqueId(); } else { return m_txnState.uniqueId; } }
/* * Extract the timestamp from the timestamp field we have been passing around * that is now a unique id with a timestamp encoded in the most significant bits ala * a pre-IV2 transaction id. */ public Date getTransactionTime() { StoredProcedureInvocation invocation = m_txnState.getInvocation(); if (invocation != null && invocation.getType() == ProcedureInvocationType.REPLICATED) { return new Date(UniqueIdGenerator.getTimestampFromUniqueId(invocation.getOriginalUniqueId())); } else { return new Date(UniqueIdGenerator.getTimestampFromUniqueId(m_txnState.uniqueId)); } }
/** * Note this fails for Sysprocs that use it in non-coordinating fragment work. Don't. * * @return The transaction id for determinism, not for ordering. */ long getTransactionId() { StoredProcedureInvocation invocation = m_txnState.getInvocation(); if (invocation != null && invocation.getType() == ProcedureInvocationType.REPLICATED) { return invocation.getOriginalTxnId(); } else { return m_txnState.txnId; } }
/** * Create a VoltMessage that can be fed into CI's handleRead() method. * * @param origTxnId The original txnId if it's a replicated transaction * @param name The procedure name * @param params Procedure parameters * @return * @throws IOException */ private static ByteBuffer createMsg(Long origTxnId, String name, final Object... params) throws IOException { FastSerializer fs = new FastSerializer(); StoredProcedureInvocation proc = new StoredProcedureInvocation(); proc.setProcName(name); if (origTxnId != null) proc.setOriginalTxnId(origTxnId); proc.setParams(params); fs.writeObject(proc); return fs.getBuffer(); }
@Override public ClientResponseImpl shouldAccept( AuthUser user, StoredProcedureInvocation invocation, Config sysProc) { if (!invocation.procName.equals("@AdHoc")) { return null; } ParameterSet params = invocation.getParams(); // note the second secret param if (params.toArray().length > 2) { return new ClientResponseImpl( ClientResponseImpl.GRACEFUL_FAILURE, new VoltTable[0], "Adhoc system procedure requires exactly one or two parameters, " + "the SQL statement to execute with an optional partitioning value.", invocation.clientHandle); } // check the types are both strings if ((params.toArray()[0] instanceof String) == false) { return new ClientResponseImpl( ClientResponseImpl.GRACEFUL_FAILURE, new VoltTable[0], "Adhoc system procedure requires sql in the String type only.", invocation.clientHandle); } return null; }
public StoredProcedureInvocation getShallowCopy() { StoredProcedureInvocation copy = new StoredProcedureInvocation(); copy.type = type; copy.clientHandle = clientHandle; copy.params = params; copy.procName = procName; copy.originalTxnId = originalTxnId; copy.originalUniqueId = originalUniqueId; if (serializedParams != null) { copy.serializedParams = serializedParams.duplicate(); } else { copy.serializedParams = null; } copy.batchTimeout = batchTimeout; return copy; }
@Test public void testUserProc() throws IOException { ByteBuffer msg = createMsg("hello", 1); StoredProcedureInvocation invocation = readAndCheck(msg, "hello", 1, false, true, true, false); assertEquals(1, invocation.getParameterAtIndex(0)); }
public MultiPartitionParticipantTxnState( Mailbox mbox, ExecutionSite site, TransactionInfoBaseMessage notice) { super(mbox, site, notice); m_hsId = site.getSiteId(); m_nonCoordinatingSites = null; m_isCoordinator = false; m_context = site.m_context; // Check to make sure we are the coordinator, it is possible to get an intiate task // where we aren't the coordinator because we are a replica of the coordinator. if (notice instanceof InitiateTaskMessage) { // keep this around for DR purposes m_invocation = ((InitiateTaskMessage) notice).getStoredProcedureInvocation(); // Determine if mismatched results are okay. if (m_invocation != null) { String procName = m_invocation.getProcName(); if (procName.startsWith("@AdHoc")) { // For now the best we can do with ad hoc is to always allow mismatched results. // We don't know if it's non-deterministic or not. But the main use case for // being lenient is "SELECT * FROM TABLE LIMIT n", typically run as ad hoc. m_allowMismatchedResults = true; } else { // Walk through the statements to see if any are non-deterministic. if (m_context != null && m_context.procedures != null) { Procedure proc = m_context.procedures.get(procName); if (proc != null) { CatalogMap<Statement> stmts = proc.getStatements(); if (stmts != null) { for (Statement stmt : stmts) { if (!stmt.getIscontentdeterministic() || !stmt.getIsorderdeterministic()) { m_allowMismatchedResults = true; break; } } } } } } } if (notice.getCoordinatorHSId() == m_hsId) { m_isCoordinator = true; m_task = (InitiateTaskMessage) notice; m_durabilityFlag = m_task.getDurabilityFlagIfItExists(); SiteTracker tracker = site.getSiteTracker(); m_readyWorkUnits.add( new WorkUnit(tracker, m_task, null, m_hsId, null, false, m_allowMismatchedResults)); /* * ENG-3374: Use the same set of non-coordinator sites the * initiator sent out the participant notices to, so that when * the coordinator send out the fragment works all participants * will get them. * * During rejoin, the initiator's site tracker and the * coordinator's site tracker may not be consistent for a brief * period of time. So can't rely on the site tracker to tell the * coordinator which sites to send work to. */ m_nonCoordinatingSites = m_task.getNonCoordinatorSites(); } else { m_durabilityFlag = ((InitiateTaskMessage) notice).getDurabilityFlagIfItExists(); m_task = null; } } else { m_task = null; m_durabilityFlag = null; m_invocation = null; } }
public long getClientHandle() { return StoredProcedureInvocation.getClientHandle(this.serializedRequest); }
/** Write an SPI using it's ByteBuffer serialization code. */ public void writeInvocation(StoredProcedureInvocation invocation) throws IOException { int len = invocation.getSerializedSize(); growIfNeeded(len); invocation.flattenToBuffer(buffer.b()); }
public ClientResponseImpl call(Object... paramListIn) { // verify per-txn state has been reset assert (m_statusCode == ClientResponse.UNINITIALIZED_APP_STATUS_CODE); assert (m_statusString == null); assert (m_cachedRNG == null); // reset the hash of results m_inputCRC.reset(); // use local var to avoid warnings about reassigning method argument Object[] paramList = paramListIn; ClientResponseImpl retval = null; // assert no sql is queued assert (m_batch.size() == 0); try { m_statsCollector.beginProcedure(); byte status = ClientResponse.SUCCESS; VoltTable[] results = null; // inject sysproc execution context as the first parameter. if (isSystemProcedure()) { final Object[] combinedParams = new Object[paramList.length + 1]; combinedParams[0] = m_systemProcedureContext; for (int i = 0; i < paramList.length; ++i) combinedParams[i + 1] = paramList[i]; // swap the lists. paramList = combinedParams; } if (paramList.length != m_paramTypes.length) { m_statsCollector.endProcedure(false, true, null, null); String msg = "PROCEDURE " + m_procedureName + " EXPECTS " + String.valueOf(m_paramTypes.length) + " PARAMS, BUT RECEIVED " + String.valueOf(paramList.length); status = ClientResponse.GRACEFUL_FAILURE; return getErrorResponse(status, msg, null); } for (int i = 0; i < m_paramTypes.length; i++) { try { paramList[i] = ParameterConverter.tryToMakeCompatible( m_paramTypeIsPrimitive[i], m_paramTypeIsArray[i], m_paramTypes[i], m_paramTypeComponentType[i], paramList[i]); } catch (Exception e) { m_statsCollector.endProcedure(false, true, null, null); String msg = "PROCEDURE " + m_procedureName + " TYPE ERROR FOR PARAMETER " + i + ": " + e.toString(); status = ClientResponse.GRACEFUL_FAILURE; return getErrorResponse(status, msg, null); } } boolean error = false; boolean abort = false; // run a regular java class if (m_catProc.getHasjava()) { try { if (log.isTraceEnabled()) { log.trace( "invoking... procMethod=" + m_procMethod.getName() + ", class=" + getClass().getName()); } try { Object rawResult = m_procMethod.invoke(m_procedure, paramList); results = getResultsFromRawResults(rawResult); } catch (IllegalAccessException e) { // If reflection fails, invoke the same error handling that other exceptions do throw new InvocationTargetException(e); } log.trace("invoked"); } catch (InvocationTargetException itex) { // itex.printStackTrace(); Throwable ex = itex.getCause(); if (ex instanceof VoltAbortException && !(ex instanceof EEException)) { abort = true; } else { error = true; } if (ex instanceof Error) { m_statsCollector.endProcedure(false, true, null, null); throw (Error) ex; } retval = getErrorResponse(ex); } } // single statement only work // (this could be made faster, but with less code re-use) else { assert (m_catProc.getStatements().size() == 1); try { m_cachedSingleStmt.params = getCleanParams(m_cachedSingleStmt.stmt, paramList); if (getHsqlBackendIfExists() != null) { // HSQL handling CatalogMap<StmtParameter> sparamsMap = m_cachedSingleStmt.stmt.catStmt.getParameters(); List<StmtParameter> sparams = CatalogUtil.getSortedCatalogItems(sparamsMap, "index"); VoltTable table = getHsqlBackendIfExists() .runSQLWithSubstitutions( m_cachedSingleStmt.stmt, m_cachedSingleStmt.params, sparams); results = new VoltTable[] {table}; } else { m_batch.add(m_cachedSingleStmt); results = voltExecuteSQL(true); } } catch (SerializableException ex) { retval = getErrorResponse(ex); } } // Record statistics for procedure call. StoredProcedureInvocation invoc = (m_txnState != null ? m_txnState.getInvocation() : null); ParameterSet paramSet = (invoc != null ? invoc.getParams() : null); m_statsCollector.endProcedure(abort, error, results, paramSet); // don't leave empty handed if (results == null) results = new VoltTable[0]; if (retval == null) retval = new ClientResponseImpl(status, m_statusCode, m_statusString, results, null); int hash = (int) m_inputCRC.getValue(); if ((retval.getStatus() == ClientResponse.SUCCESS) && (hash != 0)) { retval.setHash(hash); } if ((m_txnState != null) && // may be null for tests (m_txnState.getInvocation() != null) && (m_txnState.getInvocation().getType() == ProcedureInvocationType.REPLICATED)) { retval.convertResultsToHashForDeterminism(); } } finally { // finally at the call(..) scope to ensure params can be // garbage collected and that the queue will be empty for // the next call m_batch.clear(); // reset other per-txn state m_txnState = null; m_statusCode = ClientResponse.UNINITIALIZED_APP_STATUS_CODE; m_statusString = null; m_cachedRNG = null; m_cachedSingleStmt.params = null; m_cachedSingleStmt.expectation = null; m_seenFinalBatch = false; } return retval; }