blob: 029d70ae55ce639df0689f2dd9ead7b941940cbd [file] [log] [blame]
Brian O'Connor6ccba962015-02-17 18:16:02 -08001/*
2 * Copyright 2015 Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package org.onosproject.intentperf;
17
Brian O'Connor87ba7a72015-03-11 14:40:09 -070018import com.google.common.collect.ArrayListMultimap;
Brian O'Connor6ccba962015-02-17 18:16:02 -080019import com.google.common.collect.Lists;
20import com.google.common.collect.Maps;
Brian O'Connor87ba7a72015-03-11 14:40:09 -070021import com.google.common.collect.Multimap;
Brian O'Connor6ccba962015-02-17 18:16:02 -080022import com.google.common.collect.Sets;
Brian O'Connor87ba7a72015-03-11 14:40:09 -070023import org.apache.commons.lang.math.RandomUtils;
Brian O'Connor6ccba962015-02-17 18:16:02 -080024import org.apache.felix.scr.annotations.Activate;
25import org.apache.felix.scr.annotations.Component;
26import org.apache.felix.scr.annotations.Deactivate;
Brian O'Connor87ba7a72015-03-11 14:40:09 -070027import org.apache.felix.scr.annotations.Property;
Brian O'Connor6ccba962015-02-17 18:16:02 -080028import org.apache.felix.scr.annotations.Reference;
Brian O'Connorb9a91c12015-03-10 11:19:50 -070029import org.onlab.packet.MacAddress;
Brian O'Connor6ccba962015-02-17 18:16:02 -080030import org.onlab.util.Counter;
Brian O'Connor87ba7a72015-03-11 14:40:09 -070031import org.onosproject.cfg.ComponentConfigService;
Brian O'Connor6ccba962015-02-17 18:16:02 -080032import org.onosproject.cluster.ClusterService;
Brian O'Connor87ba7a72015-03-11 14:40:09 -070033import org.onosproject.cluster.ControllerNode;
34import org.onosproject.cluster.NodeId;
Brian O'Connor6ccba962015-02-17 18:16:02 -080035import org.onosproject.core.ApplicationId;
36import org.onosproject.core.CoreService;
Brian O'Connor87ba7a72015-03-11 14:40:09 -070037import org.onosproject.mastership.MastershipService;
Brian O'Connor6ccba962015-02-17 18:16:02 -080038import org.onosproject.net.ConnectPoint;
39import org.onosproject.net.Device;
40import org.onosproject.net.PortNumber;
41import org.onosproject.net.device.DeviceService;
42import org.onosproject.net.flow.DefaultTrafficSelector;
43import org.onosproject.net.flow.DefaultTrafficTreatment;
44import org.onosproject.net.flow.TrafficSelector;
45import org.onosproject.net.flow.TrafficTreatment;
46import org.onosproject.net.intent.Intent;
47import org.onosproject.net.intent.IntentEvent;
48import org.onosproject.net.intent.IntentListener;
49import org.onosproject.net.intent.IntentService;
50import org.onosproject.net.intent.Key;
Brian O'Connor87ba7a72015-03-11 14:40:09 -070051import org.onosproject.net.intent.PartitionService;
Brian O'Connor6ccba962015-02-17 18:16:02 -080052import org.onosproject.net.intent.PointToPointIntent;
53import org.slf4j.Logger;
54
Brian O'Connor87ba7a72015-03-11 14:40:09 -070055import java.util.ArrayList;
Brian O'Connor6ccba962015-02-17 18:16:02 -080056import java.util.Collections;
Brian O'Connor6ccba962015-02-17 18:16:02 -080057import java.util.List;
58import java.util.Map;
59import java.util.Set;
60import java.util.Timer;
61import java.util.TimerTask;
62import java.util.concurrent.ExecutorService;
63import java.util.concurrent.Executors;
64import java.util.concurrent.TimeUnit;
Brian O'Connor87ba7a72015-03-11 14:40:09 -070065import java.util.stream.Collectors;
Brian O'Connor6ccba962015-02-17 18:16:02 -080066
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080067import static com.google.common.base.Preconditions.checkState;
Thomas Vachuska0249b532015-02-20 16:46:18 -080068import static java.lang.String.format;
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -080069import static java.lang.System.currentTimeMillis;
Brian O'Connorbcfeadb2015-02-19 21:50:01 -080070import static org.apache.felix.scr.annotations.ReferenceCardinality.MANDATORY_UNARY;
Brian O'Connor6ccba962015-02-17 18:16:02 -080071import static org.onlab.util.Tools.delay;
72import static org.onlab.util.Tools.groupedThreads;
Brian O'Connor36ef71a2015-02-24 12:05:01 -080073import static org.onosproject.net.intent.IntentEvent.Type.*;
Brian O'Connor6ccba962015-02-17 18:16:02 -080074import static org.slf4j.LoggerFactory.getLogger;
75
76/**
Brian O'Connor36ef71a2015-02-24 12:05:01 -080077 * Application to test sustained intent throughput.
Brian O'Connor6ccba962015-02-17 18:16:02 -080078 */
79@Component(immediate = true)
80public class IntentPerfInstaller {
81
82 private final Logger log = getLogger(getClass());
83
Brian O'Connor87ba7a72015-03-11 14:40:09 -070084 private static final int DEFAULT_NUM_WORKERS = 1;
85
86 private static final int DEFAULT_NUM_KEYS = 40_000;
87 private static final int DEFAULT_GOAL_CYCLE_PERIOD = 1_000; //ms
88
89 private static final int DEFAULT_NUM_NEIGHBORS = 0;
90
91 private static final int START_DELAY = 5_000; // ms
92 private static final int REPORT_PERIOD = 5_000; //ms
93
94 //FIXME add path length
95
96 @Property(name = "numKeys", intValue = DEFAULT_NUM_KEYS,
Thomas Vachuska7648d662015-03-16 11:58:30 -070097 label = "Number of keys (i.e. unique intents) to generate per instance")
Brian O'Connor87ba7a72015-03-11 14:40:09 -070098 private int numKeys = DEFAULT_NUM_KEYS;
99
100 //TODO implement numWorkers property
101// @Property(name = "numThreads", intValue = DEFAULT_NUM_WORKERS,
102// label = "Number of installer threads per instance")
103// private int numWokers = DEFAULT_NUM_WORKERS;
104
105 @Property(name = "cyclePeriod", intValue = DEFAULT_GOAL_CYCLE_PERIOD,
Thomas Vachuska7648d662015-03-16 11:58:30 -0700106 label = "Goal for cycle period (in ms)")
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700107 private int cyclePeriod = DEFAULT_GOAL_CYCLE_PERIOD;
108
109 @Property(name = "numNeighbors", intValue = DEFAULT_NUM_NEIGHBORS,
Thomas Vachuska7648d662015-03-16 11:58:30 -0700110 label = "Number of neighbors to generate intents for")
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700111 private int numNeighbors = DEFAULT_NUM_NEIGHBORS;
112
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800113 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -0800114 protected CoreService coreService;
115
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800116 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -0800117 protected IntentService intentService;
118
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800119 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -0800120 protected ClusterService clusterService;
121
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800122 @Reference(cardinality = MANDATORY_UNARY)
Brian O'Connor6ccba962015-02-17 18:16:02 -0800123 protected DeviceService deviceService;
124
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700125 @Reference(cardinality = MANDATORY_UNARY)
126 protected MastershipService mastershipService;
127
128 @Reference(cardinality = MANDATORY_UNARY)
129 protected PartitionService partitionService;
130
131 @Reference(cardinality = MANDATORY_UNARY)
132 protected ComponentConfigService configService;
133
Thomas Vachuska7648d662015-03-16 11:58:30 -0700134 @Reference(cardinality = MANDATORY_UNARY)
135 protected IntentPerfCollector sampleCollector;
136
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800137 private ExecutorService workers;
Brian O'Connor6ccba962015-02-17 18:16:02 -0800138 private ApplicationId appId;
139 private Listener listener;
Brian O'Connor6ccba962015-02-17 18:16:02 -0800140 private boolean stopped;
141
Brian O'Connor6ccba962015-02-17 18:16:02 -0800142 private Timer reportTimer;
143
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800144 // FIXME this variable isn't shared properly between multiple worker threads
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800145 private int lastKey = 0;
Brian O'Connor6ccba962015-02-17 18:16:02 -0800146
Thomas Vachuska7648d662015-03-16 11:58:30 -0700147 private IntentPerfUi perfUi;
148
Brian O'Connor6ccba962015-02-17 18:16:02 -0800149 @Activate
150 public void activate() {
Thomas Vachuska7648d662015-03-16 11:58:30 -0700151// configService.registerProperties(getClass());
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700152
Brian O'Connor6ccba962015-02-17 18:16:02 -0800153 String nodeId = clusterService.getLocalNode().ip().toString();
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800154 appId = coreService.registerApplication("org.onosproject.intentperf." + nodeId);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800155
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800156 reportTimer = new Timer("onos-intent-perf-reporter");
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700157 workers = Executors.newFixedThreadPool(DEFAULT_NUM_WORKERS, groupedThreads("onos/intent-perf", "worker-%d"));
158
159
160 // disable flow backups for testing
161 log.info("flow props: {}",
162 configService.getProperties("org.onosproject.store.flow.impl.DistributedFlowRuleStore"));
163 configService.setProperty("org.onosproject.store.flow.impl.DistributedFlowRuleStore",
164 "backupEnabled", "false");
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800165
166 // Schedule delayed start
167 reportTimer.schedule(new TimerTask() {
168 @Override
169 public void run() {
170 start();
171 }
172 }, START_DELAY);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800173 }
174
175 @Deactivate
176 public void deactivate() {
Thomas Vachuska7648d662015-03-16 11:58:30 -0700177// configService.unregisterProperties(getClass(), false);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800178 stop();
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700179 }
180
181 //FIXME add modified
182
183 private void logConfig(String prefix) {
184 log.info("{} with appId {}; numKeys = {}; cyclePeriod = {} ms; numNeighbors={}",
185 prefix, appId.id(), numKeys, cyclePeriod, numNeighbors);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800186 }
187
188 public void start() {
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700189 // adjust numNeighbors and generate list of neighbors
190 numNeighbors = Math.min(clusterService.getNodes().size() - 1, numNeighbors);
191
Brian O'Connor6ccba962015-02-17 18:16:02 -0800192 // perhaps we want to prime before listening...
193 // we will need to discard the first few results for priming and warmup
194 listener = new Listener();
195 intentService.addListener(listener);
Thomas Vachuska0249b532015-02-20 16:46:18 -0800196
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800197 // Schedule reporter task on report period boundary
Brian O'Connor6ccba962015-02-17 18:16:02 -0800198 reportTimer.scheduleAtFixedRate(new TimerTask() {
199 @Override
200 public void run() {
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800201 //adjustRates(); // FIXME we currently adjust rates in the cycle thread
Brian O'Connor6ccba962015-02-17 18:16:02 -0800202 listener.report();
203 }
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800204 }, REPORT_PERIOD - currentTimeMillis() % REPORT_PERIOD, REPORT_PERIOD);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800205
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800206 // Submit workers
Brian O'Connor6ccba962015-02-17 18:16:02 -0800207 stopped = false;
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700208 for (int i = 0; i < DEFAULT_NUM_WORKERS; i++) {
209 workers.submit(new Submitter(createIntents(numKeys, /*FIXME*/ 2, lastKey)));
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800210 }
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700211 logConfig("Started");
Brian O'Connor6ccba962015-02-17 18:16:02 -0800212 }
213
214 public void stop() {
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700215 stopped = true;
Brian O'Connor6ccba962015-02-17 18:16:02 -0800216 if (listener != null) {
217 reportTimer.cancel();
218 intentService.removeListener(listener);
219 listener = null;
220 reportTimer = null;
221 }
Brian O'Connor6ccba962015-02-17 18:16:02 -0800222 try {
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800223 workers.awaitTermination(5, TimeUnit.SECONDS);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800224 } catch (InterruptedException e) {
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800225 log.warn("Failed to stop worker", e);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800226 }
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700227 log.info("Stopped");
228 }
229
230 private List<NodeId> getNeighbors() {
231 List<NodeId> nodes = clusterService.getNodes().stream()
232 .map(ControllerNode::id)
233 .collect(Collectors.toCollection(ArrayList::new));
234 // sort neighbors by id
235 Collections.sort(nodes, (node1, node2) ->
236 node1.toString().compareTo(node2.toString()));
237 // rotate the local node to index 0
238 Collections.rotate(nodes, -1 * nodes.indexOf(clusterService.getLocalNode().id()));
Brian O'Connor4964d3d2015-03-12 20:38:10 -0700239 log.debug("neighbors (raw): {}", nodes); //TODO remove
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700240 // generate the sub-list that will contain local node and selected neighbors
241 nodes = nodes.subList(0, numNeighbors + 1);
Brian O'Connor4964d3d2015-03-12 20:38:10 -0700242 log.debug("neighbors: {}", nodes); //TODO remove
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700243 return nodes;
244 }
245
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700246 private Intent createIntent(Key key, long mac, NodeId node, Multimap<NodeId, Device> devices) {
247 // choose a random device for which this node is master
248 List<Device> deviceList = devices.get(node).stream().collect(Collectors.toList());
249 Device device = deviceList.get(RandomUtils.nextInt(deviceList.size()));
250
251 //FIXME we currently ignore the path length and always use the same device
252 TrafficSelector selector = DefaultTrafficSelector.builder()
253 .matchEthDst(MacAddress.valueOf(mac)).build();
254 TrafficTreatment treatment = DefaultTrafficTreatment.emptyTreatment();
255 ConnectPoint ingress = new ConnectPoint(device.id(), PortNumber.portNumber(1));
256 ConnectPoint egress = new ConnectPoint(device.id(), PortNumber.portNumber(2));
257
258 return new PointToPointIntent(appId, key,
259 selector, treatment,
260 ingress, egress,
261 Collections.emptyList(),
262 Intent.DEFAULT_INTENT_PRIORITY);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800263 }
264
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800265 /**
266 * Creates a specified number of intents for testing purposes.
267 *
268 * @param numberOfKeys number of intents
269 * @param pathLength path depth
270 * @param firstKey first key to attempt
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700271 * @return set of intents
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800272 */
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700273 private Set<Intent> createIntents(int numberOfKeys, int pathLength, int firstKey) {
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700274 List<NodeId> neighbors = getNeighbors();
Brian O'Connor6ccba962015-02-17 18:16:02 -0800275
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700276 Multimap<NodeId, Device> devices = ArrayListMultimap.create();
Thomas Vachuska7648d662015-03-16 11:58:30 -0700277 deviceService.getAvailableDevices()
278 .forEach(device -> devices.put(mastershipService.getMasterFor(device.id()), device));
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700279
280 // ensure that we have at least one device per neighbor
Thomas Vachuska7648d662015-03-16 11:58:30 -0700281 neighbors.forEach(node -> checkState(devices.get(node).size() > 0,
282 "There are no devices for {}", node));
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700283
284 // TODO pull this outside so that createIntent can use it
285 // prefix based on node id for keys generated on this instance
286 long keyPrefix = ((long) clusterService.getLocalNode().ip().getIp4Address().toInt()) << 32;
287
288 int maxKeysPerNode = (int) Math.ceil((double) numberOfKeys / neighbors.size());
289 Multimap<NodeId, Intent> intents = ArrayListMultimap.create();
290
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800291 for (int count = 0, k = firstKey; count < numberOfKeys; k++) {
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700292 Key key = Key.of(keyPrefix + k, appId);
293
294 NodeId leader = partitionService.getLeader(key);
295 if (!neighbors.contains(leader) || intents.get(leader).size() >= maxKeysPerNode) {
296 // Bail if we are not sending to this node or we have enough for this node
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800297 continue;
298 }
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700299 intents.put(leader, createIntent(key, keyPrefix + k, leader, devices));
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800300
301 // Bump up the counter and remember this as the last key used.
302 count++;
303 lastKey = k;
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700304 if (count % 1000 == 0) {
305 log.info("Building intents... {} (attempt: {})", count, lastKey);
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800306 }
Brian O'Connor6ccba962015-02-17 18:16:02 -0800307 }
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700308 checkState(intents.values().size() == numberOfKeys,
309 "Generated wrong number of intents");
Brian O'Connorbcfeadb2015-02-19 21:50:01 -0800310 log.info("Created {} intents", numberOfKeys);
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700311 intents.keySet().forEach(node -> log.info("\t{}\t{}", node, intents.get(node).size()));
312
313 return Sets.newHashSet(intents.values());
Brian O'Connor6ccba962015-02-17 18:16:02 -0800314 }
315
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800316 // Submits intent operations.
317 final class Submitter implements Runnable {
318
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800319 private long lastDuration;
320 private int lastCount;
321
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800322 private Set<Intent> intents = Sets.newHashSet();
323 private Set<Intent> submitted = Sets.newHashSet();
324 private Set<Intent> withdrawn = Sets.newHashSet();
325
326 private Submitter(Set<Intent> intents) {
327 this.intents = intents;
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700328 lastCount = numKeys / 4;
329 lastDuration = 1_000; // 1 second
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800330 }
331
332 @Override
333 public void run() {
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800334 prime();
335 while (!stopped) {
Brian O'Connor4964d3d2015-03-12 20:38:10 -0700336 try {
337 cycle();
338 } catch (Exception e) {
339 log.warn("Exception during cycle", e);
340 }
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800341 }
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700342 clear();
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800343 }
344
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800345 private Iterable<Intent> subset(Set<Intent> intents) {
346 List<Intent> subset = Lists.newArrayList(intents);
347 Collections.shuffle(subset);
348 return subset.subList(0, lastCount);
349 }
350
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800351 // Submits the specified intent.
352 private void submit(Intent intent) {
353 intentService.submit(intent);
354 submitted.add(intent);
355 withdrawn.remove(intent); //TODO could check result here...
356 }
357
358 // Withdraws the specified intent.
359 private void withdraw(Intent intent) {
360 intentService.withdraw(intent);
361 withdrawn.add(intent);
362 submitted.remove(intent); //TODO could check result here...
363 }
364
365 // Primes the cycle.
366 private void prime() {
367 int i = 0;
368 withdrawn.addAll(intents);
369 for (Intent intent : intents) {
370 submit(intent);
371 // only submit half of the intents to start
372 if (i++ >= intents.size() / 2) {
373 break;
374 }
375 }
376 }
377
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700378 private void clear() {
379 submitted.forEach(this::withdraw);
380 }
381
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800382 // Runs a single operation cycle.
383 private void cycle() {
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800384 //TODO consider running without rate adjustment
385 adjustRates();
386
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800387 long start = currentTimeMillis();
388 subset(submitted).forEach(this::withdraw);
389 subset(withdrawn).forEach(this::submit);
390 long delta = currentTimeMillis() - start;
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800391
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700392 if (delta > cyclePeriod * 3 || delta < 0) {
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800393 log.warn("Cycle took {} ms", delta);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800394 }
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800395
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700396 int difference = cyclePeriod - (int) delta;
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800397 if (difference > 0) {
398 delay(difference);
399 }
400
401 lastDuration = delta;
402 }
403
404 int cycleCount = 0;
Thomas Vachuska7648d662015-03-16 11:58:30 -0700405
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800406 private void adjustRates() {
Brian O'Connor4964d3d2015-03-12 20:38:10 -0700407
408 int addDelta = Math.max(1000 - cycleCount, 10);
409 double multRatio = Math.min(0.8 + cycleCount * 0.0002, 0.995);
410
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800411 //FIXME need to iron out the rate adjustment
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700412 //FIXME we should taper the adjustments over time
Brian O'Connor4964d3d2015-03-12 20:38:10 -0700413 //FIXME don't just use the lastDuration, take an average
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800414 if (++cycleCount % 5 == 0) { //TODO: maybe use a timer (we should do this every 5-10 sec)
Brian O'Connor375573b2015-03-06 01:09:43 -0800415 if (listener.requestThroughput() - listener.processedThroughput() <= 2000 && //was 500
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700416 lastDuration <= cyclePeriod) {
Brian O'Connor4964d3d2015-03-12 20:38:10 -0700417 lastCount = Math.min(lastCount + addDelta, intents.size() / 2);
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800418 } else {
Brian O'Connor4964d3d2015-03-12 20:38:10 -0700419 lastCount *= multRatio;
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800420 }
421 log.info("last count: {}, last duration: {} ms (sub: {} vs inst: {})",
422 lastCount, lastDuration, listener.requestThroughput(), listener.processedThroughput());
423 }
424
Brian O'Connor6ccba962015-02-17 18:16:02 -0800425 }
426 }
427
Thomas Vachuskaa132e3a2015-02-21 01:53:14 -0800428 // Event listener to monitor throughput.
429 final class Listener implements IntentListener {
Brian O'Connor6ccba962015-02-17 18:16:02 -0800430
Thomas Vachuska0249b532015-02-20 16:46:18 -0800431 private final Counter runningTotal = new Counter();
Brian O'Connor87ba7a72015-03-11 14:40:09 -0700432 private volatile Map<IntentEvent.Type, Counter> counters;
Brian O'Connor6ccba962015-02-17 18:16:02 -0800433
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800434 private volatile double processedThroughput = 0;
435 private volatile double requestThroughput = 0;
436
Brian O'Connor6ccba962015-02-17 18:16:02 -0800437 public Listener() {
438 counters = initCounters();
Brian O'Connor6ccba962015-02-17 18:16:02 -0800439 }
440
441 private Map<IntentEvent.Type, Counter> initCounters() {
442 Map<IntentEvent.Type, Counter> map = Maps.newHashMap();
443 for (IntentEvent.Type type : IntentEvent.Type.values()) {
444 map.put(type, new Counter());
445 }
446 return map;
447 }
448
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800449 public double processedThroughput() {
450 return processedThroughput;
451 }
452
453 public double requestThroughput() {
454 return requestThroughput;
455 }
456
Brian O'Connor6ccba962015-02-17 18:16:02 -0800457 @Override
458 public void event(IntentEvent event) {
459 if (event.subject().appId().equals(appId)) {
460 counters.get(event.type()).add(1);
461 }
462 }
463
464 public void report() {
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800465 Map<IntentEvent.Type, Counter> reportCounters = counters;
466 counters = initCounters();
467
468 // update running total and latest throughput
469 Counter installed = reportCounters.get(INSTALLED);
470 Counter withdrawn = reportCounters.get(WITHDRAWN);
471 processedThroughput = installed.throughput() + withdrawn.throughput();
Thomas Vachuska0249b532015-02-20 16:46:18 -0800472 runningTotal.add(installed.total() + withdrawn.total());
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800473
474 Counter installReq = reportCounters.get(INSTALL_REQ);
475 Counter withdrawReq = reportCounters.get(WITHDRAW_REQ);
476 requestThroughput = installReq.throughput() + withdrawReq.throughput();
477
478 // build the string to report
479 StringBuilder stringBuilder = new StringBuilder();
Brian O'Connor6ccba962015-02-17 18:16:02 -0800480 for (IntentEvent.Type type : IntentEvent.Type.values()) {
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800481 Counter counter = reportCounters.get(type);
482 stringBuilder.append(format("%s=%.2f;", type, counter.throughput()));
Brian O'Connor6ccba962015-02-17 18:16:02 -0800483 }
Thomas Vachuska0249b532015-02-20 16:46:18 -0800484 log.info("Throughput: OVERALL={}; CURRENT={}; {}",
485 format("%.2f", runningTotal.throughput()),
Brian O'Connor36ef71a2015-02-24 12:05:01 -0800486 format("%.2f", processedThroughput),
487 stringBuilder);
Thomas Vachuska7648d662015-03-16 11:58:30 -0700488
489 sampleCollector.recordSample(runningTotal.throughput(),
490 processedThroughput);
Brian O'Connor6ccba962015-02-17 18:16:02 -0800491 }
492 }
Thomas Vachuska7648d662015-03-16 11:58:30 -0700493
Brian O'Connor6ccba962015-02-17 18:16:02 -0800494}