Package org.voltdb

Source Code of org.voltdb.TableStreamer

/* 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;

import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;

import org.voltcore.logging.VoltLogger;
import org.voltcore.utils.DBBPool;
import org.voltcore.utils.DBBPool.BBContainer;
import org.voltcore.utils.Pair;
import org.voltdb.utils.CatalogUtil;

import com.google_voltpatches.common.base.Preconditions;
import com.google_voltpatches.common.collect.ImmutableList;
import com.google_voltpatches.common.collect.UnmodifiableIterator;
import com.google_voltpatches.common.util.concurrent.Callables;
import com.google_voltpatches.common.util.concurrent.Futures;
import com.google_voltpatches.common.util.concurrent.ListenableFuture;

/**
* A class that manages streaming data from a single table. It knows how to interpret the return codes and check for
* errors related to table data streaming using the table stream API.
*/
public class TableStreamer {
    private static final VoltLogger log = new VoltLogger("HOST");

    // Error code returned by EE.tableStreamSerializeMore().
    private static final byte SERIALIZATION_ERROR = -1;

    private final int m_tableId;
    private final TableStreamType m_type;
    private final ImmutableList<SnapshotTableTask> m_tableTasks;

    public TableStreamer(int tableId, TableStreamType type, List<SnapshotTableTask> tableTasks)
    {
        m_tableId = tableId;
        m_type = type;
        m_tableTasks = ImmutableList.copyOf(tableTasks);
    }

    /**
     * Activate the stream with the given predicates on the given table.
     * @param context       Context
     * @param predicates    Predicates associated with the stream
     * @return true if activation succeeded.
     */
    public boolean activate(SystemProcedureExecutionContext context, byte[] predicates)
    {
        return activate(context, false, predicates);
    }

    /**
     * Activate the stream with the given predicates on the given table.
     * @param context       Context
     * @param undoToken     The undo token
     * @param predicates    Predicates associated with the stream
     * @return true if activation succeeded.
     */
    public boolean activate(SystemProcedureExecutionContext context, boolean undo, byte[] predicates)
    {
        if (!context.activateTableStream(m_tableId, m_type, undo, predicates)) {
            String tableName = CatalogUtil.getTableNameFromId(context.getDatabase(), m_tableId);
            log.debug("Attempted to activate a table stream of type " + m_type +
                      "for table " + tableName + " and failed");
            return false;
        }
        return true;
    }

    /**
     * Streams more tuples from the table.
     * @param context          Context
     * @param outputBuffers    Allocated buffers to hold output tuples
     * @param rowCountAccumulator an array of a single int use to accumulate streamed rows count
     * @return A future for all writes to data targets, and a boolean indicating if there's more left in the table.
     * The future could be null if nothing is serialized. If row count is specified it sets the number of rows that
     * is to stream
     */
    @SuppressWarnings("rawtypes")
    public Pair<ListenableFuture, Boolean> streamMore(SystemProcedureExecutionContext context,
                                                      List<DBBPool.BBContainer> outputBuffers,
                                                      int[] rowCountAccumulator)
    {
        ListenableFuture writeFuture = null;

        prepareBuffers(outputBuffers);

        Pair<Long, int[]> serializeResult = context.tableStreamSerializeMore(m_tableId, m_type, outputBuffers);
        if (serializeResult.getFirst() == SERIALIZATION_ERROR) {
            VoltDB.crashLocalVoltDB("Failure while serializing data from table " + m_tableId, false, null);
        }

        if (serializeResult.getSecond()[0] > 0) {
            if (rowCountAccumulator != null && rowCountAccumulator.length == 1) {
                rowCountAccumulator[0] += getTupleDataRowCount(outputBuffers);
            }
            writeFuture = writeBlocksToTargets(outputBuffers, serializeResult.getSecond());
        } else {
            // Return all allocated snapshot output buffers
            for (DBBPool.BBContainer container : outputBuffers) {
                container.discard();
            }
        }

        return Pair.of(writeFuture, serializeResult.getFirst() > 0);
    }

    /**
     * Get the number of rows contained of rows contained within the given list of {@link BBContainer}
     * @param outputBuffers a list of tuple data BBContainers
     * @return the number of rows contained within the given list of {@link BBContainer}
     */
    private int getTupleDataRowCount(List<DBBPool.BBContainer> outputBuffers) {
        if (outputBuffers == null || outputBuffers.size() != m_tableTasks.size()) return 0;
        int accumulator = 0;
        for (int i = 0; i < outputBuffers.size(); ++i) {
            SnapshotDataTarget target = m_tableTasks.get(i).m_target;
            int rowCount = target.getInContainerRowCount(outputBuffers.get(i));
            if (rowCount != SnapshotDataTarget.ROW_COUNT_UNSUPPORTED) {
                accumulator += target.getInContainerRowCount(outputBuffers.get(i));
            }
        }
        return accumulator;
    }

    /**
     * Set the positions of the buffers to the start of the content, leaving some room for the headers.
     */
    private void prepareBuffers(List<DBBPool.BBContainer> buffers)
    {
        Preconditions.checkArgument(buffers.size() == m_tableTasks.size());

        UnmodifiableIterator<SnapshotTableTask> iterator = m_tableTasks.iterator();
        for (DBBPool.BBContainer container : buffers) {
            int headerSize = iterator.next().m_target.getHeaderSize();
            final ByteBuffer buf = container.b();
            buf.clear();
            buf.position(headerSize);
        }
    }

    /**
     * Finalize the output buffers and write them to the corresponding data targets
     *
     * @return A future that can used to wait for all targets to finish writing the buffers
     */
    private ListenableFuture<?> writeBlocksToTargets(Collection<DBBPool.BBContainer> outputBuffers,
                                                     int[] serialized)
    {
        Preconditions.checkArgument(m_tableTasks.size() == serialized.length);
        Preconditions.checkArgument(outputBuffers.size() == serialized.length);

        final List<ListenableFuture<?>> writeFutures =
            new ArrayList<ListenableFuture<?>>(outputBuffers.size());

        // The containers, the data targets, and the serialized byte counts should all line up
        Iterator<DBBPool.BBContainer> containerIter = outputBuffers.iterator();
        int serializedIndex = 0;

        for (SnapshotTableTask task : m_tableTasks) {
            final DBBPool.BBContainer container = containerIter.next();

            /*
             * Finalize the buffer by setting position to 0 and limit to the last used byte
             */
            final ByteBuffer buf = container.b();
            buf.limit(serialized[serializedIndex++] + task.m_target.getHeaderSize());
            buf.position(0);

            Callable<DBBPool.BBContainer> valueForTarget = Callables.returning(container);
            if (task.m_filters != null) {
                for (SnapshotDataFilter filter : task.m_filters) {
                    valueForTarget = filter.filter(valueForTarget);
                }
            }

            ListenableFuture<?> writeFuture = task.m_target.write(valueForTarget, m_tableId);
            if (writeFuture != null) {
                writeFutures.add(writeFuture);
            }
        }

        // Wraps all write futures in one future
        return Futures.allAsList(writeFutures);
    }
}
TOP

Related Classes of org.voltdb.TableStreamer

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.