Package com.datastax.brisk

Source Code of com.datastax.brisk.BriskServer

/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.datastax.brisk;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.*;
import java.util.concurrent.TimeoutException;

import org.apache.cassandra.config.CFMetaData;
import org.apache.cassandra.config.DatabaseDescriptor;
import org.apache.cassandra.config.Config.DiskAccessMode;
import org.apache.cassandra.db.*;
import org.apache.cassandra.db.filter.QueryPath;
import org.apache.cassandra.db.marshal.AbstractType;
import org.apache.cassandra.dht.Token;
import org.apache.cassandra.hadoop.trackers.CassandraJobConf;
import org.apache.cassandra.hadoop.trackers.TrackerInitializer;
import org.apache.cassandra.hadoop.trackers.TrackerManager;
import org.apache.cassandra.hadoop.trackers.TrackerManagerException;
import org.apache.cassandra.io.sstable.Component;
import org.apache.cassandra.io.sstable.IndexHelper;
import org.apache.cassandra.io.sstable.SSTableReader;
import org.apache.cassandra.io.sstable.SSTableReader.Operator;
import org.apache.cassandra.io.util.*;
import org.apache.cassandra.service.StorageProxy;
import org.apache.cassandra.service.StorageService;
import org.apache.cassandra.thrift.*;
import org.apache.cassandra.utils.ByteBufferUtil;
import org.apache.cassandra.utils.FBUtilities;
import org.apache.cassandra.utils.Filter;
import org.apache.log4j.Logger;
import org.apache.thrift.TException;

import static com.datastax.brisk.BriskDBUtil.validateAndGetColumn;

public class BriskServer extends CassandraServer implements Brisk.Iface
{
   
    static final Logger     logger         = Logger.getLogger(BriskServer.class);
   
    static final ByteBuffer dataCol        = ByteBufferUtil.bytes("data");

    static final String     cfsKeyspace    = "cfs";
   
    // CFs for regular storage
    static final String     cfsInodeDefaultFamily = "inode";
    static final String     cfsSubBlockDefaultFamily = "sblocks";

    static final QueryPath    inodeDefaultQueryPath =  new QueryPath(cfsInodeDefaultFamily, null, dataCol);
    static final ColumnParent subBlockDefaultDataPath= new ColumnParent(cfsSubBlockDefaultFamily);

    // CFs for archive kind of storage
    private static final String         cfsInodeArchiveFamily       = "inode_archive";
    private static final String         cfsSubBlockArchiveFamily       = "sblocks_archive";

    static final QueryPath    inodeArchiveQueryPath =  new QueryPath(cfsInodeArchiveFamily, null, dataCol);
    static final ColumnParent subBlockArchiveDataPath= new ColumnParent(cfsSubBlockArchiveFamily);


  @Override
  public LocalOrRemoteBlock get_cfs_sblock(String callerHostName, ByteBuffer blockId, ByteBuffer sblockId, int offset,
      StorageType storageType) throws InvalidRequestException, UnavailableException, TimedOutException, NotFoundException,
      TException {
   
    if (storageType == StorageType.CFS_REGULAR)
    {
      return get_cfs_sblock(callerHostName, cfsSubBlockDefaultFamily, blockId, sblockId, offset, subBlockDefaultDataPath);
    } else
    {
      return get_cfs_sblock(callerHostName, cfsSubBlockArchiveFamily, blockId, sblockId, offset, subBlockArchiveDataPath);
    }
   
  }

