All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.voltdb.rejoin.RejoinTaskBuffer Maven / Gradle / Ivy

There is a newer version: 10.1.1
Show newest version
/* This file is part of VoltDB.
 * Copyright (C) 2008-2018 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 .
 */

package org.voltdb.rejoin;

import java.io.IOException;
import java.nio.ByteBuffer;

import org.voltcore.logging.VoltLogger;
import org.voltcore.messaging.TransactionInfoBaseMessage;
import org.voltcore.messaging.VoltMessage;
import org.voltcore.utils.DBBPool;
import org.voltcore.utils.DBBPool.BBContainer;
import org.voltdb.messaging.VoltDbMessageFactory;
import org.voltdb.utils.AllocationStrategy;

import com.google_voltpatches.common.base.Preconditions;

/**
 * Stores a binary serialization of an arbitrary number of procedure invocations,
 * all from a single partition in a single time window.
 *
 */
public class RejoinTaskBuffer {
    /**
     *  Default Invocation buffer size
     */
    public static int DEFAULT_BUFFER_SIZE = 1024 * 256;

    private static final VoltLogger log = new VoltLogger("DR");

    public final int partitionId;
    private int compiledSize = 0;
    BBContainer m_container;
    AllocationStrategy m_allocator = null;

    /**
     * The size of the metadata, also the size of an empty buffer
     * @return
     */
    public static int metadataSize() {
        return 4; /* partitionId */
    }

    /**
     * The size of task header
     */
    public static int taskHeaderSize() {
        return 4 /* int task size */
             + 8 /* long source HSID */
             ;
    }

    /**
     * Constructor used for creating a mutable (append-only) buffer of
     * invocations for a partition.
     *
     * @param partitionId
     *            The partition id this buffer belongs to.
     * @param sizeHint
     *            Initial Allocation size Hint
     */
    public RejoinTaskBuffer(int partitionId, int sizeHint) {
        this.partitionId = partitionId;
        this.m_allocator = AllocationStrategy.LR;
        this.m_container = m_allocator.allocate(sizeHint);

        ByteBuffer bb = m_container.b();
        bb.putInt(partitionId);
    }

    /**
     * Constructor used for creating an immutable buffer of invocations from
     * a network message.
     * @param cont Container for byte buffer containing the message data.
     */
    public RejoinTaskBuffer(BBContainer cont) {
        m_container = cont;

        ByteBuffer bb = m_container.b();

        compiledSize = bb.limit();
        partitionId = bb.getInt();

        m_container = cont;

        if (log.isTraceEnabled()) {
            StringBuilder sb = new StringBuilder("Constructing buffer:");
            ByteBuffer dup = m_container.bDR();
            dup.position(0);
            while (dup.hasRemaining()) {
                sb.append(" ").append(dup.get());
            }
            log.trace(sb.toString());
        }
    }

    public int size() {
        if (compiledSize == 0) {
            return m_container.b().position();
        } else {
            return compiledSize;
        }
    }

    public boolean isReadOnly() {
        return compiledSize > 0;
    }

    /**
     * Expand the underlying byte buffer to contain the specified delta
     * @param delta amount needed to accommodate the next buffer append
     */
    private void ensureCapacity(int delta) {
        ByteBuffer bb = m_container.b();
        int bufferDelta = (bb.position() + delta) - bb.capacity();
        if (bufferDelta > 0) {
            BBContainer ncntnr = DBBPool.allocateUnsafeByteBuffer(bb.capacity() + bufferDelta);

            ByteBuffer newbb = ncntnr.b();
            bb.flip();
            newbb.put(bb);

            m_container.discard();
            m_container = ncntnr;
        }
    }

    /**
     * Appends a task message to the buffer.
     * @param sourceHSId
     * @param task
     * @throws IOException If the buffer is not of the type TASK
     * @return how many bytes are left in this buffer for adding a new task
     */
    public int appendTask(long sourceHSId, TransactionInfoBaseMessage task) throws IOException {
        Preconditions.checkState(compiledSize == 0, "buffer is already compiled");

        final int msgSerializedSize = task.getSerializedSize();
        ensureCapacity(taskHeaderSize() + msgSerializedSize);

        ByteBuffer bb = m_container.b();
        bb.putInt(msgSerializedSize);
        bb.putLong(sourceHSId);

        int limit = bb.limit();
        bb.limit(bb.position() + msgSerializedSize);
        task.flattenToBuffer(bb.slice());
        bb.limit(limit);
        bb.position(bb.position() + msgSerializedSize);

        // Don't allow any further expansion to the underlying buffer
        if (bb.position() + taskHeaderSize() > DEFAULT_BUFFER_SIZE) {
            compile();
            return 0;
        } else {
            return DEFAULT_BUFFER_SIZE - (bb.position() + taskHeaderSize());
        }
    }

    /**
     * Get the next task message in this buffer.
     *
     * @return null if there is no more messages
     * @throws IOException
     *             if message deserialization fails.
     */
    public TransactionInfoBaseMessage nextTask() throws IOException {
        if (!hasMoreEntries()) {
            return null;
        }

        ByteBuffer bb = m_container.b();

        int position = bb.position();
        int length = bb.getInt();
        long sourceHSId = bb.getLong();
        VoltDbMessageFactory factory = new VoltDbMessageFactory();
        /*
         * create a new buffer that just contains the message, deserialization
         * of the messsage may assert on the capacity of the buffer
         */
        final int oldLimit = bb.limit();
        bb.limit(bb.position() + length);
        ByteBuffer slice = bb.slice();
        bb.limit(oldLimit);
        VoltMessage msg = factory.createMessageFromBuffer(slice, sourceHSId);
        // createMessageFromBuffer() doesn't move the position pointer, set it here
        bb.position(position + length + 8 + 4); // sourceHSId + buf len
        return (TransactionInfoBaseMessage) msg;
    }

    /**
     * @return true if has more entries to deserialize, false if the buffer is
     *         not compiled yet or if there is no more entries.
     * @throws IOException
     */
    private boolean hasMoreEntries() throws IOException {
        if (compiledSize == 0) {
            throw new IOException("Close the buffer by calling compile before reading it");
        }
        ByteBuffer bb = m_container.b();
        if (bb.position() < metadataSize()) {
            bb.position(metadataSize());
        }
        return bb.hasRemaining();
    }

    /**
     * Generate the byte array in preparation of moving over a message bus.
     * Idempotent, but not thread-safe. Also changes state to immutable.
     */
    public void compile() {
        if (compiledSize == 0) {
            ByteBuffer bb = m_container.b();
            compiledSize = bb.position();
            bb.flip();
            m_allocator.track(compiledSize);
        }

        if (log.isTraceEnabled()) {
            StringBuilder sb = new StringBuilder("Compiling buffer:   ");
            ByteBuffer dup = m_container.bDR();
            while (dup.hasRemaining()) {
                sb.append(" ").append(dup.get());
            }
            log.trace(sb.toString());
        }
    }

    /**
     *
     * @return the {@link BBContainer} containing the compiled
     */
    public BBContainer getContainer() {
        Preconditions.checkState(compiledSize > 0, "invocation buffer in not compiled");
        return m_container;
    }

    @Override
    public String toString() {
        return String.format("TASK BUFFER p=%d", partitionId);
    }

    public void discard() {
        if (m_container != null) {
            m_container.discard();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy