| /** |
| * Copyright 2011, Big Switch Networks, Inc. |
| * Originally created by David Erickson, Stanford University |
| * |
| * 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 net.floodlightcontroller.counter; |
| |
| import java.util.Date; |
| |
| import net.floodlightcontroller.counter.ICounter.DateSpan; |
| |
| |
| /** |
| * Implements a circular buffer to store the last x time-based counter values. This is pretty crumby |
| * implementation, basically wrapping everything with synchronized blocks, in order to ensure that threads |
| * which will be updating the series don't result in a thread which is reading the series getting stuck with |
| * a start date which does not correspond to the count values in getSeries. |
| * |
| * This could probably use a re-think... |
| * |
| * @author kyle |
| * |
| */ |
| public class CountBuffer { |
| protected long[] counterValues; |
| protected Date startDate; |
| protected DateSpan dateSpan; |
| protected int currentIndex; |
| protected int seriesLength; |
| |
| |
| public CountBuffer(Date startDate, DateSpan dateSpan, int seriesLength) { |
| this.seriesLength = seriesLength; |
| this.counterValues = new long[seriesLength]; |
| this.dateSpan = dateSpan; |
| |
| this.startDate = startDate; |
| this.currentIndex = 0; |
| } |
| |
| /** |
| * Increment the count associated with Date d, forgetting some of the older count values if necessary to ensure |
| * that the total span of time covered by this series corresponds to DateSpan * seriesLength (circular buffer). |
| * |
| * Note - fails silently if the Date falls prior to the start of the tracked count values. |
| * |
| * Note - this should be a reasonably fast method, though it will have to block if there is another thread reading the |
| * series at the same time. |
| * |
| * @param d |
| * @param delta |
| */ |
| public synchronized void increment(Date d, long delta) { |
| |
| long dsMillis = CountSeries.dateSpanToMilliseconds(this.dateSpan); |
| Date endDate = new Date(startDate.getTime() + seriesLength * dsMillis - 1); |
| |
| if(d.getTime() < startDate.getTime()) { |
| return; //silently fail rather than insert a count at a time older than the history buffer we're keeping |
| } |
| else if (d.getTime() >= startDate.getTime() && d.getTime() <= endDate.getTime()) { |
| int index = (int) (( d.getTime() - startDate.getTime() ) / dsMillis); // java rounds down on long/long |
| int modIndex = (index + currentIndex) % seriesLength; |
| long currentValue = counterValues[modIndex]; |
| counterValues[modIndex] = currentValue + delta; |
| } |
| else if (d.getTime() > endDate.getTime()) { |
| //Initialize new buckets |
| int newBuckets = (int)((d.getTime() - endDate.getTime()) / dsMillis) + 1; // java rounds down on long/long |
| for(int i = 0; i < newBuckets; i++) { |
| int modIndex = (i + currentIndex) % seriesLength; |
| counterValues[modIndex] = 0; |
| } |
| //Update internal vars |
| this.startDate = new Date(startDate.getTime() + dsMillis * newBuckets); |
| this.currentIndex = (currentIndex + newBuckets) % this.seriesLength; |
| |
| //Call again (date should be in the range this time) |
| this.increment(d, delta); |
| } |
| } |
| |
| /** |
| * Relatively slow method, expected to be called primarily from UI rather than from in-packet-path. |
| * |
| * @return the count values associated with each time interval starting with startDate and demarc'ed by dateSpan |
| */ |
| public long[] getSeries() { //synchronized here should lock on 'this', implying that it shares the lock with increment |
| long[] ret = new long[this.seriesLength]; |
| for(int i = 0; i < this.seriesLength; i++) { |
| int modIndex = (currentIndex + i) % this.seriesLength; |
| ret[i] = this.counterValues[modIndex]; |
| } |
| return ret; |
| } |
| |
| |
| /** |
| * Returns an immutable count series that represents a snapshot of this |
| * series at a specific moment in time. |
| * @return |
| */ |
| public synchronized CountSeries snapshot() { |
| long[] cvs = new long[this.seriesLength]; |
| for(int i = 0; i < this.seriesLength; i++) { |
| int modIndex = (this.currentIndex + i) % this.seriesLength; |
| cvs[i] = this.counterValues[modIndex]; |
| } |
| |
| return new CountSeries(this.startDate, this.dateSpan, cvs); |
| } |
| |
| } |