Package com.netflix.astyanax.thrift

Source Code of com.netflix.astyanax.thrift.ThriftAllRowsQueryImpl

package com.netflix.astyanax.thrift;

import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.cassandra.thrift.ColumnParent;
import org.apache.cassandra.thrift.KeyRange;
import org.apache.cassandra.thrift.KeySlice;
import org.apache.cassandra.thrift.SlicePredicate;
import org.apache.cassandra.thrift.SliceRange;
import org.apache.cassandra.thrift.Cassandra.Client;
import org.apache.cassandra.utils.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import com.netflix.astyanax.CassandraOperationType;
import com.netflix.astyanax.ExceptionCallback;
import com.netflix.astyanax.RowCallback;
import com.netflix.astyanax.connectionpool.ConnectionContext;
import com.netflix.astyanax.connectionpool.Host;
import com.netflix.astyanax.connectionpool.OperationResult;
import com.netflix.astyanax.connectionpool.TokenRange;
import com.netflix.astyanax.connectionpool.exceptions.ConnectionException;
import com.netflix.astyanax.connectionpool.impl.OperationResultImpl;
import com.netflix.astyanax.model.ByteBufferRange;
import com.netflix.astyanax.model.ColumnFamily;
import com.netflix.astyanax.model.ColumnSlice;
import com.netflix.astyanax.model.Rows;
import com.netflix.astyanax.partitioner.Partitioner;
import com.netflix.astyanax.query.AllRowsQuery;
import com.netflix.astyanax.query.CheckpointManager;
import com.netflix.astyanax.shallows.EmptyCheckpointManager;
import com.netflix.astyanax.thrift.model.ThriftRowsSliceImpl;

public class ThriftAllRowsQueryImpl<K, C> implements AllRowsQuery<K, C> {
    private final static Logger LOG = LoggerFactory.getLogger(ThriftAllRowsQueryImpl.class);
   
    private final ThriftColumnFamilyQueryImpl<K,C> query;
    protected SlicePredicate predicate = new SlicePredicate().setSlice_range(ThriftUtils.createAllInclusiveSliceRange());
    protected CheckpointManager checkpointManager = new EmptyCheckpointManager();
   
    protected ColumnFamily<K, C> columnFamily;
    private ExceptionCallback exceptionCallback;
    private int     blockSize       = 100;
    private boolean repeatLastToken = true;
    private Integer nThreads;
    private String  startToken      ;
    private String  endToken        ;
    private Boolean includeEmptyRows;
   
    public ThriftAllRowsQueryImpl(ThriftColumnFamilyQueryImpl<K, C> query) {
        this.columnFamily = query.columnFamily;
        this.query = query;
    }
   
    protected List<org.apache.cassandra.thrift.KeySlice> getNextBlock(final KeyRange range) {
        ThriftKeyspaceImpl keyspace = query.keyspace;
       
        while (true) {
            try {
                return keyspace.connectionPool.executeWithFailover(
                        new AbstractKeyspaceOperationImpl<List<org.apache.cassandra.thrift.KeySlice>>(
                                keyspace.tracerFactory.newTracer(CassandraOperationType.GET_ROWS_RANGE, columnFamily),
                                query.pinnedHost, keyspace.getKeyspaceName()) {
                            @Override
                            public List<org.apache.cassandra.thrift.KeySlice> internalExecute(Client client, ConnectionContext context)
                                    throws Exception {
                               
                                List<KeySlice> slice = client.get_range_slices(
                                        new ColumnParent().setColumn_family(columnFamily.getName()), predicate,
                                        range, ThriftConverter.ToThriftConsistencyLevel(query.consistencyLevel));
                               
                                return slice;
                            }

                            @Override
                            public ByteBuffer getRowKey() {
                                if (range.getStart_key() != null)
                                    return range.start_key;
                                return null;
                            }
                        }, query.retry).getResult();
            }
            catch (ConnectionException e) {
                // Let exception callback handle this exception. If it
                // returns false then
                // we return an empty result which the iterator's
                // hasNext() to return false.
                // If no exception handler is provided then simply
                // return an empty set as if the
                // there is no more data
                if (this.getExceptionCallback() == null) {
                    throw new RuntimeException(e);
                }
                else {
                    if (!this.getExceptionCallback().onException(e)) {
                        return new ArrayList<org.apache.cassandra.thrift.KeySlice>();
                    }
                }
            }
        }
    }

    @Override
    public OperationResult<Rows<K, C>> execute() throws ConnectionException {
        return new OperationResultImpl<Rows<K, C>>(Host.NO_HOST,
                new ThriftAllRowsImpl<K, C>(query.keyspace.getPartitioner(), this, columnFamily), 0);
    }

    @Override
    public ListenableFuture<OperationResult<Rows<K, C>>> executeAsync() throws ConnectionException {
        throw new UnsupportedOperationException("executeAsync not supported here.  Use execute()");
    }

    private boolean shouldIgnoreEmptyRows() {
        if (getIncludeEmptyRows() == null) {
            if (getPredicate().isSetSlice_range() && getPredicate().getSlice_range().getCount() == 0) {
                return false;
            }
        }
        else {
            return !getIncludeEmptyRows();
        }

        return true;
    }

    @Override
    public void executeWithCallback(final RowCallback<K, C> callback) throws ConnectionException {
        final ThriftKeyspaceImpl keyspace = query.keyspace;
        final Partitioner partitioner = keyspace.getPartitioner();
        final AtomicReference<ConnectionException> error = new AtomicReference<ConnectionException>();
        final boolean bIgnoreTombstones = shouldIgnoreEmptyRows();

        List<Pair<String, String>> ranges;
        if (this.getConcurrencyLevel() != null) {
            ranges = Lists.newArrayList();
            int nThreads = this.getConcurrencyLevel();
            List<TokenRange> tokens = partitioner.splitTokenRange(
                    startToken == null ? partitioner.getMinToken() : startToken,
                    endToken == null   ? partitioner.getMaxToken() : endToken,
                    nThreads);
            for (TokenRange range : tokens) {
                try {
                    String currentToken = checkpointManager.getCheckpoint(range.getStartToken());
                    if (currentToken == null) {
                        currentToken = range.getStartToken();
                    }
                    else if (currentToken.equals(range.getEndToken())) {
                        continue;
                    }
                    ranges.add(Pair.create(currentToken, range.getEndToken()));
                } catch (Exception e) {
                    throw ThriftConverter.ToConnectionPoolException(e);
                }
            }
        }
        else {
            ranges = Lists.transform(keyspace.describeRing(true), new Function<TokenRange, Pair<String, String>> () {
                @Override
                public Pair<String, String> apply(TokenRange input) {
                    return Pair.create(input.getStartToken(), input.getEndToken());
                }
            });
        }
        final CountDownLatch doneSignal = new CountDownLatch(ranges.size());

        for (final Pair<String, String> tokenPair : ranges) {
            // Prepare the range of tokens for this token range
            final KeyRange range = new KeyRange()
                    .setCount(getBlockSize())
                    .setStart_token(tokenPair.left)
                    .setEnd_token(tokenPair.right);

            query.executor.submit(new Callable<Void>() {
                private boolean firstBlock = true;
               
                @Override
                public Void call() throws Exception {
                    if (error.get() == null && internalRun()) {
                        query.executor.submit(this);
                    }
                    else {
                        doneSignal.countDown();
                    }
                    return null;
                }

                private boolean internalRun() throws Exception {
                    try {
                        // Get the next block
                        List<KeySlice> ks = keyspace.connectionPool.executeWithFailover(
                                new AbstractKeyspaceOperationImpl<List<KeySlice>>(keyspace.tracerFactory
                                        .newTracer(CassandraOperationType.GET_ROWS_RANGE,
                                                columnFamily), query.pinnedHost, keyspace
                                        .getKeyspaceName()) {
                                    @Override
                                    public List<KeySlice> internalExecute(Client client, ConnectionContext context)
                                            throws Exception {
                                        return client.get_range_slices(new ColumnParent()
                                                .setColumn_family(columnFamily.getName()),
                                                predicate, range, ThriftConverter
                                                        .ToThriftConsistencyLevel(query.consistencyLevel));
                                    }

                                    @Override
                                    public ByteBuffer getRowKey() {
                                        if (range.getStart_key() != null)
                                            return ByteBuffer.wrap(range.getStart_key());
                                        return null;
                                    }
                                }, query.retry.duplicate()).getResult();

                        // Notify the callback
                        if (!ks.isEmpty()) {
                            KeySlice lastRow = Iterables.getLast(ks);
                            boolean bContinue = (ks.size() == getBlockSize());

                            if (getRepeatLastToken()) {
                                if (firstBlock) {
                                    firstBlock = false;
                                }
                                else {
                                    ks.remove(0);
                                }
                            }
                           
                            if (bIgnoreTombstones) {
                                Iterator<KeySlice> iter = ks.iterator();
                                while (iter.hasNext()) {
                                    if (iter.next().getColumnsSize() == 0)
                                        iter.remove();
                                }
                            }
                            Rows<K, C> rows = new ThriftRowsSliceImpl<K, C>(ks, columnFamily
                                    .getKeySerializer(), columnFamily.getColumnSerializer());
                            try {
                                callback.success(rows);
                            }
                            catch (Throwable t) {
                                ConnectionException ce = ThriftConverter.ToConnectionPoolException(t);
                                error.set(ce);
                                return false;
                            }
                           
                            if (bContinue) {
                                // Determine the start token for the next page
                                String token = partitioner.getTokenForKey(lastRow.bufferForKey()).toString();
                                checkpointManager.trackCheckpoint(tokenPair.left, token);
                                if (getRepeatLastToken()) {
                                    range.setStart_token(partitioner.getTokenMinusOne(token));
                                }
                                else {
                                    range.setStart_token(token);
                                }
                            }
                            else {
                                checkpointManager.trackCheckpoint(tokenPair.left, tokenPair.right);
                                return false;
                            }
                        }
                        else {
                            checkpointManager.trackCheckpoint(tokenPair.left, tokenPair.right);
                            return false;
                        }
                    }
                    catch (Exception e) {
                        ConnectionException ce = ThriftConverter.ToConnectionPoolException(e);
                        if (!callback.failure(ce)) {
                            error.set(ce);
                            return false;
                        }
                    }

                    return true;
                }
            });
        }
        // Block until all threads finish
        try {
            doneSignal.await();
        }
        catch (InterruptedException e) {
            LOG.debug("Execution interrupted on get all rows for keyspace " + keyspace.getKeyspaceName());
        }

        if (error.get() != null) {
            throw error.get();
        }
    }
   
    public AllRowsQuery<K, C> setExceptionCallback(ExceptionCallback cb) {
        exceptionCallback = cb;
        return this;
    }

    protected ExceptionCallback getExceptionCallback() {
        return this.exceptionCallback;
    }

    @Override
    public AllRowsQuery<K, C> setThreadCount(int numberOfThreads) {
        setConcurrencyLevel(numberOfThreads);
        return this;
    }
   
    @Override
    public AllRowsQuery<K, C> setConcurrencyLevel(int numberOfThreads) {
        this.nThreads = numberOfThreads;
        return this;
    }


    @Override
    public AllRowsQuery<K, C> setCheckpointManager(CheckpointManager manager) {
        this.checkpointManager = manager;
        return this;
    }

    @Override
    public AllRowsQuery<K, C> withColumnSlice(C... columns) {
        if (columns != null)
            predicate.setColumn_names(columnFamily.getColumnSerializer().toBytesList(Arrays.asList(columns)))
                    .setSlice_rangeIsSet(false);
        return this;
    }

    @Override
    public AllRowsQuery<K, C> withColumnSlice(Collection<C> columns) {
        if (columns != null)
            predicate.setColumn_names(columnFamily.getColumnSerializer().toBytesList(columns)).setSlice_rangeIsSet(
                    false);
        return this;
    }

    @Override
    public AllRowsQuery<K, C> withColumnRange(C startColumn, C endColumn, boolean reversed, int count) {
        predicate.setSlice_range(ThriftUtils.createSliceRange(columnFamily.getColumnSerializer(), startColumn,
                endColumn, reversed, count));
        return this;
    }

    @Override
    public AllRowsQuery<K, C> withColumnRange(ByteBuffer startColumn, ByteBuffer endColumn, boolean reversed, int count) {
        predicate.setSlice_range(new SliceRange(startColumn, endColumn, reversed, count));
        return this;
    }

    @Override
    public AllRowsQuery<K, C> withColumnSlice(ColumnSlice<C> slice) {
        if (slice.getColumns() != null) {
            predicate.setColumn_names(columnFamily.getColumnSerializer().toBytesList(slice.getColumns()))
                    .setSlice_rangeIsSet(false);
        }
        else {
            predicate.setSlice_range(ThriftUtils.createSliceRange(columnFamily.getColumnSerializer(),
                    slice.getStartColumn(), slice.getEndColumn(), slice.getReversed(), slice.getLimit()));
        }
        return this;
    }

    @Override
    public AllRowsQuery<K, C> withColumnRange(ByteBufferRange range) {
        predicate.setSlice_range(new SliceRange().setStart(range.getStart()).setFinish(range.getEnd())
                .setCount(range.getLimit()).setReversed(range.isReversed()));
        return this;
    }

    @Override
    public AllRowsQuery<K, C> setBlockSize(int blockSize) {
        return setRowLimit(blockSize);
    }

    @Override
    public AllRowsQuery<K, C> setRowLimit(int rowLimit) {
        this.blockSize = rowLimit;
        return this;
    }

    public int getBlockSize() {
        return blockSize;
    }

    @Override
    public AllRowsQuery<K, C> setRepeatLastToken(boolean repeatLastToken) {
        this.repeatLastToken = repeatLastToken;
        return this;
    }

    public boolean getRepeatLastToken() {
        return this.repeatLastToken;
    }

    protected Integer getConcurrencyLevel() {
        return this.nThreads;
    }
   
    public AllRowsQuery<K, C> setIncludeEmptyRows(boolean flag) {
        this.includeEmptyRows = flag;
        return this;
    }

    public String getStartToken() {
        return this.startToken;
    }
   
    public String getEndToken() {
        return this.endToken;
    }
   
    @Override
    public AllRowsQuery<K, C> forTokenRange(BigInteger startToken, BigInteger endToken) {
        return forTokenRange(startToken.toString(), endToken.toString());
    }
   
    public AllRowsQuery<K, C> forTokenRange(String startToken, String endToken) {
        this.startToken = startToken;
        this.endToken = endToken;
        return this;
    }
   
    SlicePredicate getPredicate() {
        return predicate;
    }
   
    Boolean getIncludeEmptyRows() {
        return this.includeEmptyRows;
    }
}
TOP

Related Classes of com.netflix.astyanax.thrift.ThriftAllRowsQueryImpl

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.