    private LocalOrRemoteBlock get_cfs_sblock(String callerHostName, String subBlockCFName, ByteBuffer blockId,
        ByteBuffer sblockId, int offset, ColumnParent subBlockDataPath) throws TException, TimedOutException, UnavailableException, InvalidRequestException, NotFoundException
    {

        // This logic is only used on mmap spec machines
        if (DatabaseDescriptor.getDiskAccessMode() == DiskAccessMode.mmap)
        {
            if(logger.isDebugEnabled())
                logger.debug("Checking for local block: "+blockId+" from "+callerHostName+" on "+FBUtilities.getLocalAddress().getHostName() );
           
            List<String> hosts = getKeyLocations(blockId);
            boolean isLocal = false;
           
            for (String hostName : hosts)
            {
                if(logger.isDebugEnabled())
                    logger.debug("Block " + blockId + " lives on " + hostName);
               
                if (hostName.equals(callerHostName) && hostName.equals(FBUtilities.getLocalAddress().getHostName()))
                {
                    isLocal = true;           
                   
                    break;
                }
            }
           
            if(isLocal)
            {
                if(logger.isDebugEnabled())
                    logger.debug("Local block should be on this node "+blockId);
               
                LocalBlock localBlock = getLocalSubBlock(subBlockCFName, blockId, sblockId, offset);
               
                if(localBlock != null)
                {
                    if(logger.isDebugEnabled())
                        logger.debug("Local block found: "+localBlock);
                   
                    return new LocalOrRemoteBlock().setLocal_block(localBlock);
                }
            }      
        }
       
        if(logger.isDebugEnabled())
            logger.debug("Checking for remote block: "+blockId);
      
        //Fallback to storageProxy
        return getRemoteSubBlock(blockId, sblockId, offset, subBlockDataPath);
       
    }
   

  public List<List<String>> describe_keys(String keyspace, List<ByteBuffer> keys) throws TException
    {
        List<List<String>> keyEndpoints = new ArrayList<List<String>>(keys.size());

        for (ByteBuffer key : keys)
        {
            keyEndpoints.add(getKeyLocations(key));
        }

        return keyEndpoints;
    }

    private List<String> getKeyLocations(ByteBuffer key)
    {
        List<InetAddress> endpoints = StorageService.instance.getLiveNaturalEndpoints(cfsKeyspace, key);
        DatabaseDescriptor.getEndpointSnitch().sortByProximity(FBUtilities.getLocalAddress(), endpoints);

        List<String> hosts = new ArrayList<String>(endpoints.size());

        for (InetAddress endpoint : endpoints)
        {
            hosts.add(endpoint.getHostName());
        }

        return hosts;
    }

