com.facebook.stats.MultiWindowGauge.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.stats.MultiWindowGauge.java

Source

/*
 * Copyright (C) 2012 Facebook, Inc.
 *
 * 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.facebook.stats;

import org.joda.time.DateTime;
import org.joda.time.DateTimeUtils;
import org.joda.time.Duration;
import org.joda.time.ReadableDateTime;

public class MultiWindowGauge implements ReadableMultiWindowGauge, WritableMultiWindowStat {
    private final GaugeCounterFactory gaugeCounterFactory;
    // all-time counter is a windowed counter that is effectively unbounded
    private final CompositeGaugeCounter allTimeCounter;
    private final CompositeGaugeCounter hourCounter;
    private final CompositeGaugeCounter tenMinuteCounter;
    private final CompositeGaugeCounter minuteCounter;
    private volatile GaugeCounter currentCounter;
    private final ReadableDateTime start;

    private final Object rollLock = new Object();

    public MultiWindowGauge() {
        this(DefaultGaugeCounterFactory.INSTANCE);
    }

    public MultiWindowGauge(GaugeCounterFactory gaugeCounterFactory) {
        this(gaugeCounterFactory, newCompositeGaugeCounter(Integer.MAX_VALUE, gaugeCounterFactory),
                newCompositeGaugeCounter(60, gaugeCounterFactory),
                newCompositeGaugeCounter(10, gaugeCounterFactory), newCompositeGaugeCounter(1, gaugeCounterFactory),
                new DateTime());
    }

    MultiWindowGauge(GaugeCounterFactory gaugeCounterFactory, CompositeGaugeCounter allTimeCounter,
            CompositeGaugeCounter hourCounter, CompositeGaugeCounter tenMinuteCounter,
            CompositeGaugeCounter minuteCounter, ReadableDateTime start) {
        this.gaugeCounterFactory = gaugeCounterFactory;
        this.allTimeCounter = allTimeCounter;
        this.hourCounter = hourCounter;
        this.tenMinuteCounter = tenMinuteCounter;
        this.minuteCounter = minuteCounter;
        this.start = start;
        currentCounter = nextCurrentCounter();
    }

    private static CompositeGaugeCounter newCompositeGaugeCounter(int minutes,
            GaugeCounterFactory gaugeCounterFactory) {
        return new CompositeGaugeCounter(Duration.standardMinutes(minutes), gaugeCounterFactory);
    }

    @Override
    public void add(long delta) {
        rollCurrentIfNeeded();
        currentCounter.add(delta);
    }

    private void rollCurrentIfNeeded() {
        // do outside the synchronized block
        long now = DateTimeUtils.currentTimeMillis();
        // this is false for the majority of calls, so skip lock acquisition
        if (currentCounter.getEnd().getMillis() < now) {
            synchronized (rollLock) {
                // lock and re-check
                if (currentCounter.getEnd().getMillis() < now) {
                    currentCounter = nextCurrentCounter();
                }
            }
        }
    }

    private long calcRate(EventCounterIf<GaugeCounter> counter) {
        long value = counter.getValue();
        ReadableDateTime end = counter.getEnd();
        ReadableDateTime start = counter.getStart();
        ReadableDateTime now = new DateTime();
        Duration duration = now.isBefore(end) ? new Duration(start, now) : // so far
                new Duration(start, end);
        long secs = duration.getStandardSeconds();
        return secs > 0 ? value / secs : value;
    }

    @Override
    public long getMinuteSum() {
        rollCurrentIfNeeded();
        return minuteCounter.getValue();
    }

    @Override
    public long getMinuteSamples() {
        rollCurrentIfNeeded();
        return minuteCounter.getSamples();
    }

    @Override
    public long getMinuteAvg() {
        rollCurrentIfNeeded();
        return minuteCounter.getAverage();
    }

    @Override
    public long getMinuteRate() {
        rollCurrentIfNeeded();
        return calcRate(minuteCounter);
    }

    @Override
    public long getTenMinuteSum() {
        rollCurrentIfNeeded();
        return tenMinuteCounter.getValue();
    }

    @Override
    public long getTenMinuteSamples() {
        rollCurrentIfNeeded();
        return tenMinuteCounter.getSamples();
    }

    @Override
    public long getTenMinuteAvg() {
        rollCurrentIfNeeded();
        return tenMinuteCounter.getAverage();
    }

    @Override
    public long getTenMinuteRate() {
        rollCurrentIfNeeded();
        return calcRate(tenMinuteCounter);
    }

    @Override
    public long getHourSum() {
        rollCurrentIfNeeded();
        return hourCounter.getValue();
    }

    @Override
    public long getHourSamples() {
        rollCurrentIfNeeded();
        return hourCounter.getSamples();
    }

    @Override
    public long getHourAvg() {
        rollCurrentIfNeeded();
        return hourCounter.getAverage();
    }

    @Override
    public long getHourRate() {
        rollCurrentIfNeeded();
        return calcRate(hourCounter);
    }

    @Override
    public long getAllTimeSum() {
        return allTimeCounter.getValue();
    }

    @Override
    public long getAllTimeSamples() {
        return allTimeCounter.getSamples();
    }

    @Override
    public long getAllTimeAvg() {
        return allTimeCounter.getAverage();
    }

    @Override
    public long getAllTimeRate() {
        Duration sinceStart = new Duration(start, new DateTime());
        if (sinceStart.getStandardSeconds() == 0) {
            return 0;
        }
        return allTimeCounter.getValue() / sinceStart.getStandardSeconds();
    }

    /*
     * Create a new counter for the next 6 secs and add it to all the
     * composite counters.
     */
    private GaugeCounter nextCurrentCounter() {
        ReadableDateTime now = new DateTime();
        GaugeCounter gaugeCounter = gaugeCounterFactory.create(now, now.toDateTime().plusSeconds(6));

        allTimeCounter.addEventCounter(gaugeCounter);
        hourCounter.addEventCounter(gaugeCounter);
        tenMinuteCounter.addEventCounter(gaugeCounter);
        minuteCounter.addEventCounter(gaugeCounter);

        return gaugeCounter;
    }

    public MultiWindowGauge merge(MultiWindowGauge rhs) {
        return new MultiWindowGauge(gaugeCounterFactory,
                (CompositeGaugeCounter) allTimeCounter.merge(rhs.allTimeCounter),
                (CompositeGaugeCounter) hourCounter.merge(rhs.hourCounter),
                (CompositeGaugeCounter) tenMinuteCounter.merge(rhs.tenMinuteCounter),
                (CompositeGaugeCounter) minuteCounter.merge(rhs.minuteCounter),
                start.isBefore(rhs.start) ? start : rhs.start);
    }
}