blob: 3f8dbaf5d448b8bc5da4e07c48d57049efb34d3a [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;
Ray Milkey88dd7e22018-10-24 10:04:03 -070049import static org.onosproject.loadtest.OsgiPropertyConstants.RATE;
50import static org.onosproject.loadtest.OsgiPropertyConstants.RATE_DEFAULT;
Ray Milkeyd84f89b2018-08-17 14:54:17 -070051import static org.slf4j.LoggerFactory.getLogger;
Madan Jampani5551f522016-02-23 18:27:19 -080052
53/**
54 * Simple application for load testing distributed consensus.
55 * <p>
56 * This application simply increments as {@link AsyncAtomicCounter} at a configurable rate.
57 */
Ray Milkey88dd7e22018-10-24 10:04:03 -070058@Component(
59 immediate = true,
60 service = DistributedConsensusLoadTest.class,
61 property = {
62 RATE + ":Integer=" + RATE_DEFAULT
63 }
64)
Madan Jampani5551f522016-02-23 18:27:19 -080065public class DistributedConsensusLoadTest {
66
67 private final Logger log = getLogger(getClass());
68
Ray Milkeyd84f89b2018-08-17 14:54:17 -070069 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Madan Jampani5551f522016-02-23 18:27:19 -080070 protected ComponentConfigService configService;
71
Ray Milkeyd84f89b2018-08-17 14:54:17 -070072 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Madan Jampani5551f522016-02-23 18:27:19 -080073 protected StorageService storageService;
74
75 private ApplicationId appId;
76
77 private AtomicBoolean stopped = new AtomicBoolean(false);
78
Ray Milkeyd84f89b2018-08-17 14:54:17 -070079 @Reference(cardinality = ReferenceCardinality.MANDATORY)
Madan Jampani5551f522016-02-23 18:27:19 -080080 protected CoreService coreService;
81
Madan Jampaniee3736e2016-03-29 23:40:39 -070082 private static final int TOTAL_COUNTERS = 50;
Madan Jampani5551f522016-02-23 18:27:19 -080083
Ray Milkey88dd7e22018-10-24 10:04:03 -070084 /** Total number of increments per second to the atomic counter. */
85 protected int rate = RATE_DEFAULT;
Madan Jampani5551f522016-02-23 18:27:19 -080086
Madan Jampaniee3736e2016-03-29 23:40:39 -070087 private final AtomicLong previousReportTime = new AtomicLong(0);
88 private final AtomicLong previousCount = new AtomicLong(0);
89 private final AtomicInteger increments = new AtomicInteger(0);
90 private final List<AsyncAtomicCounter> counters = Lists.newArrayList();
91 private final ScheduledExecutorService runner = Executors.newSingleThreadScheduledExecutor();
92 private final ScheduledExecutorService reporter = Executors.newSingleThreadScheduledExecutor();
Madan Jampani5551f522016-02-23 18:27:19 -080093
94 @Activate
95 public void activate(ComponentContext context) {
96 configService.registerProperties(getClass());
97 appId = coreService.registerApplication("org.onosproject.loadtest");
98 log.info("Started with {}", appId);
Madan Jampaniee3736e2016-03-29 23:40:39 -070099 for (int i = 0; i < TOTAL_COUNTERS; ++i) {
Madan Jampanid5714e02016-04-19 14:15:20 -0700100 AsyncAtomicCounter counter =
101 storageService.getAsyncAtomicCounter(String.format("onos-app-loadtest-counter-%d", i));
Madan Jampaniee3736e2016-03-29 23:40:39 -0700102 counters.add(counter);
103 }
104 reporter.scheduleWithFixedDelay(() -> {
105 Tools.allOf(counters.stream()
106 .map(AsyncAtomicCounter::get)
107 .collect(Collectors.toList()))
108 .whenComplete((r, e) -> {
109 if (e == null) {
110 long newCount = r.stream().reduce(Long::sum).get();
111 long currentTime = System.currentTimeMillis();
112 long delta = currentTime - previousReportTime.getAndSet(currentTime);
113 long rate = (newCount - previousCount.getAndSet(newCount)) * 1000 / delta;
114 log.info("{} updates per second", rate);
115 } else {
116 log.warn(e.getMessage());
117 }
118 });
119 }, 5, 5, TimeUnit.SECONDS);
Madan Jampani5551f522016-02-23 18:27:19 -0800120 modified(null);
121 }
122
123 private void startTest() {
124 stopped.set(false);
125 RateLimiter limiter = RateLimiter.create(rate);
126 Semaphore s = new Semaphore(100);
127 while (!stopped.get()) {
128 limiter.acquire();
129 s.acquireUninterruptibly();
Madan Jampaniee3736e2016-03-29 23:40:39 -0700130 counters.get(RandomUtils.nextInt(TOTAL_COUNTERS)).incrementAndGet().whenComplete((r, e) -> {
Madan Jampani5551f522016-02-23 18:27:19 -0800131 s.release();
Madan Jampani5551f522016-02-23 18:27:19 -0800132 if (e == null) {
Madan Jampaniee3736e2016-03-29 23:40:39 -0700133 increments.incrementAndGet();
Madan Jampani5551f522016-02-23 18:27:19 -0800134 }
135 });
136 }
137 }
138
139 private void stopTest() {
140 stopped.set(true);
141 }
142
143 @Deactivate
144 public void deactivate(ComponentContext context) {
145 configService.unregisterProperties(getClass(), false);
146 stopTest();
Madan Jampaniee3736e2016-03-29 23:40:39 -0700147 runner.shutdown();
148 reporter.shutdown();
Madan Jampani5551f522016-02-23 18:27:19 -0800149 log.info("Stopped");
150 }
151
152 @Modified
153 public void modified(ComponentContext context) {
Ray Milkey88dd7e22018-10-24 10:04:03 -0700154 int newRate = RATE_DEFAULT;
Madan Jampani5551f522016-02-23 18:27:19 -0800155 if (context != null) {
156 Dictionary properties = context.getProperties();
157 try {
Ray Milkey88dd7e22018-10-24 10:04:03 -0700158 String s = get(properties, RATE);
Madan Jampani5551f522016-02-23 18:27:19 -0800159 newRate = isNullOrEmpty(s)
160 ? rate : Integer.parseInt(s.trim());
161 } catch (Exception e) {
162 return;
163 }
164 }
165 if (newRate != rate) {
Madan Jampaniee3736e2016-03-29 23:40:39 -0700166 log.info("Per node rate changed to {}", newRate);
Madan Jampani5551f522016-02-23 18:27:19 -0800167 rate = newRate;
168 stopTest();
Madan Jampaniee3736e2016-03-29 23:40:39 -0700169 runner.execute(this::startTest);
Madan Jampani5551f522016-02-23 18:27:19 -0800170 }
171 }
172}