    /**
     * Retrieves a local subBlock
     *
     * @param blockId row key
     * @param sblockId SubBlock column name
     * @param offset inside the sblock
     * @return a local sublock
     * @throws TException
     */
    private LocalBlock getLocalSubBlock(String subBlockCFName, ByteBuffer blockId, ByteBuffer sblockId, int offset) throws TException
    {
        DecoratedKey<Token<?>> decoratedKey = new DecoratedKey<Token<?>>(StorageService.getPartitioner().getToken(blockId), blockId);

        Table table = Table.open(cfsKeyspace);
        ColumnFamilyStore sblockStore = table.getColumnFamilyStore(subBlockCFName);

        Collection<SSTableReader> sstables = sblockStore.getSSTables();

        for (SSTableReader sstable : sstables)
        {
           
            long position = sstable.getPosition(decoratedKey, Operator.EQ);

            if (position == -1)
                continue;

            String filename = sstable.descriptor.filenameFor(Component.DATA);
            RandomAccessFile raf = null;
            int mappedLength = -1;
            MappedByteBuffer mappedData = null;
            MappedFileDataInput file = null;
            try
            {
                raf = new RandomAccessFile(filename, "r");
                assert position < raf.length();
               
                mappedLength = (raf.length() - position) < Integer.MAX_VALUE ? (int) (raf.length() - position)
                        : Integer.MAX_VALUE;

                mappedData = raf.getChannel().map(FileChannel.MapMode.READ_ONLY, position, mappedLength);

                file = new MappedFileDataInput(mappedData, filename, 0);

                if (file == null)
                    continue;
               
                //Verify key was found in data file
                DecoratedKey keyInDisk = SSTableReader.decodeKey(sstable.partitioner,
                        sstable.descriptor,
                        ByteBufferUtil.readWithShortLength(file));
                assert keyInDisk.equals(decoratedKey) : String.format("%s != %s in %s", keyInDisk, decoratedKey, file.getPath());
               
                long rowSize = SSTableReader.readRowSize(file, sstable.descriptor);

                assert rowSize > 0;
                assert rowSize < mappedLength;

                Filter bf = IndexHelper.defreezeBloomFilter(file, sstable.descriptor.usesOldBloomFilter);
               
                //verify this column in in this version of the row.
                if(!bf.isPresent(sblockId))
                    continue;
               
                List<IndexHelper.IndexInfo> indexList = IndexHelper.deserializeIndex(file);

                // we can stop early if bloom filter says none of the
                // columns actually exist -- but,
                // we can't stop before initializing the cf above, in
                // case there's a relevant tombstone
                ColumnFamilySerializer serializer = ColumnFamily.serializer();
                try
                {
                    ColumnFamily cf = serializer.deserializeFromSSTableNoColumns(ColumnFamily.create(sstable.metadata),
                            file);

                    if (cf.isMarkedForDelete())
                        continue;

                }
                catch (Exception e)
                {
                    e.printStackTrace();
                   
                    throw new IOException(serializer + " failed to deserialize " + sstable.getColumnFamilyName()
                            + " with " + sstable.metadata + " from " + file, e);
                }

               
                Integer sblockLength = null;
               
                if(indexList == null)
                    sblockLength = seekToSubColumn(sstable.metadata, file, sblockId);
                else
                    sblockLength = seekToSubColumn(sstable.metadata, file, sblockId, indexList);
                   
            
                if(sblockLength == null || sblockLength < 0)
                    continue;
               
            
                int bytesReadFromStart = mappedLength - (int)file.bytesRemaining();

                if(logger.isDebugEnabled())
                    logger.debug("BlockLength = "+sblockLength+" Availible "+file.bytesRemaining());
               
                assert offset <= sblockLength : String.format("%d > %d", offset,  sblockLength);

                long dataOffset = position + bytesReadFromStart;
               
                if(file.bytesRemaining() == 0 || sblockLength == 0)
                    continue;
               

                return new LocalBlock(file.getPath(), dataOffset + offset, sblockLength - offset);

            }
            catch (IOException e)
            {
                throw new TException(e);
            }
            finally
            {
                FileUtils.closeQuietly(raf);
            }
        }

       
        return null;
    }
   
    //Called when there are is no row index (meaning small number of columns)
    private Integer seekToSubColumn(CFMetaData metadata, FileDataInput file, ByteBuffer sblockId) throws IOException
    {
        int columns = file.readInt();
        for (int i = 0; i < columns; i++)
        {
            Integer dataLength = isSubBlockFound(metadata, file, sblockId);
           
           
            if(dataLength == null)
                return null;
           
            if(dataLength < 0)
                continue;
           
            return dataLength;
           
        }
       
        return null;
    }

    /**
     * Checks if the current column is the one we are looking for
     * @param metadata
     * @param file
     * @param sblockId
     * @return if > 0 the length to read from current file offset. if -1 not relevent. if null out of bounds
     */
    private Integer isSubBlockFound(CFMetaData metadata, FileDataInput file, ByteBuffer sblockId) throws IOException
    {
        ByteBuffer name = ByteBufferUtil.readWithShortLength(file);
       
        //Stop if we've gone too far (return null)
        if(metadata.comparator.compare(name, sblockId) > 0)
            return null;
       
        // verify column type;
        int b = file.readUnsignedByte();
                 
        // skip ts (since we know block ids are unique)
        long ts = file.readLong();
        int sblockLength = file.readInt();
     
        if(!name.equals(sblockId) || (b & ColumnSerializer.DELETION_MASK) != 0 || (b & ColumnSerializer.EXPIRATION_MASK) != 0)
        {
            FileUtils.skipBytesFully(file, sblockLength);
            return -1;
        }
            
        return sblockLength;                  
    }
   
