Package voldemort.server.protocol.admin

Source Code of voldemort.server.protocol.admin.FetchPartitionFileStreamRequestHandler$ChunkedFileWriter

package voldemort.server.protocol.admin;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.WritableByteChannel;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import org.apache.log4j.Logger;

import voldemort.VoldemortException;
import voldemort.client.protocol.pb.ProtoUtils;
import voldemort.client.protocol.pb.VAdminProto;
import voldemort.routing.StoreRoutingPlan;
import voldemort.server.StoreRepository;
import voldemort.server.VoldemortConfig;
import voldemort.server.protocol.StreamRequestHandler;
import voldemort.store.StoreDefinition;
import voldemort.store.metadata.MetadataStore;
import voldemort.store.readonly.ReadOnlyStorageConfiguration;
import voldemort.store.readonly.ReadOnlyStorageEngine;
import voldemort.store.stats.StreamingStats;
import voldemort.store.stats.StreamingStats.Operation;
import voldemort.utils.EventThrottler;
import voldemort.utils.Pair;

public class FetchPartitionFileStreamRequestHandler implements StreamRequestHandler {

    private final VAdminProto.FetchPartitionFilesRequest request;

    private final EventThrottler throttler;

    private final File storeDir;

    private final Logger logger = Logger.getLogger(getClass());

    private final long blockSize;

    private final StreamingStats streamStats;

    private final Iterator<Integer> partitionIterator;

    private FetchStatus fetchStatus;

    private int currentChunkId;

    private int numChunks;

    private Pair<Integer, Integer> currentPair;

    private File indexFile;

    private File dataFile;

    private ChunkedFileWriter chunkedFileWriter;

    private final List<Integer> partitionIds;

    private final HashMap<Object, Integer> bucketToNumChunks;

    private final boolean nioEnabled;

    private final MetadataStore metadataStore;

    private enum FetchStatus {
        NEXT_PARTITION,
        SEND_DATA_FILE,
        SEND_INDEX_FILE
    }

    protected FetchPartitionFileStreamRequestHandler(VAdminProto.FetchPartitionFilesRequest request,
                                                     MetadataStore metadataStore,
                                                     VoldemortConfig voldemortConfig,
                                                     StoreRepository storeRepository) {
        this.request = request;
        // TODO (Sid) : Confirm if keeping metadatastore as a class property is
        // the best way to do this. metadataStore is used later in the
        // handleNextPartition() to create object plan.
        this.metadataStore = metadataStore;

        StoreDefinition storeDef = metadataStore.getStoreDef(request.getStoreName());
        boolean isReadOnly = storeDef.getType().compareTo(ReadOnlyStorageConfiguration.TYPE_NAME) == 0;
        if(!isReadOnly) {
            throw new VoldemortException("Should be fetching partition files only for read-only stores");
        }

        List<Integer> partitionIds = request.getPartitionIdsList();
        this.partitionIds = partitionIds;
        ReadOnlyStorageEngine storageEngine = AdminServiceRequestHandler.getReadOnlyStorageEngine(metadataStore,
                                                                                                  storeRepository,
                                                                                                  request.getStoreName());
        this.bucketToNumChunks = storageEngine.getChunkedFileSet().getChunkIdToNumChunks();
        this.blockSize = voldemortConfig.getAllProps()
                                        .getLong("partition.buffer.size.bytes",
                                                 voldemortConfig.getAdminSocketBufferSize());
        this.storeDir = new File(storageEngine.getCurrentDirPath());
        this.throttler = new EventThrottler(voldemortConfig.getStreamMaxReadBytesPerSec());
        if(voldemortConfig.isJmxEnabled()) {
            this.streamStats = storeRepository.getStreamingStats(storageEngine.getName());
        } else {
            this.streamStats = null;
        }
        this.partitionIterator = Collections.unmodifiableList(partitionIds).iterator();
        this.fetchStatus = FetchStatus.NEXT_PARTITION;
        this.currentChunkId = 0;
        this.indexFile = null;
        this.dataFile = null;
        this.chunkedFileWriter = null;
        this.numChunks = 0;

        this.nioEnabled = voldemortConfig.getUseNioConnector();
    }

    @Override
    public StreamRequestDirection getDirection() {
        return StreamRequestDirection.WRITING;
    }

    @Override
    public final void close(DataOutputStream outputStream) throws IOException {
        ProtoUtils.writeEndOfStream(outputStream);
    }

    @Override
    public final void handleError(DataOutputStream outputStream, VoldemortException e)
            throws IOException {
        logger.error("handleFetchPartitionFilesEntries failed for request(" + request.toString()
                     + ")", e);
    }

    @Override
    public StreamRequestHandlerState handleRequest(DataInputStream inputStream,
                                                   DataOutputStream outputStream)
            throws IOException {
        StreamRequestHandlerState handlerState = StreamRequestHandlerState.WRITING;

        switch(fetchStatus) {
            case NEXT_PARTITION:
                handlerState = handleNextPartition();
                break;
            case SEND_DATA_FILE:
                handleSendDataFile(outputStream);
                break;
            case SEND_INDEX_FILE:
                handleSendIndexFile();
                break;
            default:
                throw new VoldemortException("Invalid fetch status " + fetchStatus);
        }

        return handlerState;
    }

