blob: fa45862e8b938cad6a5b7e57600861986a68f7ff [file] [log] [blame]
Umesh Krishnaswamy345ee992012-12-13 20:29:48 -08001/**
2* Copyright 2011, Big Switch Networks, Inc.
3* Originally created by David Erickson, Stanford University
4*
5* Licensed under the Apache License, Version 2.0 (the "License"); you may
6* not use this file except in compliance with the License. You may obtain
7* a copy of the License at
8*
9* http://www.apache.org/licenses/LICENSE-2.0
10*
11* Unless required by applicable law or agreed to in writing, software
12* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14* License for the specific language governing permissions and limitations
15* under the License.
16**/
17
18package net.floodlightcontroller.counter;
19
20import java.util.Date;
21
22import net.floodlightcontroller.counter.ICounter.DateSpan;
23
24
25/**
26 * Implements a circular buffer to store the last x time-based counter values. This is pretty crumby
27 * implementation, basically wrapping everything with synchronized blocks, in order to ensure that threads
28 * which will be updating the series don't result in a thread which is reading the series getting stuck with
29 * a start date which does not correspond to the count values in getSeries.
30 *
31 * This could probably use a re-think...
32 *
33 * @author kyle
34 *
35 */
36public class CountBuffer {
37 protected long[] counterValues;
38 protected Date startDate;
39 protected DateSpan dateSpan;
40 protected int currentIndex;
41 protected int seriesLength;
42
43
44 public CountBuffer(Date startDate, DateSpan dateSpan, int seriesLength) {
45 this.seriesLength = seriesLength;
46 this.counterValues = new long[seriesLength];
47 this.dateSpan = dateSpan;
48
49 this.startDate = startDate;
50 this.currentIndex = 0;
51 }
52
53 /**
54 * Increment the count associated with Date d, forgetting some of the older count values if necessary to ensure
55 * that the total span of time covered by this series corresponds to DateSpan * seriesLength (circular buffer).
56 *
57 * Note - fails silently if the Date falls prior to the start of the tracked count values.
58 *
59 * Note - this should be a reasonably fast method, though it will have to block if there is another thread reading the
60 * series at the same time.
61 *
62 * @param d
63 * @param delta
64 */
65 public synchronized void increment(Date d, long delta) {
66
67 long dsMillis = CountSeries.dateSpanToMilliseconds(this.dateSpan);
68 Date endDate = new Date(startDate.getTime() + seriesLength * dsMillis - 1);
69
70 if(d.getTime() < startDate.getTime()) {
71 return; //silently fail rather than insert a count at a time older than the history buffer we're keeping
72 }
73 else if (d.getTime() >= startDate.getTime() && d.getTime() <= endDate.getTime()) {
74 int index = (int) (( d.getTime() - startDate.getTime() ) / dsMillis); // java rounds down on long/long
75 int modIndex = (index + currentIndex) % seriesLength;
76 long currentValue = counterValues[modIndex];
77 counterValues[modIndex] = currentValue + delta;
78 }
79 else if (d.getTime() > endDate.getTime()) {
80 //Initialize new buckets
81 int newBuckets = (int)((d.getTime() - endDate.getTime()) / dsMillis) + 1; // java rounds down on long/long
82 for(int i = 0; i < newBuckets; i++) {
83 int modIndex = (i + currentIndex) % seriesLength;
84 counterValues[modIndex] = 0;
85 }
86 //Update internal vars
87 this.startDate = new Date(startDate.getTime() + dsMillis * newBuckets);
88 this.currentIndex = (currentIndex + newBuckets) % this.seriesLength;
89
90 //Call again (date should be in the range this time)
91 this.increment(d, delta);
92 }
93 }
94
95 /**
96 * Relatively slow method, expected to be called primarily from UI rather than from in-packet-path.
97 *
98 * @return the count values associated with each time interval starting with startDate and demarc'ed by dateSpan
99 */
100 public long[] getSeries() { //synchronized here should lock on 'this', implying that it shares the lock with increment
101 long[] ret = new long[this.seriesLength];
102 for(int i = 0; i < this.seriesLength; i++) {
103 int modIndex = (currentIndex + i) % this.seriesLength;
104 ret[i] = this.counterValues[modIndex];
105 }
106 return ret;
107 }
108
109
110 /**
111 * Returns an immutable count series that represents a snapshot of this
112 * series at a specific moment in time.
113 * @return
114 */
115 public synchronized CountSeries snapshot() {
116 long[] cvs = new long[this.seriesLength];
117 for(int i = 0; i < this.seriesLength; i++) {
118 int modIndex = (this.currentIndex + i) % this.seriesLength;
119 cvs[i] = this.counterValues[modIndex];
120 }
121
122 return new CountSeries(this.startDate, this.dateSpan, cvs);
123 }
124
125}