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

com.bigdata.counters.query.HistoryTable Maven / Gradle / Ivy

package com.bigdata.counters.query;

import java.util.Arrays;

import org.apache.log4j.Logger;

import com.bigdata.counters.HistoryInstrument;
import com.bigdata.counters.ICounter;
import com.bigdata.counters.IHistoryEntry;
import com.bigdata.counters.PeriodEnum;
import com.bigdata.counters.History.SampleIterator;

/**
 * A class representing one or more performance counter histories where those
 * histories have been aligned so that the individual timestamps for the
 * performance counter values in each row are aligned.
 * 
 * @author Bryan Thompson
 * @version $Id$
 * 
 * FIXME provide aggregation either here or in how the counters are selected.
 * The capturing groups should specify the dimensions of aggregation.
 */
public class HistoryTable {

    protected static final Logger log = Logger.getLogger(HistoryTable.class);
    
    /**
     * The selected counters.
     */
    public final ICounter[] a;

    /**
     * Identifies the history to be written for each of the selected
     * counters by its based reporting period.
     */
    public final PeriodEnum basePeriod;

    /**
     * 
     * @param a
     *            The selected counters.
     * @param basePeriod
     *            Identifies the history to be written for each of the
     *            selected counters by its based reporting period.
     */
    public HistoryTable(final ICounter[] a, final PeriodEnum basePeriod) {
        
        if (a == null)
            throw new IllegalArgumentException();

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

        this.a = a;

        this.ncols = a.length;
        
        this.basePeriod = basePeriod;
        
        // The units of the history (first column).
        units = basePeriod.name();

        // The period for those units.
        period = basePeriod.getPeriodMillis();

        if (log.isInfoEnabled())
            log.info("#counters=" + a.length + ", units=" + units
                    + ", period=" + period);
        
        /*
         * Create a snapshot iterator for each counter and find the first and
         * last timestamp across the samples for all histories that will be
         * written into the table.
         */
        long firstTimestamp = Long.MAX_VALUE;
        long lastTimestamp = Long.MIN_VALUE;
        int maxSamplesIndex = -1;
        int firstSampleTimeIndex = -1;
        int lastSampleTimeIndex = -1;
        this.firstLogicalSlot = new long[a.length];
        
        /*
         * An array of snapshot history iterators for the selected units of
         * the specified counters.
         */
        final SampleIterator[] hitrs = new SampleIterator[ncols];

        for (int col = 0; col < ncols; col++) {

            if (a[col] == null)
                throw new IllegalArgumentException();

            if (!(a[col].getInstrument() instanceof HistoryInstrument)) {

                throw new IllegalArgumentException();

            }

            // snapshot iterator for the history for that counter.
            final SampleIterator itr = ((HistoryInstrument) a[col]
                    .getInstrument()).getHistory(/*basePeriod*/).iterator();

            hitrs[col] = itr;

            final int sampleCount = itr.getSampleCount();

            if (sampleCount == 0) {

                // No samples for that counter and this period.
                continue;
                
            }
            
            // the logical slot into which the first sample falls for
            // that history.
            firstLogicalSlot[col] = itr.getFirstSampleTime() / period;

            if (itr.getFirstSampleTime() < firstTimestamp) {

                // update the earliest timestamp for the histories.
                firstTimestamp = itr.getFirstSampleTime();

                // update the index of the history with the earliest sample.
                firstSampleTimeIndex = col;

            }

            if (itr.getLastSampleTime() > lastTimestamp) {

                // update the latest timestamp for the histories.
                lastTimestamp = itr.getLastSampleTime();

                // update the index of the history with the latest sample.
                lastSampleTimeIndex = col;

            }

            if (maxSamplesIndex == -1
                    || sampleCount > hitrs[maxSamplesIndex]
                            .getSampleCount()) {

                // update the index of the history with the most samples.
                maxSamplesIndex = col;

            }

        }

        if (maxSamplesIndex != -1) {

            /*
             * There is some data for the table.
             */
            
            assert firstSampleTimeIndex != -1;
            assert lastSampleTimeIndex != -1;

            // the maximum #of samples.
            this.maxSamples = hitrs[maxSamplesIndex].getSampleCount();
            this.maxSamplesIndex = maxSamplesIndex;

            this.firstTimestamp = firstTimestamp;
            this.lastTimestamp = lastTimestamp;
            this.firstSampleTimeIndex = firstSampleTimeIndex;
            this.lastSampleTimeIndex = lastSampleTimeIndex;
            this.logicalSlotOffset = firstLogicalSlot[firstSampleTimeIndex];

            /*
             * Figure out how many rows we need to display. This can be more
             * than the #of samples. It is in fact the max( sampleCount +
             * the offset of the first row) for each counters.
             */
            int nrows = -1;
            for (int col = 0; col < ncols; col++) {

                final int x = getFirstRowIndex(col)
                        + hitrs[col].getSampleCount();

                if (x > nrows) {

                    nrows = x;

                }

            }
            this.nrows = nrows;

            if (log.isInfoEnabled()) {

                log.info("nrows=" + nrows + ", ncols=" + ncols);
                
                log.info("maxSamples=" + maxSamples + " @ index="
                        + maxSamplesIndex);

                log.info("firstTimestamp=" + firstTimestamp + " @ index="
                        + firstSampleTimeIndex);

                log.info("lastTimestamp=" + lastTimestamp + " @ index="
                        + lastSampleTimeIndex);

                log.info("logicalSlotOffset=" + logicalSlotOffset
                        + " : firstLogicalSlot="
                        + Arrays.toString(firstLogicalSlot));

                StringBuilder sb = new StringBuilder();
                sb.append("[");
                for (int i = 0; i < a.length; i++) {

                    if (i > 0)
                        sb.append(", ");

                    sb.append(getFirstRowIndex(i));
                    
                }
                sb.append("]");
                
                log.info("adjustedLogicalSlots: "+sb);
                
            }
            
        } else {

            this.nrows = 0;
            this.maxSamples = 0;
            this.maxSamplesIndex = 0;
            this.firstTimestamp = 0L;
            this.firstSampleTimeIndex = 0;
            this.lastTimestamp = 0L;
            this.lastSampleTimeIndex = 0;
            this.logicalSlotOffset = 0L;

        }

        /*
         * Align the counters and populate the rows with the counter values
         * from the selected history unit.
         */
        
        data = new IHistoryEntry[nrows][];

        // pre-populate each row of the table with an array.
        for (int row = 0; row < nrows; row++) {

            data[row] = new IHistoryEntry[ncols];

        }

        /*
         * Now fill in each cell of the table. Since we know the row at
         * which each counter starts in the table, it is easier to proceed
         * in column order for each counter in turn.
         */
        for (int col = 0; col < ncols; col++) {

            final int firstRow = getFirstRowIndex(col);
            
            final SampleIterator itr = hitrs[col];

            int i = 0;

            if (log.isDebugEnabled() && col == 0)
                log.debug(a[col].getPath());

            while (itr.hasNext()) {

                final IHistoryEntry e = itr.next();

                final int row = i + firstRow;

                if (log.isDebugEnabled() && col == 0)
                    log.debug("data[" + row + "," + col + "] = " + e);

                data[row][col] = e;

                i++;
                
            }
            
        }

    }

