com.rackspacecloud.blueflood.io.datastax.DAbstractMetricIO.java Source code

Java tutorial

Introduction

Here is the source code for com.rackspacecloud.blueflood.io.datastax.DAbstractMetricIO.java

Source

/*
 * Copyright (c) 2016 Rackspace.
 *
 * Licensed 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.rackspacecloud.blueflood.io.datastax;

import com.datastax.driver.core.*;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.rackspacecloud.blueflood.io.CassandraModel;
import com.rackspacecloud.blueflood.io.Instrumentation;
import com.rackspacecloud.blueflood.rollup.Granularity;
import com.rackspacecloud.blueflood.types.*;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.Long;
import java.nio.ByteBuffer;
import java.util.*;

/**
 * This is an abstract class that collects the common behavior of
 * reading/writing preaggregated & basic numeric metrics objects.
 */
public abstract class DAbstractMetricIO {

    private static final Logger LOG = LoggerFactory.getLogger(DAbstractMetricIO.class);

    protected Session session;

    protected final DMetricsCFPreparedStatements metricsCFPreparedStatements;

    protected DAbstractMetricIO() {
        metricsCFPreparedStatements = DMetricsCFPreparedStatements.getInstance();
        session = DatastaxIO.getSession();
    }

    /**
     * Asynchronously insert a rolled up metric to the appropriate column family
     * for a particular granularity
     *
     * @param locator
     * @param collectionTime
     * @param rollup
     * @param granularity
     * @return
     */
    public ResultSetFuture putAsync(Locator locator, long collectionTime, Rollup rollup, Granularity granularity,
            int ttl) {

        Session session = DatastaxIO.getSession();

        // we use batch statement here in case sub classes
        // override the addRollupToBatch() and provide
        // multiple statements
        BatchStatement batch = new BatchStatement();
        addRollupToBatch(batch, locator, rollup, collectionTime, granularity, ttl);

        Collection<Statement> statements = batch.getStatements();
        if (statements.size() == 1) {
            Statement oneStatement = statements.iterator().next();
            return session.executeAsync(oneStatement);
        } else {
            LOG.debug(String.format("Using BatchStatement for %d statements", statements.size()));
            return session.executeAsync(batch);
        }
    }

    public Statement createStatement(Locator locator, long collectionTime, Rollup rollup, Granularity granularity,
            int ttl) {
        final PreparedStatement statement;

        if (rollup.getRollupType() == RollupType.BF_BASIC) {

            // Strings and Booleans don't get rolled up.  I'd like to verify
            // that none are passed in, but that would require a db access

            statement = metricsCFPreparedStatements.basicGranToInsertStatement.get(granularity);
        } else {
            statement = metricsCFPreparedStatements.preaggrGranToInsertStatement.get(granularity);
        }

        BoundStatement bound = statement.bind(locator.toString(), collectionTime, toByteBuffer(rollup), ttl);

        return bound;
    }

    /**
     * Fetch rollup objects for a {@link com.rackspacecloud.blueflood.types.Locator}
     * from the specified column family and range.
     *
     * @param locator
     * @param columnFamily
     * @param range
     * @return
     */
    protected <T extends Object> Table<Locator, Long, T> getRollupsForLocator(final Locator locator,
            String columnFamily, Range range) {
        return getValuesForLocators(new ArrayList<Locator>() {
            {
                add(locator);
            }
        }, columnFamily, range);
    }

    /**
     * Fetch values for a list of {@link com.rackspacecloud.blueflood.types.Locator}
     * from the specified column family and range.
     *
     * This is a base behavior for most rollup types. IO subclasses can override
     * this behavior as they see fit.
     *
     * @param locators
     * @param columnFamily
     * @param range
     * @return
     */
    protected <T extends Object> Table<Locator, Long, T> getValuesForLocators(final List<Locator> locators,
            String columnFamily, Range range) {

        Table<Locator, Long, T> locatorTimestampRollup = HashBasedTable.create();

        Map<Locator, List<ResultSetFuture>> resultSetFuturesMap = selectForLocatorListAndRange(columnFamily,
                locators, range);

        for (Map.Entry<Locator, List<ResultSetFuture>> entry : resultSetFuturesMap.entrySet()) {
            Locator locator = entry.getKey();
            List<ResultSetFuture> futures = entry.getValue();

            Table<Locator, Long, T> result = toLocatorTimestampValue(futures, locator, columnFamily, range);
            locatorTimestampRollup.putAll(result);
        }
        return locatorTimestampRollup;
    }

    /**
     * Provides a way for the sub class to get a {@link java.nio.ByteBuffer}
     * representation of a certain value.
     *
     * @param value
     * @return
     */
    protected abstract <T extends Object> ByteBuffer toByteBuffer(T value);

    /**
     * Provides a way for the sub class to construct the right Rollup
     * object from a {@link java.nio.ByteBuffer}
     *
     * @param byteBuffer
     * @return
     */
    protected abstract <T extends Object> T fromByteBuffer(ByteBuffer byteBuffer);

    /**
     * Add a {@link com.datastax.driver.core.PreparedStatement} statement to the
     * {@link com.datastax.driver.core.BatchStatement} to insert this Rollup
     * object to metrics_preaggregated_{granularity} column family
     *
     * @param batch
     * @param locator
     * @param collectionTime
     * @param granularity
     * @param ttl
     */
    protected void addRollupToBatch(BatchStatement batch, Locator locator, Rollup rollup, long collectionTime,
            Granularity granularity, int ttl) {
        Statement statement = createStatement(locator, collectionTime, rollup, granularity, ttl);
        batch.add(statement);
    }

    /**
     * Asynchronously execute select statements against the specified
     * column family for a specific list of
     * {@link com.rackspacecloud.blueflood.types.Locator} and
     * {@link com.rackspacecloud.blueflood.types.Range}
     *
     * @param locators
     * @param range
     * @return a map of Locator -> a list of ResultSetFuture
     */
    protected Map<Locator, List<ResultSetFuture>> selectForLocatorListAndRange(String columnFamilyName,
            List<Locator> locators, Range range) {
        Map<Locator, List<ResultSetFuture>> locatorFuturesMap = new HashMap<Locator, List<ResultSetFuture>>();
        for (Locator locator : locators) {
            List<ResultSetFuture> existing = locatorFuturesMap.get(locator);
            if (existing == null) {
                existing = new ArrayList<ResultSetFuture>();
                locatorFuturesMap.put(locator, existing);
            }
            existing.addAll(selectForLocatorAndRange(columnFamilyName, locator, range));
        }
        return locatorFuturesMap;
    }

    /**
     * Execute a select statement against the specified column family for a specific
     * {@link com.rackspacecloud.blueflood.types.Locator} and
     * {@link com.rackspacecloud.blueflood.types.Range}
     *
     * @param columnFamily the column family name to do select against
     * @param locator
     * @param range
        
     * @return
     */
    protected List<ResultSetFuture> selectForLocatorAndRange(String columnFamily, Locator locator, Range range) {
        List<ResultSetFuture> resultsFutures = new ArrayList<ResultSetFuture>();
        PreparedStatement statement = metricsCFPreparedStatements.cfNameToSelectStatement.get(columnFamily);
        resultsFutures
                .add(session.executeAsync(statement.bind(locator.toString(), range.getStart(), range.getStop())));
        return resultsFutures;
    }

    /**
     *  Give a {@link com.datastax.driver.core.ResultSetFuture}, get
     *  the corresponding data from it and return it as a
     *  Table of locator, long and rollup.
     */
    public <T extends Object> Table<Locator, Long, T> toLocatorTimestampValue(List<ResultSetFuture> futures,
            Locator locator, String columnFamily, Range range) {
        Table<Locator, Long, T> locatorTimestampRollup = HashBasedTable.create();
        for (ResultSetFuture future : futures) {
            try {
                List<Row> rows = future.getUninterruptibly().all();

                // we only want to count the number of points we
                // get when we're querying the metrics_full
                if (StringUtils.isNotEmpty(columnFamily)
                        && columnFamily.equals(CassandraModel.CF_METRICS_FULL_NAME)) {
                    Instrumentation.getRawPointsIn5MinHistogram().update(rows.size());
                }

                for (Row row : rows) {
                    String key = row.getString(DMetricsCFPreparedStatements.KEY);
                    Locator loc = Locator.createLocatorFromDbKey(key);
                    Long hash = row.getLong(DMetricsCFPreparedStatements.COLUMN1);
                    locatorTimestampRollup.put(loc, hash,
                            (T) fromByteBuffer(row.getBytes(DMetricsCFPreparedStatements.VALUE)));
                }
            } catch (Exception ex) {
                Instrumentation.markReadError();
                LOG.error(String.format("error reading metric for locator %s, column family '%s', range %s",
                        locator, columnFamily, range.toString()), ex);
            }
        }
        return locatorTimestampRollup;
    }

    /**
     * Retrieves the {@link BoundStatement} for a particular metric and granularity.
     * Subclasses will implement this.
     *
     * @param metric
     * @param granularity
     * @return
     */
    protected abstract BoundStatement getBoundStatementForMetric(IMetric metric, Granularity granularity);
}