io.rainfall.execution.Pattern Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of rainfall-core Show documentation
Show all versions of rainfall-core Show documentation
Rainfall-core is the core of the Rainfall framework
/*
* Copyright (c) 2014-2022 Aurélien Broszniowski
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.rainfall.execution;
import io.rainfall.AssertionEvaluator;
import io.rainfall.Configuration;
import io.rainfall.Execution;
import io.rainfall.Scenario;
import io.rainfall.TestException;
import io.rainfall.WeightedOperation;
import io.rainfall.configuration.ConcurrencyConfig;
import io.rainfall.statistics.StatisticsHolder;
import io.rainfall.unit.From;
import io.rainfall.unit.Over;
import io.rainfall.unit.To;
import io.rainfall.utils.RangeMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.SECONDS;
/**
* @author Aurelien Broszniowski
*/
public class Pattern extends Execution {
private final static Logger logger = LoggerFactory.getLogger(Ramp.class);
protected final From from;
protected final To to;
protected final Over over;
protected final Function function;
public Pattern(From from, To to, Over over, Function function) {
this.from = from;
this.to = to;
this.over = over;
this.function = function;
}
@Override
public > void execute(StatisticsHolder statisticsHolder, Scenario scenario,
Map, Configuration> configurations,
List assertions) throws TestException {
ConcurrencyConfig concurrencyConfig = (ConcurrencyConfig)configurations.get(ConcurrencyConfig.class);
for (String threadpoolName : concurrencyConfig.getThreadCountMap().keySet()) {
if (concurrencyConfig.getThreadCountMap().get(threadpoolName) < from.getCount()
|| concurrencyConfig.getThreadCountMap().get(threadpoolName) < to.getCount()) {
throw new TestException(
"Concurrency config thread count for threadpool " + threadpoolName + " is lower than the Ramp parameters. [From = "
+ from.getCount() + ", To = " + to.getCount() + "]");
}
}
final ScheduledExecutorService endScheduler = Executors.newScheduledThreadPool(1);
final Map execSchedulers = concurrencyConfig.createScheduledExecutorService();
markExecutionState(scenario, ExecutionState.BEGINNING);
final AtomicBoolean doneFlag = new AtomicBoolean(false);
final List> futures = scheduleThreads(statisticsHolder, scenario, configurations, assertions, doneFlag, execSchedulers);
// Schedule the end of the execution after the time entered as parameter
final ScheduledFuture> endFuture = endScheduler.schedule(() -> {
markExecutionState(scenario, ExecutionState.ENDING);
shutdownNicely(doneFlag, execSchedulers, endScheduler);
}, over.getCount(), over.getTimeDivision().getTimeUnit());
try {
for (Future future : futures) {
future.get();
}
endFuture.get();
} catch (InterruptedException e) {
markExecutionState(scenario, ExecutionState.ENDING);
shutdownNicely(doneFlag, execSchedulers, endScheduler);
throw new TestException("Thread execution Interruption", e);
} catch (ExecutionException e) {
markExecutionState(scenario, ExecutionState.ENDING);
shutdownNicely(doneFlag, execSchedulers, endScheduler);
throw new TestException("Thread execution error", e);
}
try {
boolean success = true;
for (ExecutorService executor : execSchedulers.values()) {
boolean executorSuccess = executor.awaitTermination(60, SECONDS);
if (!executorSuccess) {
executor.shutdownNow();
success &= executor.awaitTermination(60, SECONDS);
}
}
boolean schedulerSuccess = endScheduler.awaitTermination(60, SECONDS);
if (!schedulerSuccess) {
endScheduler.shutdownNow();
success &= endScheduler.awaitTermination(60, SECONDS);
}
if (!success) {
throw new TestException("Execution of Scenario timed out.");
}
} catch (InterruptedException e) {
for (ExecutorService executor : execSchedulers.values()) {
executor.shutdownNow();
}
endScheduler.shutdownNow();
Thread.currentThread().interrupt();
throw new TestException("Execution of Scenario didn't stop correctly.", e);
}
}
List> scheduleThreads(final StatisticsHolder statisticsHolder, final Scenario scenario, final Map, Configuration> configurations, final List assertions, final AtomicBoolean doneFlag, Map executors) {
List> futures = new ArrayList<>();
int threadCountStep = to.getCount() - from.getCount() > 0 ? 1 : -1;
int lowerLimit = Math.min(from.getCount(), to.getCount());
final AtomicInteger threadCount = new AtomicInteger(from.getCount());
while (threadCount.get() != to.getCount()) {
for (final String threadpoolName : executors.keySet()) {
final RangeMap operations = scenario.getOperations().get(threadpoolName);
futures.add(executors.get(threadpoolName).schedule(() -> {
logger.info("Rainfall Ramp - Adding thread " + threadCount.get() + " at " + new Date());
Thread.currentThread().setName("Rainfall-core Operations Thread n" + threadCount.get());
while (!Thread.currentThread().isInterrupted() && !doneFlag.get()) {
operations.getNextRandom(weightRnd).getOperation().exec(statisticsHolder, configurations, assertions);
}
return null;
}, function.apply(threadCount.get() - lowerLimit), MILLISECONDS));
}
threadCount.getAndAdd(threadCountStep);
}
return futures;
}
@Override
public String toString() {
return "Ramp " + from.toString() + " "
+ to.toString() + " " + over.toString();
}
private void shutdownNicely(AtomicBoolean doneFlag, Map executors, ExecutorService scheduler) {
doneFlag.set(true);
for (ExecutorService executor : executors.values()) {
executor.shutdown();
}
scheduler.shutdown();
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy