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

com.bigdata.bop.fed.NIOChunkMessage Maven / Gradle / Ivy

/**

Copyright (C) SYSTAP, LLC DBA Blazegraph 2006-2016.  All rights reserved.

Contact:
     SYSTAP, LLC DBA Blazegraph
     2501 Calvert ST NW #106
     Washington, DC 20008
     [email protected]

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 of the License.

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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
/*
 * Created on Sep 10, 2010
 */

package com.bigdata.bop.fed;

import java.io.Externalizable;
import java.io.Serializable;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.rmi.RemoteException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;

import com.bigdata.bop.engine.IChunkAccessor;
import com.bigdata.bop.engine.IChunkMessage;
import com.bigdata.bop.engine.IQueryClient;
import com.bigdata.io.DirectBufferPoolAllocator;
import com.bigdata.io.DirectBufferPoolAllocator.IAllocation;
import com.bigdata.io.DirectBufferPoolAllocator.IAllocationContext;
import com.bigdata.io.SerializerUtil;
import com.bigdata.rdf.internal.encoder.IVSolutionSetEncoder;
import com.bigdata.service.ManagedResourceService;
import com.bigdata.service.ResourceService;

import cutthecrap.utils.striterators.ICloseableIterator;

/**
 * An {@link IChunkMessage} where the payload is made available to the receiving
 * service using an NIO transfer against the sender's {@link ResourceService}.
 * This is suitable for moving large blocks of data during query evaluation.
 * 
 * @author Bryan Thompson
 * @version $Id$
 * 
 * @see 
 *      ResourceService should use NIO for file and buffer transfers
 * 
 * @see Support
 *      NIO solution set interchange on the cluster
 * 
 *      TODO Implement {@link Externalizable} for this class based on the
 *      {@link ThickChunkMessage} and {@link IVSolutionSetEncoder}.
 */
public class NIOChunkMessage implements IChunkMessage, Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    final private IQueryClient queryController;
    
    final private UUID queryControllerId;

    final private UUID queryId;

    final private int bopId;

    final private int partitionId;

    final private int solutionCount;
    
    final private int nbytes;
    
    /**
     * Note: Even when we send one message per chunk, we can still have a list
     * of {@link IAllocation}s if the chunk did not get formatted onto a single
     * {@link IAllocation}.
     */
    final private A[] allocations;

    /**
     * The Internet address and port where the receiver can fetch the payload
     * using the sender's {@link ResourceService}.
     */
    final private InetSocketAddress addr;

    @Override
    public IQueryClient getQueryController() {
        return queryController;
    }

    @Override
    public UUID getQueryControllerId() {
        return queryControllerId;
    }
    
    @Override
    public UUID getQueryId() {
        return queryId;
    }

    @Override
    public int getBOpId() {
        return bopId;
    }

    @Override
    public int getPartitionId() {
        return partitionId;
    }

    @Override
    public boolean isLastInvocation() {
        return false; // Never.
    }

    /**
     * The #of elements in this chunk.
     * 
     * @todo we could track this in total and in {@link A} on a per-slice basis.
     */
    @Override
    public int getSolutionCount() {
        return solutionCount;
    }
    
    /** The #of bytes of data which are available for that operator. */
    public int getBytesAvailable() {
        return nbytes;
    }

    /**
     * The Internet address and port of a {@link ResourceService} from which the
     * receiver may demand the data.
     */
    public InetSocketAddress getServiceAddr() {
        return addr;
    }

    @Override
    public String toString() {

        return getClass().getName() + "{queryId=" + queryId + ",bopId=" + bopId
                + ",partitionId=" + partitionId + ", controller="
                + queryController + ",solutionCount=" + solutionCount
                + ", bytesAvailable=" + nbytes + ", nslices="
                + allocations.length + ", serviceAddr=" + addr + "}";

    }

    /**
     * 
     * @param queryController
     * @param queryId
     * @param sinkId
     * @param partitionId
     * @param allocations
     *            The ordered list of {@link IAllocation}s comprising the chunk.
     * @param addr
     *            The Internet address and port where the receiver can fetch the
     *            payload using the sender's {@link ResourceService}.
     */
    public NIOChunkMessage(final IQueryClient queryController,
            final UUID queryId, final int sinkId, final int partitionId,
            final IAllocationContext allocationContext,
            final E[] source,
            final InetSocketAddress addr) {

        if (queryController == null)
            throw new IllegalArgumentException();

        if (queryId == null)
            throw new IllegalArgumentException();

        if (allocationContext == null)
            throw new IllegalArgumentException();

        if (source == null)
            throw new IllegalArgumentException();

        if (addr == null)
            throw new IllegalArgumentException();

        // format onto NIO buffers.
        final AtomicInteger nsolutions = new AtomicInteger();
        final List allocations = moveToNIOBuffers(
                allocationContext, source, nsolutions);

        this.queryController = queryController;
        try {
            this.queryControllerId = queryController.getServiceUUID();
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
        this.queryId = queryId;
        this.bopId = sinkId;
        this.partitionId = partitionId;
        final int n = allocations.size();
        this.allocations = new A[n];
        int i = 0;
        int nbytes = 0;
        final Iterator itr = allocations.iterator();
        while (itr.hasNext()) {
            final IAllocation alloc = itr.next();
            final int len = alloc.getSlice().capacity();
            this.allocations[i++] = new A(alloc.getId(), len);
            nbytes += len;
        }
        this.solutionCount = nsolutions.get();
        this.nbytes = nbytes;
        this.addr = addr;

    }

    /**
     * Chunk-wise serialization of the data onto allocations.
     * 
     * @param allocationContext
     * @param source
     * @return
     */
    static private  List moveToNIOBuffers(
            final IAllocationContext allocationContext,
            final E[] source,
            final AtomicInteger nsolutions) {

        int nbytes = 0;

        int n = 0;

        final List allocations = new LinkedList();

        // Next chunk to be serialized.
        final E[] chunk = source;// itr.next();

        // track #of solutions.
        n += chunk.length;

        // FIXME Replace with FAST/TIGHT SERIALIZATION
        // serialize the chunk of binding sets.
        final byte[] data = SerializerUtil.serialize(chunk);

        // track size of the allocations.
        nbytes += data.length;

        // allocate enough space for those data.
        final IAllocation[] tmp;
        try {
            tmp = allocationContext.alloc(data.length);
        } catch (InterruptedException ex) {
            throw new RuntimeException(ex);
        }

        // copy the data into the allocations.
        DirectBufferPoolAllocator.put(data, tmp);

        for (IAllocation a : tmp) {

            // prepare for reading.
            a.getSlice().flip();

            // append the allocation.
            allocations.add(a);

        }

        nsolutions.addAndGet(n);

        return allocations;

    }

    /**
     * Metadata about an allocation to be retrieved from the sender's
     * {@link ResourceService}.
     */
    private static final class A implements Serializable {

        /**
         * 
         */
        private static final long serialVersionUID = 1L;

        /**
         * The identifier of the resource on the sender's
         * {@link ResourceService}.
         */
        private final UUID bufferId;

        /**
         * The size of that resource in bytes.
         */
        private final int nbytes;

        /**
         * 
         * @param bufferId
         *            The identifier of the resource on the sender's
         *            {@link ResourceService}.
         * @param nbytes
         *            The size of that resource in bytes.
         */
        public A(final UUID bufferId, final int nbytes) {

            if (bufferId == null)
                throw new IllegalArgumentException();

            if (nbytes <= 0)
                throw new IllegalArgumentException();
            
            this.bufferId = bufferId;
            
            this.nbytes = nbytes;
            
        }
        
    }
    
    @Override
    public boolean isMaterialized() {

        return materialized != null;
        
    }
    
    private volatile List materialized = null;

    @Override
    public void materialize(final FederatedRunningQuery runningQuery) {

        final AllocationContextKey key = new ShardContext(queryId, bopId,
                partitionId);

        final IAllocationContext allocationContext = runningQuery
                .getAllocationContext(key);

        final ManagedResourceService resourceService = runningQuery
                .getQueryEngine().getResourceService();

        materialize(resourceService, allocationContext);
        
    }
    
    /**
     * Discard the materialized data.
     */
    @Override
    public void release() {

        if (chunkAccessor != null) {
         
            chunkAccessor.close();
            
        }
        
        final List tmp = materialized;

        if (tmp != null) {

            boolean interrupted = false;
            for (IAllocation a : tmp) {
                while (true) {
                    try {
                        a.release();
                        break;
                    } catch (InterruptedException e) {
                        interrupted = true;
                    }
                }
            }
            materialized = null;
        
            if(interrupted) {
                Thread.currentThread().interrupt();
            }
            
        }
        
    }

    /**
     * Core implementation. This is responsible receiving the data from the
     * remote {@link ResourceService} and assembling it into a set of
     * {@link IAllocation}s in the specified {@link IAllocationContext}.
     * 
     * @param resourceService
     * @param allocationContext
     * 
     * @todo The {@link ResourceService} does not currently use NIO and there is
     *       no benefit to passing in direct {@link ByteBuffer}s yet.
     */
    synchronized protected void materialize(
            final ManagedResourceService resourceService,
            final IAllocationContext allocationContext) {

        if (materialized != null)
            return;
        
        try {
            // list of the allocations created to receive the data.
            final List received = new LinkedList();

            for (A a : allocations) {

                final ByteBuffer buf = ByteBuffer.allocate(a.nbytes);

                new ResourceService.ReadBufferTask(addr, a.bufferId, buf)
                        .call();

                final IAllocation[] tmp = allocationContext.alloc(a.nbytes);

                DirectBufferPoolAllocator.put(buf, tmp);

                for (IAllocation alloc : tmp) {

                    // prepare for reading.
                    alloc.getSlice().flip();

                    // add to list of received slices.
                    received.add(alloc);

                }

            }

            materialized = received;

        } catch (Exception ex) {

            throw new RuntimeException(ex);
            
        }

    }

    @Override
    public IChunkAccessor getChunkAccessor() {

        if (chunkAccessor == null) {

            chunkAccessor = new ChunkAccessor();
            
        }

        return chunkAccessor;
    
    }
    
    private volatile transient ChunkAccessor chunkAccessor = null;

    /**
     * FIXME Provide in place decompression and read out of the binding sets.
     * This should be factored out into classes similar to IRaba and IRabaCoder.
     * This stuff should be generic so it can handle elements and binding sets
     * and bats, but there should be specific coders for handling binding sets
     * which leverages the known set of variables in play as of the operator
     * which generated those intermediate results.
     * 

* Note: Some similar work was done to improve the htree performance. *

* Note: Very small chunks (1-5 solutions) are common on a cluster and might * be optimized different than modest chunks (10-100+). * * @see HTree * performance tuning */ private class ChunkAccessor implements IChunkAccessor { private final ICloseableIterator source; public ChunkAccessor() { final List tmp = materialized; if (tmp == null) throw new UnsupportedOperationException(); source = new DeserializationIterator(materialized.iterator()); } public ICloseableIterator iterator() { return source; } public void close() { source.close(); } } private class DeserializationIterator implements ICloseableIterator { private final Iterator src; private volatile boolean open = true; public DeserializationIterator(final Iterator src) { this.src = src; } @Override public void close() { if(open) { open = false; // TODO Anything to discard? } } @Override public boolean hasNext() { if(open && src.hasNext()) return true; close(); return false; } @Override @SuppressWarnings("unchecked") public E[] next() { if (!hasNext()) throw new NoSuchElementException(); /* * Note: Deserialization from a direct ByteBuffer is very expensive. * First copy the data into a byte[] and then deserialize it. */ final IAllocation a = src.next(); // independent position, limit, etc. to avoid side effects final ByteBuffer b = a.getSlice().asReadOnlyBuffer(); final byte[] c = new byte[b.remaining()]; b.get(c); return (E[]) SerializerUtil.deserialize(c); } @Override public void remove() { throw new UnsupportedOperationException(); } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy