blob: 7b7cf1b9e4ad9f637b8443bcf5c3d5b74a16813f [file] [log] [blame]
Madan Jampani5551f522016-02-23 18:27:19 -08001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2016-present Open Networking Foundation
Madan Jampani5551f522016-02-23 18:27:19 -08003 *
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.loadtest;
17
Ray Milkeyd84f89b2018-08-17 14:54:17 -070018import com.google.common.collect.Lists;
19import com.google.common.util.concurrent.RateLimiter;
20import org.apache.commons.lang.math.RandomUtils;
21import org.onlab.util.Tools;
22import org.onosproject.cfg.ComponentConfigService;
23import org.onosproject.core.ApplicationId;
24import org.onosproject.core.CoreService;
25import org.onosproject.store.service.AsyncAtomicCounter;
26import org.onosproject.store.service.StorageService;
27import org.osgi.service.component.ComponentContext;
28import org.osgi.service.component.annotations.Activate;
29import org.osgi.service.component.annotations.Component;
30import org.osgi.service.component.annotations.Deactivate;
31import org.osgi.service.component.annotations.Modified;
32import org.osgi.service.component.annotations.Reference;
33import org.osgi.service.component.annotations.ReferenceCardinality;
34import org.slf4j.Logger;
Madan Jampani5551f522016-02-23 18:27:19 -080035
36import java.util.Dictionary;
Madan Jampaniee3736e2016-03-29 23:40:39 -070037import java.util.List;
Madan Jampani5551f522016-02-23 18:27:19 -080038import java.util.concurrent.Executors;
Madan Jampaniee3736e2016-03-29 23:40:39 -070039import java.util.concurrent.ScheduledExecutorService;
Madan Jampani5551f522016-02-23 18:27:19 -080040import java.util.concurrent.Semaphore;
Madan Jampaniee3736e2016-03-29 23:40:39 -070041import java.util.concurrent.TimeUnit;
Madan Jampani5551f522016-02-23 18:27:19 -080042import java.util.concurrent.atomic.AtomicBoolean;
Madan Jampaniee3736e2016-03-29 23:40:39 -070043import java.util.concurrent.atomic.AtomicInteger;
Madan Jampani5551f522016-02-23 18:27:19 -080044import java.util.concurrent.atomic.AtomicLong;
Madan Jampaniee3736e2016-03-29 23:40:39 -070045import java.util.stream.Collectors;
Madan Jampani5551f522016-02-23 18:27:19 -080046
Ray Milkeyd84f89b2018-08-17 14:54:17 -070047import static com.google.common.base.Strings.isNullOrEmpty;
48import static org.onlab.util.Tools.get;
49import static org.slf4j.LoggerFactory.getLogger;
Madan Jampani5551f522016-02-23 18:27:19 -080050
51/**
52 * Simple application for load testing distributed consensus.
53 * <p>
54 * This application simply increments as {@link AsyncAtomicCounter} at a configurable rate.
55 */
Ray Milkeyd84f89b2018-08-17 14:54:17 -070056@Component(immediate = true, service = DistributedConsensusLoadTest.class)
Madan Jampani5551f522016-02-23 18:27:19 -080057public class DistributedConsensusLoadTest {
58
59 private final Logger log = getLogger(getClass());
60
Ray Milkeyd84f89b2018-08-17 14:54:17 -070061 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Madan Jampani5551f522016-02-23 18:27:19 -080062 protected ComponentConfigService configService;
63
Ray Milkeyd84f89b2018-08-17 14:54:17 -070064 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Madan Jampani5551f522016-02-23 18:27:19 -080065 protected StorageService storageService;
66
67 private ApplicationId appId;
68
69 private AtomicBoolean stopped = new AtomicBoolean(false);
70
Ray Milkeyd84f89b2018-08-17 14:54:17 -070071 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Madan Jampani5551f522016-02-23 18:27:19 -080072 protected CoreService coreService;
73
74 private static final int DEFAULT_RATE = 100;
Madan Jampaniee3736e2016-03-29 23:40:39 -070075 private static final int TOTAL_COUNTERS = 50;
Madan Jampani5551f522016-02-23 18:27:19 -080076
Ray Milkeyd84f89b2018-08-17 14:54:17 -070077 //@Property(name = "rate", intValue = DEFAULT_RATE,
78 // label = "Total number of increments per second to the atomic counter")
Madan Jampani5551f522016-02-23 18:27:19 -080079 protected int rate = 0;
80
Madan Jampaniee3736e2016-03-29 23:40:39 -070081 private final AtomicLong previousReportTime = new AtomicLong(0);
82 private final AtomicLong previousCount = new AtomicLong(0);
83 private final AtomicInteger increments = new AtomicInteger(0);
84 private final List<AsyncAtomicCounter> counters = Lists.newArrayList();
85 private final ScheduledExecutorService runner = Executors.newSingleThreadScheduledExecutor();
86 private final ScheduledExecutorService reporter = Executors.newSingleThreadScheduledExecutor();
Madan Jampani5551f522016-02-23 18:27:19 -080087
88 @Activate
89 public void activate(ComponentContext context) {
90 configService.registerProperties(getClass());
91 appId = coreService.registerApplication("org.onosproject.loadtest");
92 log.info("Started with {}", appId);
Madan Jampaniee3736e2016-03-29 23:40:39 -070093 for (int i = 0; i < TOTAL_COUNTERS; ++i) {
Madan Jampanid5714e02016-04-19 14:15:20 -070094 AsyncAtomicCounter counter =
95 storageService.getAsyncAtomicCounter(String.format("onos-app-loadtest-counter-%d", i));
Madan Jampaniee3736e2016-03-29 23:40:39 -070096 counters.add(counter);
97 }
98 reporter.scheduleWithFixedDelay(() -> {
99 Tools.allOf(counters.stream()
100 .map(AsyncAtomicCounter::get)
101 .collect(Collectors.toList()))
102 .whenComplete((r, e) -> {
103 if (e == null) {
104 long newCount = r.stream().reduce(Long::sum).get();
105 long currentTime = System.currentTimeMillis();
106 long delta = currentTime - previousReportTime.getAndSet(currentTime);
107 long rate = (newCount - previousCount.getAndSet(newCount)) * 1000 / delta;
108 log.info("{} updates per second", rate);
109 } else {
110 log.warn(e.getMessage());
111 }
112 });
113 }, 5, 5, TimeUnit.SECONDS);
Madan Jampani5551f522016-02-23 18:27:19 -0800114 modified(null);
115 }
116
117 private void startTest() {
118 stopped.set(false);
119 RateLimiter limiter = RateLimiter.create(rate);
120 Semaphore s = new Semaphore(100);
121 while (!stopped.get()) {
122 limiter.acquire();
123 s.acquireUninterruptibly();
Madan Jampaniee3736e2016-03-29 23:40:39 -0700124 counters.get(RandomUtils.nextInt(TOTAL_COUNTERS)).incrementAndGet().whenComplete((r, e) -> {
Madan Jampani5551f522016-02-23 18:27:19 -0800125 s.release();
Madan Jampani5551f522016-02-23 18:27:19 -0800126 if (e == null) {
Madan Jampaniee3736e2016-03-29 23:40:39 -0700127 increments.incrementAndGet();
Madan Jampani5551f522016-02-23 18:27:19 -0800128 }
129 });
130 }
131 }
132
133 private void stopTest() {
134 stopped.set(true);
135 }
136
137 @Deactivate
138 public void deactivate(ComponentContext context) {
139 configService.unregisterProperties(getClass(), false);
140 stopTest();
Madan Jampaniee3736e2016-03-29 23:40:39 -0700141 runner.shutdown();
142 reporter.shutdown();
Madan Jampani5551f522016-02-23 18:27:19 -0800143 log.info("Stopped");
144 }
145
146 @Modified
147 public void modified(ComponentContext context) {
148 int newRate = DEFAULT_RATE;
149 if (context != null) {
150 Dictionary properties = context.getProperties();
151 try {
152 String s = get(properties, "rate");
153 newRate = isNullOrEmpty(s)
154 ? rate : Integer.parseInt(s.trim());
155 } catch (Exception e) {
156 return;
157 }
158 }
159 if (newRate != rate) {
Madan Jampaniee3736e2016-03-29 23:40:39 -0700160 log.info("Per node rate changed to {}", newRate);
Madan Jampani5551f522016-02-23 18:27:19 -0800161 rate = newRate;
162 stopTest();
Madan Jampaniee3736e2016-03-29 23:40:39 -0700163 runner.execute(this::startTest);
Madan Jampani5551f522016-02-23 18:27:19 -0800164 }
165 }
166}