Package org.voltdb.messaging

Source Code of org.voltdb.messaging.FragmentTaskMessage

/* This file is part of VoltDB.
* Copyright (C) 2008-2014 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.messaging;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;

import org.voltcore.logging.Level;
import org.voltcore.logging.VoltLogger;
import org.voltcore.messaging.Subject;
import org.voltcore.messaging.TransactionInfoBaseMessage;
import org.voltcore.utils.CoreUtils;
import org.voltdb.ParameterSet;
import org.voltdb.VoltDB;
import org.voltdb.common.Constants;
import org.voltdb.utils.Encoder;
import org.voltdb.utils.LogKeys;

import com.google_voltpatches.common.base.Charsets;
import com.google_voltpatches.common.collect.ImmutableSet;

/**
* Message from a stored procedure coordinator to an execution site
* which is participating in the transaction. This message specifies
* which planfragment to run and with which parameters.
*
*/
public class FragmentTaskMessage extends TransactionInfoBaseMessage
{
    protected static final VoltLogger hostLog = new VoltLogger("HOST");

    public static final byte USER_PROC = 0;
    public static final byte SYS_PROC_PER_PARTITION = 1;
    public static final byte SYS_PROC_PER_SITE = 2;

    public static final byte[] EMPTY_HASH;
    static {
        MessageDigest md = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            System.exit(-1); // this means the jvm is broke
        }
        md.update("".getBytes(Constants.UTF8ENCODING));
        EMPTY_HASH = md.digest();
    }

    private static class FragmentData {
        byte[] m_planHash = null;
        ByteBuffer m_parameterSet = null;
        Integer m_outputDepId = null;
        ArrayList<Integer> m_inputDepIds = null;
        // For unplanned item
        byte[] m_fragmentPlan = null;
        byte[] m_stmtText = null;

        public FragmentData() {
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append(String.format("FRAGMENT PLAN HASH: %s\n", Encoder.hexEncode(m_planHash)));
            if (m_parameterSet != null) {
                ParameterSet pset = null;
                try {
                    pset = ParameterSet.fromByteBuffer(m_parameterSet.asReadOnlyBuffer());
                } catch (IOException e) {
                    e.printStackTrace();
                }
                assert(pset != null);
                sb.append("\n  ").append(pset.toString());
            }
            if (m_outputDepId != null) {
                sb.append("\n");
                sb.append("  OUTPUT_DEPENDENCY_ID ");
                sb.append(m_outputDepId);
            }
            if ((m_inputDepIds != null) && (m_inputDepIds.size() > 0)) {
                sb.append("\n");
                sb.append("  INPUT_DEPENDENCY_IDS ");
                for (long id : m_inputDepIds)
                    sb.append(id).append(", ");
                sb.setLength(sb.lastIndexOf(", "));
            }
            if ((m_fragmentPlan != null) && (m_fragmentPlan.length != 0)) {
                sb.append("\n");
                sb.append("  FRAGMENT_PLAN ");
                sb.append(m_fragmentPlan);
            }
            if ((m_stmtText != null) && (m_stmtText.length != 0)) {
                sb.append("\n");
                sb.append("  STATEMENT_TEXT ");
                sb.append(m_stmtText);
            }
            return sb.toString();
        }
    }

    List<FragmentData> m_items = new ArrayList<FragmentData>();

    boolean m_isFinal = false;
    byte m_taskType = 0;
    // We use this flag to generate a null FragmentTaskMessage that returns a
    // response for a dependency but doesn't try to execute anything on the EE.
    // This is a hack to serialize CompleteTransactionMessages and
    // BorrowTaskMessages when the first fragment of the restarting transaction
    // is a short-circuited replicated table read.
    // If this flag is set, the message should contain a single fragment with the
    // desired output dep ID, but no real work to do.
    boolean m_emptyForRestart = false;

    int m_inputDepCount = 0;
    Iv2InitiateTaskMessage m_initiateTask;
    ByteBuffer m_initiateTaskBuffer;
    // Partitions involved in this multipart, set in the first fragment
    Set<Integer> m_involvedPartitions = ImmutableSet.of();

    // context for long running fragment status log messages
    byte[] m_procedureName = null;
    int m_currentBatchIndex = 0;

    public int getCurrentBatchIndex() {
        return m_currentBatchIndex;
    }

    /** Empty constructor for de-serialization */
    FragmentTaskMessage() {
        m_subject = Subject.DEFAULT.getId();
    }

    /**
     *
     * @param initiatorHSId
     * @param coordinatorHSId
     * @param txnId
     * @param isReadOnly
     * @param isFinal
     */
    public FragmentTaskMessage(long initiatorHSId,
                               long coordinatorHSId,
                               long txnId,
                               long uniqueId,
                               boolean isReadOnly,
                               boolean isFinal,
                               boolean isForReplay) {
        super(initiatorHSId, coordinatorHSId, txnId, uniqueId, isReadOnly, isForReplay);
        m_isFinal = isFinal;
        m_subject = Subject.DEFAULT.getId();
        assert(selfCheck());
    }

    // If you add a new field to the message and you don't want to lose information at all point,
    // remember to add it to the constructor below, Because this constructor is used to copy a message at some place.
    // for example, in SpScheduler.handleFragmentTaskMessage()
    // The parameter sets are .duplicate()'d in flattenToBuffer,
    // so we can make a shallow copy here and still be thread-safe
    // when we serialize the copy.
    public FragmentTaskMessage(long initiatorHSId,
            long coordinatorHSId,
            FragmentTaskMessage ftask)
    {
        super(initiatorHSId, coordinatorHSId, ftask);

        setSpHandle(ftask.getSpHandle());
        m_taskType = ftask.m_taskType;
        m_isFinal = ftask.m_isFinal;
        m_subject = ftask.m_subject;
        m_inputDepCount = ftask.m_inputDepCount;
        m_items = ftask.m_items;
        m_initiateTask = ftask.m_initiateTask;
        m_emptyForRestart = ftask.m_emptyForRestart;
        m_procedureName = ftask.m_procedureName;
        m_currentBatchIndex = ftask.m_currentBatchIndex;
        m_involvedPartitions = ftask.m_involvedPartitions;
        if (ftask.m_initiateTaskBuffer != null) {
            m_initiateTaskBuffer = ftask.m_initiateTaskBuffer.duplicate();
        }
        assert(selfCheck());
    }

    public void setProcedureName(String procedureName) {
        Iv2InitiateTaskMessage it = getInitiateTask();
        if (it != null) {
            assert(it.getStoredProcedureName().equals(procedureName));
        }
        else {
            m_procedureName = procedureName.getBytes(Charsets.UTF_8);
        }
    }

    public void setBatch(int batchIndex) {
        m_currentBatchIndex = batchIndex;
    }

    /**
     * Add a pre-planned fragment.
     *
     * @param fragmentId
     * @param outputDepId
     * @param parameterSet
     */
    public void addFragment(byte[] planHash, int outputDepId, ByteBuffer parameterSet) {
        FragmentData item = new FragmentData();
        item.m_planHash = planHash;
        item.m_outputDepId = outputDepId;
        item.m_parameterSet = parameterSet;
        m_items.add(item);
    }

    /**
     * Add an unplanned fragment.
     *
     * @param fragmentId
     * @param outputDepId
     * @param parameterSet
     * @param fragmentPlan
     */
    public void addCustomFragment(byte[] planHash, int outputDepId, ByteBuffer parameterSet, byte[] fragmentPlan, String stmtText) {
        FragmentData item = new FragmentData();
        item.m_planHash = planHash;
        item.m_outputDepId = outputDepId;
        item.m_parameterSet = parameterSet;
        item.m_fragmentPlan = fragmentPlan;
        item.m_stmtText = stmtText.getBytes();
        m_items.add(item);
    }


    /**
     * Convenience factory method to replace constructor that includes arrays of stuff.
     *
     * @param initiatorHSId
     * @param coordinatorHSId
     * @param txnId
     * @param isReadOnly
     * @param fragmentId
     * @param outputDepId
     * @param parameterSet
     * @param isFinal
     *
     * @return new FragmentTaskMessage
     */
    public static FragmentTaskMessage createWithOneFragment(long initiatorHSId,
                                                            long coordinatorHSId,
                                                            long txnId,
                                                            long uniqueId,
                                                            boolean isReadOnly,
                                                            byte[] planHash,
                                                            int outputDepId,
                                                            ParameterSet params,
                                                            boolean isFinal,
                                                            boolean isForReplay) {
        ByteBuffer parambytes = null;
        if (params != null) {
            parambytes = ByteBuffer.allocate(params.getSerializedSize());
            try {
                params.flattenToBuffer(parambytes);
                parambytes.flip();
            }
            catch (IOException e) {
                VoltDB.crashLocalVoltDB("Failed to serialize parameter for fragment: " + params.toString(), true, e);
            }
        }

        FragmentTaskMessage ret = new FragmentTaskMessage(initiatorHSId, coordinatorHSId,
                                                          txnId, uniqueId, isReadOnly, isFinal, isForReplay);
        ret.addFragment(planHash, outputDepId, parambytes);
        return ret;
    }

    private boolean selfCheck() {
        for (FragmentData item : m_items) {
            if (item == null) {
                return false;
            }
            if (item.m_parameterSet == null) {
                return false;
            }
        }
        return true;
    }

    public void addInputDepId(int index, int depId) {
        assert(index >= 0 && index < m_items.size());
        FragmentData item = m_items.get(index);
        assert(item != null);
        if (item.m_inputDepIds == null)
            item.m_inputDepIds = new ArrayList<Integer>();
        item.m_inputDepIds.add(depId);
        m_inputDepCount++;
    }

    public ArrayList<Integer> getInputDepIds(int index) {
        assert(index >= 0 && index < m_items.size());
        FragmentData item = m_items.get(index);
        assert(item != null);
        return item.m_inputDepIds;
    }

    public int getOnlyInputDepId(int index) {
        assert(index >= 0 && index < m_items.size());
        FragmentData item = m_items.get(index);
        assert(item != null);
        if (item.m_inputDepIds == null)
            return -1;
        assert(item.m_inputDepIds.size() == 1);
        return item.m_inputDepIds.get(0);
    }

    public int[] getAllUnorderedInputDepIds() {
        int[] retval = new int[m_inputDepCount];
        int i = 0;
        for (FragmentData item : m_items) {
            if (item.m_inputDepIds != null) {
                for (int depId : item.m_inputDepIds) {
                    retval[i++] = depId;
                }
            }
        }
        assert(i == m_inputDepCount);
        return retval;
    }

    public void setFragmentTaskType(byte value) {
        m_taskType = value;
    }

    public boolean isFinalTask() {
        return m_isFinal;
    }

    public boolean isSysProcTask() {
        return (m_taskType != USER_PROC);
    }

    public byte getFragmentTaskType() {
        return m_taskType;
    }

    public int getFragmentCount() {
        return m_items.size();
    }

    // We're going to use this fragment task to generate a null distributed
    // fragment to serialize Completion and Borrow messages.  Create an empty
    // fragment with the provided outputDepId
    public void setEmptyForRestart(int outputDepId) {
        m_emptyForRestart = true;
        ParameterSet blank = ParameterSet.emptyParameterSet();
        ByteBuffer mt = ByteBuffer.allocate(blank.getSerializedSize());
        try {
            blank.flattenToBuffer(mt);
        }
        catch (IOException ioe) {
            // Shouldn't ever happen, just bail out to not-obviously equivalent behavior
            mt = ByteBuffer.allocate(2);
            mt.putShort((short)0);
        }
        addFragment(EMPTY_HASH, outputDepId, mt);
    }

    public boolean isEmptyForRestart() {
        return m_emptyForRestart;
    }

    public String getProcedureName() {
        Iv2InitiateTaskMessage initMsg = getInitiateTask();
        if (initMsg != null) {
            return initMsg.m_invocation.getProcName();
        }
        else if (m_procedureName != null) {
            return new String(m_procedureName, Charsets.UTF_8);
        }
        else {
            return null;
        }
    }

    /*
     * The first fragment contains the initiate task and the involved partitions set
     * for a multi-part txn for command logging.
     *
     * Involved partitions set is a set of partition IDs that are involved in this
     * multi-part txn.
     */
    public void setStateForDurability(Iv2InitiateTaskMessage initiateTask,
                                      Collection<Integer> involvedPartitions) {
        m_initiateTask = initiateTask;
        m_involvedPartitions = ImmutableSet.copyOf(involvedPartitions);
        m_initiateTaskBuffer = ByteBuffer.allocate(initiateTask.getSerializedSize());
        try {
            initiateTask.flattenToBuffer(m_initiateTaskBuffer);
            m_initiateTaskBuffer.flip();
        } catch (IOException e) {
            //Executive decision, don't throw a checked exception. Let it burn.
            throw new RuntimeException(e);
        }
    }

    public Iv2InitiateTaskMessage getInitiateTask() {
        return m_initiateTask;
    }

    public Set<Integer> getInvolvedPartitions() { return m_involvedPartitions; }

    public byte[] getPlanHash(int index) {
        assert(index >= 0 && index < m_items.size());
        FragmentData item = m_items.get(index);
        assert(item != null);
        return item.m_planHash;
    }

    public int getOutputDepId(int index) {
        assert(index >= 0 && index < m_items.size());
        FragmentData item = m_items.get(index);
        assert(item != null);
        return item.m_outputDepId;
    }

    public ByteBuffer getParameterDataForFragment(int index) {
        assert(index >= 0 && index < m_items.size());
        FragmentData item = m_items.get(index);
        assert(item != null);
        return item.m_parameterSet.asReadOnlyBuffer();
    }

    public ParameterSet getParameterSetForFragment(int index) {
        ParameterSet params = null;
        final ByteBuffer paramData = m_items.get(index).m_parameterSet.asReadOnlyBuffer();
        if (paramData != null) {
            try {
                params = ParameterSet.fromByteBuffer(paramData);
            }
            catch (final IOException e) {
                hostLog.l7dlog(Level.FATAL,
                        LogKeys.host_ExecutionSite_FailedDeserializingParamsForFragmentTask.name(), e);
                VoltDB.crashLocalVoltDB(e.getMessage(), true, e);
            }
        }
        else {
            params = ParameterSet.emptyParameterSet();
        }
        return params;
    }

    public byte[] getFragmentPlan(int index) {
        assert(index >= 0 && index < m_items.size());
        FragmentData item = m_items.get(index);
        assert(item != null);
        return item.m_fragmentPlan;
    }

    public String getStmtText(int index) {
        assert(index >= 0 && index < m_items.size());
        FragmentData item = m_items.get(index);
        assert(item != null);
        return new String(item.m_stmtText, Constants.UTF8ENCODING);
    }

    /*
     * Serialization Format [description: type: byte count]
     *
     * Fixed header:
     *     item count (nitems): short: 2
     *     unplanned item count (nunplanned): short: 2
     *     final flag: byte: 1
     *     task type: byte: 1
     *     should undo flag: byte: 1
     *     output dependencies flag (outdep): byte: 1
     *     input dependencies flag (indep): byte: 1
     *
     * Fragment ID block (1 per item):
     *     fragment ID: long: 8 * nitems
     *
     * Procedure name: byte: length of the name string.
     *
     * voltExecuteIndex: short: 2
     *
     * batchIndexBase: short: 2
     *
     * Parameter set block (1 per item):
     *     parameter buffer size: int: 4 * nitems
     *     parameter buffer: bytes: ? * nitems
     *
     * Output dependency block (1 per item if outdep == 1):
     *     output dependency ID: int: 4 * nitems
     *
     * Input dependency block (1 per item if indep == 1):
     *     ID count: short: 2 * nitems
     *     input dependency ID sub-block (1 per ID):
     *         input dependency ID: int: 4 * ? * nitems
     *
     * Unplanned block (1 of each per unplanned item):
     *    item index: short: 2 * nunplanned
     *    fragment plan string length: int: 4 * nunplanned
     *    fragment plan string: bytes: ? * nunplanned
     */

    @Override
    public int getSerializedSize()
    {
        assert(m_items != null);
        assert(!m_items.isEmpty());

        int msgsize = super.getSerializedSize();

        // Fixed header
        msgsize += 2 + 2 + 1 + 1 + 1 + 1 + 1;

        // Fragment ID block (20 bytes per sha1-hash)
        msgsize += 20 * m_items.size();

        // short + str for proc name
        msgsize += 2;
        if (m_procedureName != null) {
            msgsize += m_procedureName.length;
        }

        // int for which batch (4)
        msgsize += 4;

        // Involved partitions
        msgsize += 2 + m_involvedPartitions.size() * 4;

        //nested initiate task message length prefix
        msgsize += 4;
        if (m_initiateTaskBuffer != null) {
            msgsize += m_initiateTaskBuffer.remaining();
        }

        // Make a pass through the fragment data items to account for the
        // optional output and input dependency blocks, plus the unplanned block.
        boolean foundOutputDepId = false;
        boolean foundInputDepIds = false;
        for (FragmentData item : m_items) {
            // Account for parameter sets
            msgsize += 4 + item.m_parameterSet.remaining();

            // Account for the optional output dependency block, if needed.
            if (!foundOutputDepId && item.m_outputDepId != null) {
                msgsize += 4 * m_items.size();
                foundOutputDepId = true;
            }

            // Account for the optional input dependency block, if needed.
            if (item.m_inputDepIds != null) {
                if (!foundInputDepIds) {
                    // Account for the size short for each item now that we know
                    // that the optional block is needed.
                    msgsize += 2 * m_items.size();
                    foundInputDepIds = true;
                }
                // Account for the input dependency IDs themselves, if any.
                msgsize += 4 * item.m_inputDepIds.size();
            }

            // Each unplanned item gets:
            //  - an index (2)
            //  - a size (4)
            //  - buffer for fragment plan string
            //  - a size (4)
            //  - a buffer for statement text
            if (item.m_fragmentPlan != null) {
                msgsize += 2 + 4 + item.m_fragmentPlan.length;

                assert(item.m_stmtText != null);
                msgsize += 4 + item.m_stmtText.length;
            }
        }

        return msgsize;
    }

    @Override
    public void flattenToBuffer(ByteBuffer buf) throws IOException
    {
        flattenToSubMessageBuffer(buf);
        assert(buf.capacity() == buf.position());
        buf.limit(buf.position());
    }

    /**
     * Used directly by {@link FragmentTaskLogMessage} to embed FTMs
     */
    void flattenToSubMessageBuffer(ByteBuffer buf) throws IOException
    {
        // See Serialization Format comment above getSerializedSize().

        assert(m_items != null);
        assert(!m_items.isEmpty());

        buf.put(VoltDbMessageFactory.FRAGMENT_TASK_ID);
        super.flattenToBuffer(buf);

        // Get useful statistics for the header and optional blocks.
        short nInputDepIds = 0;
        short nOutputDepIds = 0;
        short nUnplanned = 0;
        for (FragmentData item : m_items) {
            if (item.m_inputDepIds != null) {
                // Supporting only one input dep id for now.
                nInputDepIds++;
            }
            if (item.m_outputDepId != null) {
                nOutputDepIds++;
            }
            if (item.m_fragmentPlan != null) {
                nUnplanned++;
            }
        }

        // Header block
        buf.putShort((short) m_items.size());
        buf.putShort(nUnplanned);
        buf.put(m_isFinal ? (byte) 1 : (byte) 0);
        buf.put(m_taskType);
        buf.put(m_emptyForRestart ? (byte) 1 : (byte) 0);
        buf.put(nOutputDepIds > 0 ? (byte) 1 : (byte) 0);
        buf.put(nInputDepIds  > 0 ? (byte) 1 : (byte) 0);

        // Plan Hash block
        for (FragmentData item : m_items) {
            buf.put(item.m_planHash);
        }

        // Parameter set block
        for (FragmentData item : m_items) {
            buf.putInt(item.m_parameterSet.remaining());
            buf.put(item.m_parameterSet.asReadOnlyBuffer());
        }

        // Optional output dependency ID block
        if (nOutputDepIds > 0) {
            for (FragmentData item : m_items) {
                buf.putInt(item.m_outputDepId);
            }
        }

        // Optional input dependency ID block
        if (nInputDepIds > 0) {
            for (FragmentData item : m_items) {
                if (item.m_inputDepIds == null || item.m_inputDepIds.size() == 0) {
                    buf.putShort((short) 0);
                } else {
                    buf.putShort((short) item.m_inputDepIds.size());
                    for (Integer inputDepId : item.m_inputDepIds) {
                        buf.putInt(inputDepId);
                    }
                }
            }
        }

        // write procedure name
        if (m_procedureName == null) {
            buf.putShort((short) -1);
        }
        else {
            assert(m_procedureName.length <= Short.MAX_VALUE);
            buf.putShort((short) m_procedureName.length);
            buf.put(m_procedureName);
        }

        // ints for batch context
        buf.putInt(m_currentBatchIndex);

        buf.putShort((short) m_involvedPartitions.size());
        for (int pid : m_involvedPartitions) {
            buf.putInt(pid);
        }

        if (m_initiateTaskBuffer != null) {
            ByteBuffer dup = m_initiateTaskBuffer.duplicate();
            buf.putInt(dup.remaining());
            buf.put(dup);
        } else {
            buf.putInt(0);
        }

        // Unplanned item block
        for (short index = 0; index < m_items.size(); index++) {
            // Each unplanned item gets:
            //  - an index (2)
            //  - a size (4)
            //  - buffer for the fragment plan string
            //  - a size(4)
            //  - buffer for the statement text
            FragmentData item = m_items.get(index);
            if (item.m_fragmentPlan != null) {
                buf.putShort(index);
                buf.putInt(item.m_fragmentPlan.length);
                buf.put(item.m_fragmentPlan);

                assert(item.m_stmtText != null);
                buf.putInt(item.m_stmtText.length);
                buf.put(item.m_stmtText);
            }
        }
    }

    @Override
    public void initFromBuffer(ByteBuffer buf) throws IOException
    {
        initFromSubMessageBuffer(buf);
        assert(buf.capacity() == buf.position());
    }

    /**
     * Used directly by {@link FragmentTaskLogMessage} to embed FTMs
     */
    void initFromSubMessageBuffer(ByteBuffer buf) throws IOException
    {
        // See Serialization Format comment above getSerializedSize().

        super.initFromBuffer(buf);

        // Header block
        short fragCount = buf.getShort();
        assert(fragCount > 0);
        short unplannedCount = buf.getShort();
        assert(unplannedCount >= 0 && unplannedCount <= fragCount);
        m_isFinal = buf.get() != 0;
        m_taskType = buf.get();
        m_emptyForRestart = buf.get() != 0;
        boolean haveOutputDependencies = buf.get() != 0;
        boolean haveInputDependencies = buf.get() != 0;

        m_items = new ArrayList<FragmentData>(fragCount);

        // Fragment ID block (creates the FragmentData objects)
        for (int i = 0; i < fragCount; i++) {
            FragmentData item = new FragmentData();
            item.m_planHash = new byte[20]; // sha1 is 20b
            buf.get(item.m_planHash);
            m_items.add(item);
        }

        // Parameter set block
        for (FragmentData item : m_items) {
            int paramsbytecount = buf.getInt();
            item.m_parameterSet = ByteBuffer.allocate(paramsbytecount);
            int cachedLimit = buf.limit();
            buf.limit(buf.position() + item.m_parameterSet.remaining());
            item.m_parameterSet.put(buf);
            item.m_parameterSet.flip();
            buf.limit(cachedLimit);
        }

        // Optional output dependency block
        if (haveOutputDependencies) {
            for (FragmentData item : m_items) {
                item.m_outputDepId = buf.getInt();
            }
        }

        // Optional input dependency block
        if (haveInputDependencies) {
            for (FragmentData item : m_items) {
                short count = buf.getShort();
                if (count > 0) {
                    item.m_inputDepIds = new ArrayList<Integer>(count);
                    for (int j = 0; j < count; j++) {
                        item.m_inputDepIds.add(buf.getInt());
                        m_inputDepCount++;
                    }
                }
            }
        }

        // read procedure name if there
        short procNameLen = buf.getShort();
        if (procNameLen >= 0) {
            m_procedureName = new byte[procNameLen];
            buf.get(m_procedureName);
        }
        else {
            m_procedureName = null;
        }

        // ints for batch context
        m_currentBatchIndex = buf.getInt();

        // Involved partition
        short involvedPartitionCount = buf.getShort();
        ImmutableSet.Builder<Integer> involvedPartitionsBuilder = ImmutableSet.builder();
        for (int i = 0; i < involvedPartitionCount; i++) {
            involvedPartitionsBuilder.add(buf.getInt());
        }
        m_involvedPartitions = involvedPartitionsBuilder.build();

        int initiateTaskMessageLength = buf.getInt();
        if (initiateTaskMessageLength > 0) {
            int startPosition = buf.position();
            Iv2InitiateTaskMessage message = new Iv2InitiateTaskMessage();
            // EHGAWD: init task was serialized with flatten which added
            // the message type byte. deserialization expects the message
            // factory to have stripped that byte. but ... that's not the
            // way we do it here. So read the message type byte...
            byte messageType = buf.get();
            assert(messageType == VoltDbMessageFactory.IV2_INITIATE_TASK_ID);
            message.initFromBuffer(buf);
            m_initiateTask = message;
            if (m_initiateTask != null && m_initiateTaskBuffer == null) {
                m_initiateTaskBuffer = ByteBuffer.allocate(m_initiateTask.getSerializedSize());
                try {
                    m_initiateTask.flattenToBuffer(m_initiateTaskBuffer);
                    m_initiateTaskBuffer.flip();
                } catch (IOException e) {
                    //Executive decision, don't throw a checked exception. Let it burn.
                    throw new RuntimeException(e);
                }
            }

            /*
             * There is an assertion that all bytes of the message are consumed.
             * Initiate task lazily deserializes the parameter buffer and doesn't consume
             * all the bytes so do it here so the assertion doesn't trip
             */
            buf.position(startPosition + initiateTaskMessageLength);
        }

        // Unplanned block
        for (int iUnplanned = 0; iUnplanned < unplannedCount; iUnplanned++) {
            short index = buf.getShort();
            assert(index >= 0 && index < m_items.size());
            FragmentData item = m_items.get(index);
            int fragmentPlanLength = buf.getInt();
            if (fragmentPlanLength > 0) {
                item.m_fragmentPlan = new byte[fragmentPlanLength];
                buf.get(item.m_fragmentPlan);

                int stmtTextLength = buf.getInt();
                item.m_stmtText = new byte[stmtTextLength];
                buf.get(item.m_stmtText);
            }
        }
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();

        sb.append("FRAGMENT_TASK (FROM ");
        sb.append(CoreUtils.hsIdToString(m_coordinatorHSId));
        sb.append(") FOR TXN ");
        sb.append(m_txnId);
        sb.append(" FOR REPLAY ").append(isForReplay());
        sb.append(", SP HANDLE: ").append(getSpHandle());
        sb.append("\n");
        if (m_isReadOnly)
            sb.append("  READ, COORD ");
        else
            sb.append("  WRITE, COORD ");
        sb.append(CoreUtils.hsIdToString(m_coordinatorHSId));

        if (!m_emptyForRestart) {
            for (FragmentData item : m_items) {
                sb.append("\n=====\n");
                sb.append(item.toString());
            }
        }
        else {
            sb.append("\n=====\n");
            sb.append("  FRAGMENT EMPTY FOR RESTART SERIALIZATION");
        }

        if (m_isFinal)
            sb.append("\n  THIS IS THE FINAL TASK");

        if (m_taskType == USER_PROC)
        {
            sb.append("\n  THIS IS A USER TASK");
        }
        else if (m_taskType == SYS_PROC_PER_PARTITION)
        {
            sb.append("\n  THIS IS A SYSPROC RUNNING PER PARTITION");
        }
        else if (m_taskType == SYS_PROC_PER_SITE)
        {
            sb.append("\n  THIS IS A SYSPROC TASK RUNNING PER EXECUTION SITE");
        }
        else
        {
            sb.append("\n  UNKNOWN FRAGMENT TASK TYPE");
        }

        if (m_emptyForRestart)
            sb.append("\n  THIS IS A NULL FRAGMENT TASK USED FOR RESTART");

        return sb.toString();
    }

    public boolean isEmpty() {
        return m_items.isEmpty();
    }
}
TOP

Related Classes of org.voltdb.messaging.FragmentTaskMessage

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.