    /**
     * The logical slot into which the first sample falls for each of the
     * specified counters. This is just timestamp/period for
     * the sample.
     */
    final long[] firstLogicalSlot;

    /**
     * The logical slots are adjusted to a common base (origin zero) by
     * subtracting out the logical slot of the counter with the earliest
     * timestamp for any of the specified counters - this is the value of
     * {@link #firstLogicalSlot} at the {@link #firstSampleTimeIndex}.
     */
    final long logicalSlotOffset;

    /**
     * The index of the row into which the first sample for a counter falls
     * is given by
     * 
     * 
     * (int) (firstLogicalSlot[counterIndex] - logicalSlotOffset)
     * 
* * @param counterIndex * The index of the counter in the array specified to the * ctor. */ public int getFirstRowIndex(final int counterIndex) { return (int) (firstLogicalSlot[counterIndex] - logicalSlotOffset); } /** * The earliest timestamp in the selected history units for any of the * specified counters. */ public final long firstTimestamp; /** * The most recent timestamp in the selected history units for any of * the specified counters. */ public final long lastTimestamp; /** * The index of the counter in the specified array having the greatest * number of samples for the selected history units. */ final int maxSamplesIndex; /** * The index of the counter in the specified array whose first sample * timestamp was selected as the {@link #firstTimestamp} for the table. */ final int firstSampleTimeIndex; /** * The index of the counter in the specified array whose last sample * timestamp was selected as the {@link #lastTimestamp} for the table. */ final int lastSampleTimeIndex; /** * The index of the counter in the specified array with the greatest #of * samples for the selected history units. */ final int maxSamples; /** * The #of rows in the table. This can be more than the #of samples. It * is in fact the max( sampleCount + rowOffset) for each counters. */ public final int nrows; /** * The #of columns in the table. This is the same as the #of counters * specified to the ctor. */ public final int ncols; /** * The label for the units of the history. */ public final String units; /** * The #of milliseconds in each unit for {@link #units}. */ public final long period; /** * An array of the performance counter values. The first index is the * row. The second index is the column and is correlated with the array * specified to the ctor. The rows of the performance counters in the * caller's array are aligned by first deciding which counter has the * earliest timestamp to be reported ({@link #firstSampleTimeIndex}) * and then examining the other counters and deciding if they have a * value for the same reporting period. If a counter has a value for the * same reporting period then the value is incorporated into that row * and the row index for that counter is advanced. Otherwise the row * index for that counter IS NOT advanced. If there is no data for a * given counter in a given row then that cell will be null. * It is necessary to align the samples in this manner as counters are * created and destroyed over the life of the system and thus some * counters may not have data for some reporting periods. */ public final IHistoryEntry[][] data; /** * Return the timestamp for the row, which is the timestamp of first * sample which would be allowed into the logical slot for that row. * * @param row * The row. * * @return The timestamp of the first sample which would be allowed into * that row. */ public long getTimestamp(final int row) { return (row + logicalSlotOffset) * period; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy