com.facebook.stats.MultiWindowRate Maven / Gradle / Ivy
 The newest version!
        
        /*
 * 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 MultiWindowRate implements ReadableMultiWindowRate, WritableMultiWindowStat {
  private static final int DEFAULT_TIME_BUCKET_SIZE_MILLIS = 6000; // 6 seconds
  // all-time counter is a windowed counter that is effectively unbounded
  private final CompositeSum allTimeCounter;
  private final CompositeSum hourCounter;
  private final CompositeSum tenMinuteCounter;
  private final CompositeSum minuteCounter;
  private final EventRate hourRate;
  private final EventRate tenMinuteRate;
  private final EventRate minuteRate;
  private final ReadableDateTime start;
  private final Object rollLock = new Object();
  private final int timeBucketSizeMillis;
  private volatile EventCounterIf currentCounter;
  MultiWindowRate(int timeBucketSizeMillis) {
    this(
      newCompositeEventCounter(Integer.MAX_VALUE),
      newCompositeEventCounter(60),
      newCompositeEventCounter(10),
      newCompositeEventCounter(1),
      new DateTime(),
      timeBucketSizeMillis
    );
  }
  MultiWindowRate(
    CompositeSum allTimeCounter,
    CompositeSum hourCounter,
    CompositeSum tenMinuteCounter,
    CompositeSum minuteCounter,
    ReadableDateTime start,
    int timeBucketSizeMillis
  ) {
    this.allTimeCounter = allTimeCounter;
    this.hourCounter = hourCounter;
    this.tenMinuteCounter = tenMinuteCounter;
    this.minuteCounter = minuteCounter;
    this.start = start;
    this.timeBucketSizeMillis = timeBucketSizeMillis;
    hourRate =
      newEventRate(hourCounter, Duration.standardMinutes(60), start);
    tenMinuteRate =
      newEventRate(tenMinuteCounter, Duration.standardMinutes(10), start);
    minuteRate =
      newEventRate(minuteCounter, Duration.standardMinutes(1), start);
    currentCounter = nextCurrentCounter(start.toDateTime());
  }
  public MultiWindowRate() {
    this(DEFAULT_TIME_BUCKET_SIZE_MILLIS);
  }
  private static CompositeSum newCompositeEventCounter(int minutes) {
    return new CompositeSum(Duration.standardMinutes(minutes));
  }
  private EventRate newEventRate(
    EventCounterIf counter, Duration windowSize, ReadableDateTime start
  ) {
    return new EventRateImpl(counter, windowSize, start);
  }
  @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(new DateTime(now));
        }
      }
    }
  }
  @Override
  public long getMinuteSum() {
    rollCurrentIfNeeded();
    return minuteCounter.getValue();
  }
  @Override
  public long getMinuteRate() {
    rollCurrentIfNeeded();
    return minuteRate.getValue();
  }
  @Override
  public long getTenMinuteSum() {
    rollCurrentIfNeeded();
    return tenMinuteCounter.getValue();
  }
  @Override
  public long getTenMinuteRate() {
    rollCurrentIfNeeded();
    return tenMinuteRate.getValue();
  }
  @Override
  public long getHourSum() {
    rollCurrentIfNeeded();
    return hourCounter.getValue();
  }
  @Override
  public long getHourRate() {
    rollCurrentIfNeeded();
    return hourRate.getValue();
  }
  @Override
  public long getAllTimeSum() {
    return allTimeCounter.getValue();
  }
  @Override
  public long getAllTimeRate() {
    Duration sinceStart = new Duration(start, getNow());
    if (sinceStart.getStandardSeconds() == 0) {
      return 0;
    }
    return allTimeCounter.getValue() / sinceStart.getStandardSeconds();
  }
  protected ReadableDateTime getNow() {
    return new DateTime();
  }
  // current
  private EventCounterIf nextCurrentCounter(ReadableDateTime now) {
    EventCounter eventCounter =
      new EventCounterImpl(now, now.toDateTime().plusMillis(timeBucketSizeMillis));
    allTimeCounter.addEventCounter(eventCounter);
    hourCounter.addEventCounter(eventCounter);
    tenMinuteCounter.addEventCounter(eventCounter);
    minuteCounter.addEventCounter(eventCounter);
    return eventCounter;
  }
  public MultiWindowRate merge(MultiWindowRate rate) {
    return new MultiWindowRate(
      (CompositeSum)allTimeCounter.merge(rate.allTimeCounter),
      (CompositeSum)hourCounter.merge(rate.hourCounter),
      (CompositeSum)tenMinuteCounter.merge(rate.tenMinuteCounter),
      (CompositeSum)minuteCounter.merge(rate.minuteCounter),
      start.isBefore(rate.start) ? start : rate.start,
      timeBucketSizeMillis
    );
  }
}
       © 2015 - 2025 Weber Informatics LLC | Privacy Policy