/*
* 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.ReadableDateTime;
import org.joda.time.ReadableDuration;
import java.util.Arrays;
import java.util.Iterator;
/**
* Keeps a list of gauge counters (see AbstractCompositeCounter)
* that look like one summarized gauge counter.
*/
public class CompositeGaugeCounter extends AbstractCompositeSum<GaugeCounter>
implements GaugeCounter {
private final GaugeCounterFactory gaugeCounterFactory;
public CompositeGaugeCounter(
ReadableDuration maxLength,
ReadableDuration maxChunkLength,
GaugeCounterFactory gaugeCounterFactory
) {
super(maxLength, maxChunkLength);
this.gaugeCounterFactory = gaugeCounterFactory;
}
public CompositeGaugeCounter(
ReadableDuration maxLength, GaugeCounterFactory gaugeCounterFactory
) {
super(maxLength);
this.gaugeCounterFactory = gaugeCounterFactory;
}
public CompositeGaugeCounter(ReadableDuration maxLength) {
this(maxLength, DefaultGaugeCounterFactory.INSTANCE);
}
public synchronized CompositeEventCounterIf<GaugeCounter> add(
long delta, long nsamples, ReadableDateTime start, ReadableDateTime end
) {
GaugeCounter counter = nextCounter(start, end);
counter.add(delta, nsamples);
return addEventCounter(counter);
}
/**
* mirrors AbstractCompositeCounter:add()
*/
@Override
public void add(long delta, long nsamples) {
DateTime now = new DateTime();
GaugeCounter last;
synchronized (this) {
if (getMostRecentCounter() == null ||
!now.isBefore(getMostRecentCounter().getEnd())) {
addEventCounter(nextCounter(now, now.plus(getMaxChunkLength())));
}
last = getMostRecentCounter();
}
last.add(delta, nsamples);
}
/**
* mirrors AbstractCompositeCounter:getValue()
*/
@Override
public synchronized long getSamples() {
long nsamples = 0L;
trimIfNeeded();
Iterator<GaugeCounter> counterIterator = getEventCounters().iterator();
boolean first = true;
while (counterIterator.hasNext()) {
GaugeCounter counter = counterIterator.next();
nsamples += counter.getSamples();
if (first) {
// if there is at least one counter that is partially expired
if (getWindowStart().isAfter(counter.getStart())) {
// adjust for partial expiration
nsamples -=
(long) (getExpiredFraction(counter) * counter.getSamples());
}
first = false;
}
}
return nsamples;
}
public synchronized long getAverage() {
long nsamples = getSamples();
if (nsamples == 0) {
return 0;
}
long value = getValue();
return value / nsamples;
}
@Override
protected GaugeCounter nextCounter(
ReadableDateTime start, ReadableDateTime end
) {
return gaugeCounterFactory.create(start, end);
}
@Override
public GaugeCounter merge(GaugeCounter counter) {
// special case to handle merging of 2 composite counters
if (counter instanceof CompositeGaugeCounter) {
return internalMerge(
((CompositeGaugeCounter) counter).getEventCounters(),
new CompositeGaugeCounter( getMaxLength(), getMaxChunkLength(), gaugeCounterFactory)
);
} else {
return internalMerge(
Arrays.asList(counter),
new CompositeGaugeCounter(getMaxLength(), getMaxChunkLength(), gaugeCounterFactory)
);
}
}
}