/* This file is part of VoltDB.
* Copyright (C) 2008-2017 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.utils;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.voltcore.utils.DBBPool.BBContainer;
import org.voltcore.utils.DeferredSerialization;
import org.voltcore.utils.Pair;
/**
* Specialized deque interface for storing binary objects. Objects can be provided as a buffer chain
* and will be returned as a single buffer. Technically not a deque because removal at
* the end is not supported.
*
*/
public interface BinaryDeque {
/*
* Allocator for storage coming out of the BinaryDeque. Only
* used if copying is necessary, otherwise a slice is returned
*/
public static interface OutputContainerFactory {
public BBContainer getContainer(int minimumSize);
}
/**
* Store a buffer chain as a single object in the deque. IOException may be thrown if the object
* is larger then the implementation defined max. 64 megabytes in the case of PersistentBinaryDeque.
* If there is an exception attempting to write the buffers then all the buffers will be discarded
* @param object
* @throws IOException
*/
void offer(BBContainer object) throws IOException;
/**
* Store a buffer chain as a single object in the deque. IOException may be thrown if the object
* is larger then the implementation defined max. 64 megabytes in the case of PersistentBinaryDeque.
* If there is an exception attempting to write the buffers then all the buffers will be discarded
* @param object
* @param allowCompression
* @throws IOException
*/
void offer(BBContainer object, boolean allowCompression) throws IOException;
int offer(DeferredSerialization ds) throws IOException;
/**
* A push creates a new file each time to be "the head" so it is more efficient to pass
* in all the objects you want to push at once so that they can be packed into
* as few files as possible. IOException may be thrown if the object
* is larger then the implementation defined max. 64 megabytes in the case of PersistentBinaryDeque.
* If there is an exception attempting to write the buffers then all the buffers will be discarded
* @param objects Array of buffers representing the objects to be pushed to the head of the queue
* @throws java.io.IOException
*/
public void push(BBContainer objects[]) throws IOException;
/**
* Start a BinaryDequeReader for reading, positioned at the start of the deque.
* @param cursorId a String identifying the cursor. If a cursor is already open for this id,
* the existing cursor will be returned.
* @return a BinaryDequeReader for this cursorId
* @throws IOException on any errors trying to read the PBD files
*/
public BinaryDequeReader openForRead(String cursorId) throws IOException;
/**
* Close a BinaryDequeReader for reader, also close the SegmentReader for the segment if it is reading one
* @param cursorId a String identifying the cursor.
* @throws IOException on any errors trying to close the SegmentReader if it is the last one for the segment
*/
public void closeCursor(String cursorId);
/**
* Persist all objects in the queue to the backing store
* @throws IOException
*/
public void sync() throws IOException;
public void parseAndTruncate(BinaryDequeTruncator truncator) throws IOException;
/**
* Release all resources (open files) held by the back store of the queue. Continuing to use the deque
* will result in an exception
* @throws IOException
*/
public void close() throws IOException;
public boolean initializedFromExistingFiles();
public Pair<Integer, Long> getBufferCountAndSize() throws IOException;
public void closeAndDelete() throws IOException;
/**
* Reader class used to read entries from the deque. Multiple readers may be active at the same time,
* each of them maintaining their own read location within the deque.
*/
public interface BinaryDequeReader {
/**
* Read and return the object at the current read position of this reader.
* The entry will be removed once all active readers have read the entry.
* @param ocf
* @return BBContainer with the bytes read. Null if there is nothing left to read.
* @throws IOException
*/
public BBContainer poll(OutputContainerFactory ocf) throws IOException;
/**
* Number of bytes left to read for this reader.
* @return number of bytes left to read for this reader.
* @throws IOException
*/
public long sizeInBytes() throws IOException;
/**
* Number of objects left to read for this reader.
* @return number of objects left to read for this reader
* @throws IOException
*/
public int getNumObjects() throws IOException;
/**
* Returns true if this reader still has entries to read. False otherwise
* @return true if this reader still has entries to read. False otherwise
* @throws IOException
*/
public boolean isEmpty() throws IOException;
}
public static class TruncatorResponse {
public enum Status {
FULL_TRUNCATE,
PARTIAL_TRUNCATE
}
public final Status status;
public TruncatorResponse(Status status) {
this.status = status;
}
public int writeTruncatedObject(ByteBuffer output) throws IOException {
throw new UnsupportedOperationException("Must implement this for partial object truncation");
}
}
/*
* A binary deque truncator parses all the objects in a binary deque
* from head to tail until it find the truncation point. At the truncation
* point it can return a version of the last object passed to it that will be updated in place.
* Everything after that object in the deque will be truncated and deleted.
*/
public interface BinaryDequeTruncator {
/*
* Invoked by parseAndTruncate on every object in the deque from head to tail
* until parse returns a non-null ByteBuffer. The returned ByteBuffer can be length 0 or it can contain
* an object to replace the last object that was passed to the binary deque. If the length is 0
* then the last object passed to parse will be truncated out of the deque. Part of the object
* or a new object can be returned to replace it.
*/
public TruncatorResponse parse(BBContainer bb);
}
}