    private void handleSendIndexFile() throws IOException {

        if(0 == chunkedFileWriter.streamFile()) {

            // We are done with the index file, move to next chunk
            logger.info("Completed streaming " + indexFile.getAbsolutePath());
            this.chunkedFileWriter.close();
            currentChunkId++;
            dataFile = indexFile = null;
            if(streamStats != null)
                streamStats.reportStreamingFetch(Operation.FETCH_FILE);
            if(currentChunkId >= numChunks) {
                fetchStatus = FetchStatus.NEXT_PARTITION;
            } else {
                fetchStatus = FetchStatus.SEND_DATA_FILE;
            }
        } else {

            // Index file not done yet, keep sending
            fetchStatus = FetchStatus.SEND_INDEX_FILE;

        }
    }

    private void handleSendDataFile(DataOutputStream outputStream) throws IOException {
        if(null == dataFile && null == indexFile) {

            // First time enter here, create files based on partition and
            // chunk ids
            String fileName = Integer.toString(currentPair.getSecond()) + "_"
                              + Integer.toString(currentPair.getFirst()) + "_"
                              + Integer.toString(currentChunkId);
            this.dataFile = new File(this.storeDir, fileName + ".data");
            this.indexFile = new File(this.storeDir, fileName + ".index");

            // Create a new writer for data file
            this.chunkedFileWriter = new ChunkedFileWriter(dataFile, outputStream);
            logger.info("Streaming " + dataFile.getAbsolutePath());
            this.chunkedFileWriter.writeHeader();

        }

        if(0 == chunkedFileWriter.streamFile()) {

            // We are done with the data file, move to index file
            logger.info("Completed streaming " + dataFile.getAbsolutePath());
            this.chunkedFileWriter.close();
            this.chunkedFileWriter = new ChunkedFileWriter(indexFile, outputStream);
            logger.info("Streaming " + indexFile.getAbsolutePath());
            this.chunkedFileWriter.writeHeader();
            fetchStatus = FetchStatus.SEND_INDEX_FILE;

        } else {

            // Data file not done yet, keep sending
            fetchStatus = FetchStatus.SEND_DATA_FILE;

        }
    }

    private StreamRequestHandlerState handleNextPartition() {
        StreamRequestHandlerState handlerState = StreamRequestHandlerState.WRITING;
        if(partitionIterator.hasNext()) {
            // Start a new partition
            Integer partitionId = partitionIterator.next();
            int nodeId = metadataStore.getNodeId();
            int zoneId = metadataStore.getCluster().getNodeById(nodeId).getZoneId();

            StoreDefinition storeDef = metadataStore.getStoreDef(request.getStoreName());
            StoreRoutingPlan storeRoutingPlan = new StoreRoutingPlan(metadataStore.getCluster(),
                                                                     storeDef);
            int getZoneNary = storeRoutingPlan.getZoneNaryForNodesPartition(zoneId,
                                                                            nodeId,
                                                                            partitionId);

            currentPair = Pair.create(getZoneNary, partitionId);
            currentChunkId = 0;

            // First check if bucket exists
            if(!bucketToNumChunks.containsKey(Pair.create(currentPair.getSecond(),
                                                          currentPair.getFirst()))) {
                throw new VoldemortException("Bucket [ partition = " + currentPair.getSecond()
                                             + ", replica = " + currentPair.getFirst()
                                             + " ] does not exist for store "
                                             + request.getStoreName());
            }
            numChunks = bucketToNumChunks.get(Pair.create(currentPair.getSecond(),
                                                          currentPair.getFirst()));

            dataFile = indexFile = null;
            fetchStatus = FetchStatus.SEND_DATA_FILE;
        } else {

            // We are done since we have gone through the entire
            // partition list
            logger.info("Finished streaming files for partitions tuples " + partitionIds);
            handlerState = StreamRequestHandlerState.COMPLETE;
        }
        return handlerState;
    }

    private class ChunkedFileWriter {

        private final File fileToWrite;
        private final DataOutputStream outStream;
        private final FileChannel dataChannel;
        private final WritableByteChannel outChannel;
        private long currentPos;

        public ChunkedFileWriter(File fileToWrite, DataOutputStream stream)
                                                                           throws FileNotFoundException {
            this.fileToWrite = fileToWrite;
            this.outStream = stream;
            this.dataChannel = new FileInputStream(fileToWrite).getChannel();
            this.outChannel = Channels.newChannel(outStream);
            this.currentPos = 0;
        }

        public void close() throws IOException {
            dataChannel.close();
            if(nioEnabled) {
                outChannel.close();
            }
        }

        public void writeHeader() throws IOException {
            VAdminProto.FileEntry response = VAdminProto.FileEntry.newBuilder()
                                                                  .setFileName(fileToWrite.getName())
                                                                  .setFileSizeBytes(dataChannel.size())
                                                                  .build();

            ProtoUtils.writeMessage(outStream, response);
            throttler.maybeThrottle(response.getSerializedSize());
        }

        /**
         * This function returns the number of bytes left. Zero means everything
         * is done
         */
        public long streamFile() throws IOException {
            long bytesRemaining = dataChannel.size() - currentPos;
            if(0 < bytesRemaining) {
                long bytesToWrite = Math.min(bytesRemaining, blockSize);
                long bytesWritten = dataChannel.transferTo(currentPos, bytesToWrite, outChannel);
                currentPos += bytesWritten;
                logger.debug(bytesWritten + " bytes written");
                throttler.maybeThrottle((int) bytesWritten);
            }
            bytesRemaining = dataChannel.size() - currentPos;
            return bytesRemaining;
        }
    }
}
TOP

Related Classes of voldemort.server.protocol.admin.FetchPartitionFileStreamRequestHandler$ChunkedFileWriter

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.