blob: cdec1e0ed3e2e036af82c96fe35b100510296166 [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
18/**
19 *
20 */
21package net.floodlightcontroller.counter;
22
23import java.util.Collections;
24import java.util.Date;
25import java.util.HashMap;
26import java.util.Map;
27import java.util.Queue;
28import java.util.Set;
29import java.util.concurrent.ConcurrentLinkedQueue;
30import java.util.concurrent.ConcurrentHashMap;
31import java.util.concurrent.Executors;
32import java.util.concurrent.TimeUnit;
33
34import net.floodlightcontroller.counter.CounterValue.CounterType;
35
36
37/**
38 * This module needs to be updated with CounterValue.
39 *
40 * This is a crumby attempt at a highly concurrent implementation of the Counter interface.
41 *
42 * (Help! Help! Someone please re-write me! This will almost certainly break at high loads.)
43 *
44 * The gist is that this class, ConcurrentCounter, keeps an internal highly transient buffer that is occasionally flushed
45 * in to a set of CountBuffers (circular buffers) which store a longer term historical view of the count values at different
46 * moments in time.
47 *
48 * This Counter implementation may be a bit over-engineered... The goal here was to present an implementation that is very
49 * predictable with respect to memory and CPU time and, at the same time, present a very fast increment() method. The reasoning
50 * here is that this will be a go-to class when it comes to debugging, particularly in high-load situations where logging
51 * may introduce so much variability to the system that it foils the results.
52 *
53 * @author kyle
54 *
55 */
56public class ConcurrentCounter implements ICounter {
57
58 protected static final Map<DateSpan, Integer> MAX_HISTORY = new HashMap<DateSpan, Integer>();
59 static {
60 MAX_HISTORY.put(DateSpan.REALTIME, new Integer(1));
61 MAX_HISTORY.put(DateSpan.SECONDS, new Integer(120));
62 MAX_HISTORY.put(DateSpan.MINUTES, new Integer(60));
63 MAX_HISTORY.put(DateSpan.HOURS, new Integer(48));
64 MAX_HISTORY.put(DateSpan.DAYS, new Integer(60));
65 MAX_HISTORY.put(DateSpan.WEEKS, new Integer(2));
66 }
67
68 protected static Set<ConcurrentCounter> liveCounters;
69
70 static {
71 liveCounters = Collections.newSetFromMap(new ConcurrentHashMap<ConcurrentCounter, Boolean>()); //nifty way to get concurrent hash set
72 //Set a background thread to flush any liveCounters every 100 milliseconds
73 Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
74 public void run() {
75 for(ConcurrentCounter c : liveCounters) {
76 c.flush();
77 }
78 }}, 100, 100, TimeUnit.MILLISECONDS);
79 }
80
81 /**
82 * Very simple data structure to store off a single count entry at a single point in time
83 * @author kyle
84 *
85 */
86 protected static final class CountAtom {
87 protected Date date;
88 protected Long delta;
89
90 protected CountAtom(Date date, Long delta) {
91 this.date = date;
92 this.delta = delta;
93 }
94
95 public String toString() {
96 return "[" + this.date + ": " + this.delta + "]";
97 }
98 }
99
100
101 protected Queue<CountAtom> unprocessedCountBuffer;
102 protected Map<DateSpan, CountBuffer> counts;
103 protected Date startDate;
104
105 /**
106 * Factory method to create a new counter instance. (Design note -
107 * use a factory pattern here as it may be necessary to hook in other
108 * registrations around counter objects as they are created.)
109 *
110 * @param startDate
111 * @return
112 */
113 public static ICounter createCounter(Date startDate) {
114 ConcurrentCounter cc = new ConcurrentCounter(startDate);
115 ConcurrentCounter.liveCounters.add(cc);
116 return cc;
117
118 }
119
120 /**
121 * Protected constructor - use createCounter factory method instead
122 * @param startDate
123 */
124 protected ConcurrentCounter(Date startDate) {
125 init(startDate);
126 }
127
128 protected void init(Date startDate) {
129 this.startDate = startDate;
130 this.unprocessedCountBuffer = new ConcurrentLinkedQueue<CountAtom>();
131 this.counts = new HashMap<DateSpan, CountBuffer>();
132
133 for(DateSpan ds : DateSpan.values()) {
134 CountBuffer cb = new CountBuffer(startDate, ds, MAX_HISTORY.get(ds));
135 counts.put(ds, cb);
136 }
137 }
138 /**
139 * This is the key method that has to be both fast and very thread-safe.
140 */
141 @Override
142 public void increment() {
143 this.increment(new Date(), (long)1);
144 }
145
146 @Override
147 public void increment(Date d, long delta) {
148 this.unprocessedCountBuffer.add(new CountAtom(d, delta));
149 }
150
151 @Override
152 public void setCounter(Date d, CounterValue value) {
153 // To be done later
154 }
155
156 /**
157 * Reset the value.
158 */
159 @Override
160 public void reset(Date startDate) {
161 init(startDate);
162 }
163
164 /**
165 * Flushes values out of the internal buffer and in to structures
166 * that can be fetched with a call to snapshot()
167 */
168 public synchronized void flush() {
169 for(CountAtom c = this.unprocessedCountBuffer.poll(); c != null; c = this.unprocessedCountBuffer.poll()) {
170 for(DateSpan ds : DateSpan.values()) {
171 CountBuffer cb = counts.get(ds);
172 cb.increment(c.date, c.delta);
173 }
174 }
175 }
176
177 @Override
178 public CounterValue getCounterValue() {
179 // To be done later
180 //CountSeries cs = counts.get(DateSpan.REALTIME).snapshot();
181 //return cs.getSeries()[0];
182 return new CounterValue(CounterType.LONG);
183 }
184
185 @Override
186 public Date getCounterDate() {
187 // To be done later
188 //CountSeries cs = counts.get(DateSpan.REALTIME).snapshot();
189 //return cs.getSeries()[0];
190 return new Date();
191 }
192
193 @Override
194 /**
195 * This method returns a disconnected copy of the underlying CountSeries corresponding to dateSpan.
196 */
197 public CountSeries snapshot(DateSpan dateSpan) {
198 flush();
199 CountSeries cs = counts.get(dateSpan).snapshot();
200 return cs;
201 }
202
203
204
205}