    private Integer seekToSubColumn(CFMetaData metadata, FileDataInput file, ByteBuffer sblockId, List<IndexHelper.IndexInfo> indexList) throws IOException
    {
        file.readInt(); // column count

        /* get the various column ranges we have to read */
        AbstractType comparator = metadata.comparator;
       
        int index = IndexHelper.indexFor(sblockId, indexList, comparator, false);
        if (index == indexList.size())
            return null;
       
        IndexHelper.IndexInfo indexInfo = indexList.get(index);
        if (comparator.compare(sblockId, indexInfo.firstName) < 0)
            return null;
      
        FileMark mark = file.mark();
      
        FileUtils.skipBytesFully(file, indexInfo.offset);

        while (file.bytesPastMark(mark) < indexInfo.offset + indexInfo.width)
        {           
            Integer dataLength = isSubBlockFound(metadata, file, sblockId);
                      
            if(dataLength == null)
                return null;
           
            if(dataLength < 0)
                continue;
           
            return dataLength;         
        }
       
        return null;
    }

    private LocalOrRemoteBlock getRemoteSubBlock(ByteBuffer blockId, ByteBuffer sblockId, int offset, ColumnParent subBlockDataPath)
      throws TimedOutException, UnavailableException, InvalidRequestException, NotFoundException
    {
        // The column name is the SubBlock id (UUID)
        ReadCommand rc = new SliceByNamesReadCommand(cfsKeyspace, blockId, subBlockDataPath, Arrays.asList(sblockId));

        try
        {
            // CL=ONE as there are NOT multiple versions of the blocks.
            List<Row> rows = StorageProxy.read(Arrays.asList(rc), ConsistencyLevel.ONE);
           
            IColumn col = null;
            try
            {
              col = validateAndGetColumn(rows, sblockId);
            } catch (NotFoundException e)
            {
              // This is a best effort to get the value. Sometimes due to the size of
              // the sublocks, the normal replication may time out leaving a replicate without
              // the piece of data. Hence we re try with higher CL.
              rows = StorageProxy.read(Arrays.asList(rc), ConsistencyLevel.QUORUM);
            }
           
            col = validateAndGetColumn(rows, sblockId);
           
            ByteBuffer value = col.value();
           
            if(value.remaining() < offset)
                throw new InvalidRequestException("Invalid offset for block of size: "+value.remaining());
           
           
            LocalOrRemoteBlock block = new LocalOrRemoteBlock();
            if(offset > 0)
            {
                ByteBuffer offsetBlock = value.duplicate();
                offsetBlock.position(offsetBlock.position()+offset);
                block.setRemote_block(offsetBlock);
            }  
            else
            {
                block.setRemote_block(value);
            }
           
            return block;
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
        catch (TimeoutException e)
        {
            throw new TimedOutException();
        }
    }


    public String get_jobtracker_address() throws NotFoundException, TException
    {
        if(!TrackerInitializer.isTrackerNode)
        {
            throw new NotFoundException();
        }
               
        return CassandraJobConf.getJobTrackerNode().getHostName()+":8012";
    }

  @Override
  public String move_job_tracker(String newJobtracker) throws NotFoundException, TException {
   
    try {
      TrackerManager.insertJobtrackerLocation(InetAddress.getByName(newJobtracker));
    } catch (UnknownHostException e) {
      throw new TException("Unable to set the new Job Tracker lcoation");
    } catch (TrackerManagerException e) {
      throw new TException("Unable to set the new Job Tracker lcoation");
    }
    return "New JobTracker location: " + newJobtracker +
      " set successfully. Please wait and check Brisk web interface at: http://" + newJobtracker + ":50030";
  }

}
TOP

Related Classes of com.datastax.brisk.BriskServer

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.