From 3070174573f3217558d17db2a57815963a2cb027 Mon Sep 17 00:00:00 2001
From: Gaetan Boismal <gaetan.boismal@forgerock.com>
Date: Thu, 15 Jan 2015 09:26:10 +0000
Subject: [PATCH] OPENDJ-1693 OPENDJ-1694 OPENDJ-1721 (CR-5748) addrate tool new version This version includes *Bug fixes ** OPENDJ-1693: Solve NoSuchElementException by checking race conditions. ** OPENDJ-1694: Changes the age threshold feature implementation. *Improvement: Adding purge phase
---
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java | 4
opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/AddRateITCase.java | 120 +++++++-----
opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties | 5
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java | 3
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java | 318 +++++++++++++++++++++++------------
opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsITCase.java | 4
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java | 3
opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java | 73 ++++---
8 files changed, 330 insertions(+), 200 deletions(-)
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
index bd1c1b5..0271b28 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AddRate.java
@@ -21,16 +21,29 @@
* CDDL HEADER END
*
*
- * Copyright 2014 ForgeRock AS
+ * Copyright 2014-2015 ForgeRock AS
*/
package com.forgerock.opendj.ldap.tools;
+import static java.util.concurrent.TimeUnit.*;
+
+import static org.forgerock.opendj.ldap.LdapException.*;
+import static org.forgerock.opendj.ldap.ResultCode.*;
+import static org.forgerock.opendj.ldap.requests.Requests.*;
+import static org.forgerock.util.promise.Promises.*;
+
+import static com.forgerock.opendj.cli.ArgumentConstants.*;
+import static com.forgerock.opendj.cli.Utils.*;
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
+
import java.io.IOException;
import java.io.PrintStream;
import java.util.Arrays;
+import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentSkipListMap;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.forgerock.i18n.LocalizableMessage;
@@ -40,9 +53,7 @@
import org.forgerock.opendj.ldap.LdapException;
import org.forgerock.opendj.ldap.ResultCode;
import org.forgerock.opendj.ldap.ResultHandler;
-import org.forgerock.opendj.ldap.requests.AddRequest;
-import org.forgerock.opendj.ldap.requests.DeleteRequest;
-import org.forgerock.opendj.ldap.requests.Requests;
+import org.forgerock.opendj.ldap.responses.Responses;
import org.forgerock.opendj.ldap.responses.Result;
import org.forgerock.opendj.ldif.EntryGenerator;
import org.forgerock.util.promise.Promise;
@@ -57,23 +68,25 @@
import com.forgerock.opendj.cli.MultiChoiceArgument;
import com.forgerock.opendj.cli.StringArgument;
-import static org.forgerock.opendj.ldap.LdapException.*;
-
-import static com.forgerock.opendj.cli.ArgumentConstants.*;
-import static com.forgerock.opendj.cli.Utils.*;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
-
/**
* A load generation tool that can be used to load a Directory Server with Add
* and Delete requests using one or more LDAP connections.
*/
public class AddRate extends ConsoleApplication {
- private static final class AddPerformanceRunner extends PerformanceRunner {
+ @SuppressWarnings("serial")
+ private static final class AddRateExecutionEndedException extends LdapException {
+ private AddRateExecutionEndedException() {
+ super(Responses.newResult(OTHER));
+ }
+ }
+
+ private final class AddPerformanceRunner extends PerformanceRunner {
+
private final class AddStatsHandler extends UpdateStatsResultHandler<Result> {
private final String entryDN;
- private AddStatsHandler(final long currentTime, String entryDN) {
+ private AddStatsHandler(final long currentTime, final String entryDN) {
super(currentTime);
this.entryDN = entryDN;
}
@@ -87,11 +100,11 @@
long newKey;
do {
newKey = randomSeq.get().nextInt();
- } while (dnEntriesAdded.putIfAbsent(newKey, this.entryDN) != null);
+ } while (dnEntriesAdded.putIfAbsent(newKey, entryDN) != null);
break;
case FIFO:
long uniqueTime = currentTime;
- while (dnEntriesAdded.putIfAbsent(uniqueTime, this.entryDN) != null) {
+ while (dnEntriesAdded.putIfAbsent(uniqueTime, entryDN) != null) {
uniqueTime++;
}
break;
@@ -99,12 +112,13 @@
break;
}
- nbAdd.getAndIncrement();
+ recentAdds.getAndIncrement();
+ totalAdds.getAndIncrement();
+ entryCount.getAndIncrement();
}
}
private final class DeleteStatsHandler extends UpdateStatsResultHandler<Result> {
-
private DeleteStatsHandler(final long startTime) {
super(startTime);
}
@@ -112,7 +126,8 @@
@Override
public void handleResult(final Result result) {
super.handleResult(result);
- nbDelete.getAndIncrement();
+ recentDeletes.getAndIncrement();
+ entryCount.getAndDecrement();
}
}
@@ -126,96 +141,139 @@
@Override
void resetStats() {
super.resetStats();
- nbAdd.set(0);
- nbDelete.set(0);
+ recentAdds.set(0);
+ recentDeletes.set(0);
}
@Override
String[] getAdditionalColumns() {
- final int nbAddStat = nbAdd.getAndSet(0);
- final int nbDelStat = nbDelete.getAndSet(0);
- final int total = nbAddStat + nbDelStat;
+ final int adds = recentAdds.getAndSet(0);
+ final int deleteStat = recentDeletes.getAndSet(0);
+ final int total = adds + deleteStat;
- extraColumn[0] = String.format("%.2f", total > 0 ? ((double) nbAddStat / total) * 100 : 0.0);
+ extraColumn[0] = String.format("%.2f", total > 0 ? ((double) adds / total) * 100 : 0.0);
return extraColumn;
}
}
- private final class AddWorkerThread extends WorkerThread {
-
- AddWorkerThread(Connection connection, ConnectionFactory connectionFactory) {
+ private final class AddDeleteWorkerThread extends WorkerThread {
+ private AddDeleteWorkerThread(final Connection connection, final ConnectionFactory connectionFactory) {
super(connection, connectionFactory);
}
@Override
- public Promise<?, LdapException> performOperation(Connection connection, DataSource[] dataSources,
- long currentTime) {
- if (needsDelete(currentTime)) {
- DeleteRequest dr = Requests.newDeleteRequest(getDNEntryToRemove());
- ResultHandler<Result> deleteHandler = new DeleteStatsHandler(currentTime);
-
- return connection.deleteAsync(dr).onSuccess(deleteHandler).onFailure(deleteHandler);
- } else {
- return performAddOperation(connection, currentTime);
- }
- }
-
- private Promise<Result, LdapException> performAddOperation(Connection connection, long currentTime) {
+ public Promise<?, LdapException> performOperation(
+ final Connection connection, final DataSource[] dataSources, final long currentTime) {
+ startPurgeIfMaxNumberAddReached();
+ startToggleDeleteIfAgeThresholdReached(currentTime);
try {
- Entry entry;
- synchronized (generator) {
- entry = generator.readEntry();
+ String entryToRemove = getEntryToRemove(currentTime);
+ if (entryToRemove != null) {
+ return doDelete(connection, currentTime, entryToRemove);
}
- AddRequest ar = Requests.newAddRequest(entry);
- ResultHandler<Result> addHandler = new AddStatsHandler(currentTime, entry.getName().toString());
- return connection.addAsync(ar).onSuccess(addHandler).onFailure(addHandler);
- } catch (IOException e) {
- // faking an error result by notifying the Handler
- UpdateStatsResultHandler<Result> resHandler = new UpdateStatsResultHandler<Result>(currentTime);
- resHandler.handleError(newLdapException(ResultCode.OTHER, e));
- return null;
+ return doAdd(connection, currentTime);
+ } catch (final AddRateExecutionEndedException a) {
+ return newSuccessfulPromise(OTHER);
+ } catch (final IOException e) {
+ return newFailedPromise(newLdapException(OTHER, e));
}
}
- private boolean needsDelete(final long currentTime) {
- if (dnEntriesAdded.isEmpty() || delStrategy == DeleteStrategy.OFF) {
- return false;
- }
-
- switch (delThreshold) {
- case SIZE_THRESHOLD:
- return dnEntriesAdded.size() > sizeThreshold;
- case AGE_THRESHOLD:
- long olderEntryTimestamp = dnEntriesAdded.firstKey();
- return (olderEntryTimestamp + timeToWait) < currentTime;
- default:
- return false;
+ private void startToggleDeleteIfAgeThresholdReached(long currentTime) {
+ if (!toggleDelete
+ && delThreshold == DeleteThreshold.AGE_THRESHOLD
+ && !dnEntriesAdded.isEmpty()
+ && dnEntriesAdded.firstKey() + timeToWait < currentTime) {
+ setSizeThreshold(entryCount.get());
}
}
- private String getDNEntryToRemove() {
- String removedEntry = null;
-
- while (removedEntry == null) {
- long minKey = dnEntriesAdded.firstKey();
- long maxKey = dnEntriesAdded.lastKey();
- long randomIndex = Math.round(Math.random() * (maxKey - minKey) + minKey);
- Long key = dnEntriesAdded.ceilingKey(randomIndex);
-
- if (key != null) {
- removedEntry = dnEntriesAdded.remove(key);
+ private void startPurgeIfMaxNumberAddReached() {
+ AtomicBoolean purgeLatch = new AtomicBoolean();
+ if (!isPurgeBranchRunning.get()
+ && 0 < maxNbAddIterations && maxNbAddIterations < totalAdds.get()) {
+ if (purgeLatch.compareAndSet(false, true)) {
+ newPurgerThread().start();
}
}
-
- return removedEntry;
}
+ // FIXME Followings @Checkstyle:ignore tags are related to the maven-checkstyle-plugin
+ // issue related here: https://github.com/checkstyle/checkstyle/issues/5
+ // @Checkstyle:ignore
+ private String getEntryToRemove(final long currentTime) throws AddRateExecutionEndedException {
+ if (isPurgeBranchRunning.get()) {
+ return purgeEntry();
+ }
+
+ if (toggleDelete && entryCount.get() > sizeThreshold) {
+ return removeFirstAddedEntry();
+ }
+
+ return null;
+ }
+
+ // @Checkstyle:ignore
+ private String purgeEntry() throws AddRateExecutionEndedException {
+ if (!dnEntriesAdded.isEmpty()) {
+ return removeFirstAddedEntry();
+ }
+ localStopRequested = true;
+ throw new AddRateExecutionEndedException();
+ }
+
+ private String removeFirstAddedEntry() {
+ final Map.Entry<Long, String> entry = dnEntriesAdded.pollFirstEntry();
+ return entry != null ? entry.getValue() : null;
+ }
+
+ private Promise<Result, LdapException> doAdd(
+ final Connection connection, final long currentTime) throws IOException {
+ Entry entry;
+ synchronized (generator) {
+ entry = generator.readEntry();
+ }
+
+ final ResultHandler<Result> addHandler = new AddStatsHandler(currentTime, entry.getName().toString());
+ return connection.addAsync(newAddRequest(entry))
+ .onSuccess(addHandler)
+ .onFailure(addHandler);
+ }
+
+ private Promise<?, LdapException> doDelete(
+ final Connection connection, final long currentTime, final String entryToRemove) {
+ final ResultHandler<Result> deleteHandler = new DeleteStatsHandler(currentTime);
+ return connection.deleteAsync(newDeleteRequest(entryToRemove))
+ .onSuccess(deleteHandler)
+ .onFailure(deleteHandler);
+ }
}
- private final ConcurrentSkipListMap<Long, String> dnEntriesAdded =
- new ConcurrentSkipListMap<Long, String>();
+ private final class AddRateTimerThread extends TimerThread {
+ private AddRateTimerThread(final long timeToWait) {
+ super(timeToWait);
+ }
+
+ @Override
+ void performStopOperations() {
+ if (purgeEnabled && isPurgeBranchRunning.compareAndSet(false, true)) {
+ if (!isScriptFriendly()) {
+ println(LocalizableMessage.raw("Purge phase..."));
+ }
+ try {
+ joinAllWorkerThreads();
+ } catch (final InterruptedException e) {
+ throw new IllegalStateException();
+ }
+ } else if (!purgeEnabled) {
+ stopRequested = true;
+ }
+ }
+ }
+
+ private final ConcurrentSkipListMap<Long, String> dnEntriesAdded = new ConcurrentSkipListMap<Long, String>();
private final ThreadLocal<Random> randomSeq = new ThreadLocal<Random>() {
@Override
protected Random initialValue() {
@@ -226,18 +284,25 @@
private EntryGenerator generator;
private DeleteStrategy delStrategy;
private DeleteThreshold delThreshold;
- private Integer sizeThreshold;
+ private int sizeThreshold;
+ private volatile boolean toggleDelete;
private long timeToWait;
- private final AtomicInteger nbAdd = new AtomicInteger();
- private final AtomicInteger nbDelete = new AtomicInteger();
+ private int maxNbAddIterations;
+ private boolean purgeEnabled;
+ private final AtomicInteger recentAdds = new AtomicInteger();
+ private final AtomicInteger recentDeletes = new AtomicInteger();
+ private final AtomicInteger totalAdds = new AtomicInteger();
+ private final AtomicInteger entryCount = new AtomicInteger();
+ private final AtomicBoolean isPurgeBranchRunning = new AtomicBoolean();
private AddPerformanceRunner(final PerformanceRunnerOptions options) throws ArgumentException {
super(options);
+ maxIterationsArgument.setPropertyName("maxNumberOfAdd");
}
@Override
- WorkerThread newWorkerThread(Connection connection, ConnectionFactory connectionFactory) {
- return new AddWorkerThread(connection, connectionFactory);
+ WorkerThread newWorkerThread(final Connection connection, final ConnectionFactory connectionFactory) {
+ return new AddDeleteWorkerThread(connection, connectionFactory);
}
@Override
@@ -245,10 +310,23 @@
return new AddRateStatsThread();
}
- public void validate(MultiChoiceArgument<DeleteStrategy> delModeArg, IntegerArgument delSizeThresholdArg,
- IntegerArgument delAgeThresholdArg) throws ArgumentException {
+ @Override
+ TimerThread newEndTimerThread(final long timeTowait) {
+ return new AddRateTimerThread(timeTowait);
+ }
+
+ TimerThread newPurgerThread() {
+ return newEndTimerThread(0);
+ }
+
+ public void validate(final MultiChoiceArgument<DeleteStrategy> delModeArg,
+ final IntegerArgument delSizeThresholdArg, final IntegerArgument delAgeThresholdArg,
+ final BooleanArgument noPurgeArgument) throws ArgumentException {
super.validate();
delStrategy = delModeArg.getTypedValue();
+ maxNbAddIterations = maxIterationsArgument.getIntValue();
+ purgeEnabled = !noPurgeArgument.isPresent();
+
// Check for inconsistent use cases
if (delSizeThresholdArg.isPresent() && delAgeThresholdArg.isPresent()) {
throw new ArgumentException(ERR_ADDRATE_THRESHOLD_SIZE_AND_AGE.get());
@@ -263,12 +341,20 @@
delThreshold =
delAgeThresholdArg.isPresent() ? DeleteThreshold.AGE_THRESHOLD : DeleteThreshold.SIZE_THRESHOLD;
if (delThreshold == DeleteThreshold.SIZE_THRESHOLD) {
- sizeThreshold = delSizeThresholdArg.getIntValue();
+ setSizeThreshold(delSizeThresholdArg.getIntValue());
+ if (0 < maxNbAddIterations && maxNbAddIterations < sizeThreshold) {
+ throw new ArgumentException(ERR_ADDRATE_SIZE_THRESHOLD_LOWER_THAN_ITERATIONS.get());
+ }
} else {
- timeToWait = delAgeThresholdArg.getIntValue() * 1000000000L;
+ timeToWait = NANOSECONDS.convert(delAgeThresholdArg.getIntValue(), SECONDS);
}
}
}
+
+ private void setSizeThreshold(int entriesSizeThreshold) {
+ sizeThreshold = entriesSizeThreshold;
+ toggleDelete = true;
+ }
}
private enum DeleteStrategy {
@@ -280,6 +366,11 @@
}
private static final int EXIT_CODE_SUCCESS = 0;
+ private static final int DEFAULT_SIZE_THRESHOLD = 10000;
+ /** The minimum time to wait before starting add/delete phase (in seconds). */
+ private static final int AGE_THRESHOLD_LOWERBOUND = 1;
+ /** The minimum number of entries to add before starting add/delete phase. */
+ private static final int SIZE_THRESHOLD_LOWERBOUND = 1;
/**
* The main method for AddRate tool.
@@ -293,14 +384,13 @@
}
private BooleanArgument verbose;
-
private BooleanArgument scriptFriendly;
private AddRate() {
// Nothing to do
}
- AddRate(PrintStream out, PrintStream err) {
+ AddRate(final PrintStream out, final PrintStream err) {
super(out, err);
}
@@ -328,23 +418,24 @@
final ArgumentParser argParser =
new ArgumentParser(AddRate.class.getName(), toolDescription, false, true, 1, 1, "template-file-path");
- ConnectionFactoryProvider connectionFactoryProvider;
- ConnectionFactory connectionFactory;
- AddPerformanceRunner runner;
+ final ConnectionFactoryProvider connectionFactoryProvider;
+ final ConnectionFactory connectionFactory;
+ final AddPerformanceRunner runner;
/* Entries generation parameters */
- IntegerArgument randomSeedArg;
- StringArgument resourcePathArg;
- StringArgument constantsArg;
+ final IntegerArgument randomSeedArg;
+ final StringArgument resourcePathArg;
+ final StringArgument constantsArg;
/* addrate specifics arguments */
- MultiChoiceArgument<DeleteStrategy> deleteMode;
- IntegerArgument deleteSizeThreshold;
- IntegerArgument deleteAgeThreshold;
+ final MultiChoiceArgument<DeleteStrategy> deleteMode;
+ final IntegerArgument deleteSizeThreshold;
+ final IntegerArgument deleteAgeThreshold;
+ final BooleanArgument noPurgeArgument;
try {
Utils.setDefaultPerfToolProperties();
- PerformanceRunnerOptions options = new PerformanceRunnerOptions(argParser, this);
+ final PerformanceRunnerOptions options = new PerformanceRunnerOptions(argParser, this);
options.setSupportsGeneratorArgument(false);
connectionFactoryProvider = new ConnectionFactoryProvider(argParser, this);
@@ -377,15 +468,21 @@
argParser.addArgument(deleteMode);
deleteSizeThreshold =
- new IntegerArgument("deletesizethreshold", 's', "deleteSizeThreshold", false, true,
- INFO_DELETESIZETHRESHOLD_PLACEHOLDER.get(), INFO_ADDRATE_DESCRIPTION_DELETESIZETHRESHOLD.get());
- deleteSizeThreshold.setDefaultValue(String.valueOf(10000));
+ new IntegerArgument("deletesizethreshold", 's', "deleteSizeThreshold", false, false, true,
+ INFO_DELETESIZETHRESHOLD_PLACEHOLDER.get(), DEFAULT_SIZE_THRESHOLD, "deleteSizeThreshold", true,
+ SIZE_THRESHOLD_LOWERBOUND, false, Integer.MAX_VALUE,
+ INFO_ADDRATE_DESCRIPTION_DELETESIZETHRESHOLD.get());
argParser.addArgument(deleteSizeThreshold);
deleteAgeThreshold =
new IntegerArgument("deleteagethreshold", 'a', "deleteAgeThreshold", false, true,
- INFO_DELETEAGETHRESHOLD_PLACEHOLDER.get(), INFO_ADDRATE_DESCRIPTION_DELETEAGETHRESHOLD.get());
+ INFO_DELETEAGETHRESHOLD_PLACEHOLDER.get(), true, AGE_THRESHOLD_LOWERBOUND, false,
+ Integer.MAX_VALUE, INFO_ADDRATE_DESCRIPTION_DELETEAGETHRESHOLD.get());
+ deleteAgeThreshold.setPropertyName(deleteAgeThreshold.getLongIdentifier());
argParser.addArgument(deleteAgeThreshold);
+
+ noPurgeArgument = new BooleanArgument("nopurge", 'n', "noPurge", INFO_ADDRATE_DESCRIPTION_NOPURGE.get());
+ argParser.addArgument(noPurgeArgument);
} catch (final ArgumentException ae) {
errPrintln(ERR_CANNOT_INITIALIZE_ARGS.get(ae.getMessage()));
return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
@@ -400,32 +497,31 @@
}
connectionFactory = connectionFactoryProvider.getAuthenticatedConnectionFactory();
- runner.validate(deleteMode, deleteSizeThreshold, deleteAgeThreshold);
+ runner.validate(deleteMode, deleteSizeThreshold, deleteAgeThreshold, noPurgeArgument);
} catch (final ArgumentException ae) {
- final LocalizableMessage message = ERR_ERROR_PARSING_ARGS.get(ae.getMessage());
- errPrintln(message);
+ errPrintln(ERR_ERROR_PARSING_ARGS.get(ae.getMessage()));
errPrintln(argParser.getUsageMessage());
return ResultCode.CLIENT_SIDE_PARAM_ERROR.intValue();
}
final String templatePath = argParser.getTrailingArguments().get(0);
-
runner.generator =
MakeLDIF.createGenerator(templatePath, resourcePathArg, randomSeedArg, constantsArg, false, this);
+ Runtime.getRuntime().addShutdownHook(runner.newPurgerThread());
return runner.run(connectionFactory);
}
- private void addCommonArguments(ArgumentParser argParser) throws ArgumentException {
- StringArgument propertiesFileArgument = CommonArguments.getPropertiesFile();
+ private void addCommonArguments(final ArgumentParser argParser) throws ArgumentException {
+ final StringArgument propertiesFileArgument = CommonArguments.getPropertiesFile();
argParser.addArgument(propertiesFileArgument);
argParser.setFilePropertiesArgument(propertiesFileArgument);
- BooleanArgument noPropertiesFileArgument = CommonArguments.getNoPropertiesFile();
+ final BooleanArgument noPropertiesFileArgument = CommonArguments.getNoPropertiesFile();
argParser.addArgument(noPropertiesFileArgument);
argParser.setNoPropertiesFileArgument(noPropertiesFileArgument);
- BooleanArgument showUsage = CommonArguments.getShowUsage();
+ final BooleanArgument showUsage = CommonArguments.getShowUsage();
argParser.addArgument(showUsage);
argParser.setUsageArgument(showUsage, getOutputStream());
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
index 35fddee..244a954 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/AuthRate.java
@@ -22,7 +22,7 @@
*
*
* Copyright 2010 Sun Microsystems, Inc.
- * Portions Copyright 2011-2014 ForgeRock AS
+ * Portions Copyright 2011-2015 ForgeRock AS
*/
package com.forgerock.opendj.ldap.tools;
@@ -178,6 +178,7 @@
returnedPromise = performBind(connection, data);
}
+ incrementIterationCount();
return returnedPromise.onSuccess(new UpdateStatsResultHandler<BindResult>(startTime)).onFailure(
new BindUpdateStatsResultHandler(startTime));
}
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
index 24c0947..98117f8 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/ModRate.java
@@ -22,7 +22,7 @@
*
*
* Copyright 2010 Sun Microsystems, Inc.
- * Portions Copyright 2011-2014 ForgeRock AS
+ * Portions Copyright 2011-2015 ForgeRock AS
*/
package com.forgerock.opendj.ldap.tools;
@@ -74,6 +74,7 @@
mr = newModifyRequest(data);
ResultHandler<Result> modRes = new UpdateStatsResultHandler<Result>(startTime);
+ incrementIterationCount();
return connection.modifyAsync(mr).onSuccess(modRes).onFailure(modRes);
}
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java
index 50e0795..36677e7 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/PerformanceRunner.java
@@ -22,7 +22,7 @@
*
*
* Copyright 2010 Sun Microsystems, Inc.
- * Portions Copyright 2011-2014 ForgeRock AS.
+ * Portions Copyright 2011-2015 ForgeRock AS.
*/
package com.forgerock.opendj.ldap.tools;
@@ -440,6 +440,7 @@
return EMPTY_STRINGS;
}
+ /** Resets both general and recent statistic indicators. */
void resetStats() {
failedCount = 0;
operationCount = 0;
@@ -451,13 +452,17 @@
}
}
- private class TimerThread extends Thread {
+ class TimerThread extends Thread {
private final long timeToWait;
- public TimerThread(long timeToWait) {
+ TimerThread(long timeToWait) {
this.timeToWait = timeToWait;
}
+ void performStopOperations() {
+ stopRequested = true;
+ }
+
@Override
public void run() {
try {
@@ -465,7 +470,7 @@
} catch (InterruptedException e) {
throw new IllegalStateException(e);
} finally {
- stopRequested = true;
+ performStopOperations();
}
}
}
@@ -512,6 +517,7 @@
private int count;
private final Connection connection;
private final ConnectionFactory connectionFactory;
+ boolean localStopRequested;
WorkerThread(final Connection connection, final ConnectionFactory connectionFactory) {
super("Worker Thread");
@@ -526,11 +532,11 @@
public void run() {
Promise<?, LdapException> promise;
Connection connection;
-
final double targetTimeInMS = 1000.0 / (targetThroughput / (double) (numThreads * numConnections));
double sleepTimeInMS = 0;
- long start;
- while (!stopRequested && !(maxIterations > 0 && count >= maxIterations)) {
+
+ while (!stopRequested && !localStopRequested
+ && (maxIterations <= 0 || count < maxIterations)) {
if (this.connection == null) {
try {
connection = connectionFactory.getConnectionAsync().getOrThrow();
@@ -565,15 +571,12 @@
}
}
- start = System.nanoTime();
+ long start = System.nanoTime();
promise = performOperation(connection, dataSources.get(), start);
operationRecentCount.getAndIncrement();
- count++;
if (!isAsync) {
try {
- if (promise != null) {
- promise.getOrThrow();
- }
+ promise.getOrThrow();
} catch (final InterruptedException e) {
// Ignore and check stop requested
continue;
@@ -607,6 +610,10 @@
}
}
}
+
+ void incrementIterationCount() {
+ count++;
+ }
}
private static final String[] EMPTY_STRINGS = new String[0];
@@ -636,10 +643,10 @@
};
- private volatile boolean stopRequested;
+ int numThreads;
+ int numConnections;
+ volatile boolean stopRequested;
private volatile boolean isWarmingUp;
- private int numThreads;
- private int numConnections;
private int targetThroughput;
private int maxIterations;
/** Warm-up duration time in ms. **/
@@ -650,7 +657,6 @@
private boolean noRebind;
private int statsInterval;
private final IntegerArgument numThreadsArgument;
- private final IntegerArgument maxIterationsArgument;
private final IntegerArgument maxDurationArgument;
private final IntegerArgument statsIntervalArgument;
private final IntegerArgument targetThroughputArgument;
@@ -660,8 +666,11 @@
private final BooleanArgument noRebindArgument;
private final BooleanArgument asyncArgument;
private final StringArgument arguments;
+ protected final IntegerArgument maxIterationsArgument;
protected final IntegerArgument warmUpArgument;
+ private final List<Thread> workerThreads = new ArrayList<Thread>();
+
PerformanceRunner(final PerformanceRunnerOptions options) throws ArgumentException {
ArgumentParser argParser = options.getArgumentParser();
@@ -686,8 +695,8 @@
maxIterationsArgument =
new IntegerArgument("maxIterations", 'm', "maxIterations", false, false, true,
- LocalizableMessage.raw("{maxIterations}"), 0, null, LocalizableMessage
- .raw("Max iterations, 0 for unlimited"));
+ LocalizableMessage.raw("{maxIterations}"), 0, null,
+ LocalizableMessage.raw("Max iterations, 0 for unlimited"));
maxIterationsArgument.setPropertyName("maxIterations");
argParser.addArgument(maxIterationsArgument);
@@ -828,19 +837,19 @@
final DataSource[] getDataSources() {
if (dataSourcePrototypes == null) {
- throw new IllegalStateException(
- "dataSources are null - validate() must be called first");
+ throw new IllegalStateException("dataSources are null - validate() must be called first");
}
return dataSourcePrototypes;
}
- abstract WorkerThread newWorkerThread(final Connection connection,
- final ConnectionFactory connectionFactory);
-
+ abstract WorkerThread newWorkerThread(final Connection connection, final ConnectionFactory connectionFactory);
abstract StatsThread newStatsThread();
+ TimerThread newEndTimerThread(final long timeTowait) {
+ return new TimerThread(timeTowait);
+ }
+
final int run(final ConnectionFactory connectionFactory) {
- final List<Thread> threads = new ArrayList<Thread>();
final List<Connection> connections = new ArrayList<Connection>();
Connection connection = null;
@@ -854,13 +863,13 @@
}
for (int j = 0; j < numThreads; j++) {
final Thread thread = newWorkerThread(connection, connectionFactory);
- threads.add(thread);
+ workerThreads.add(thread);
thread.start();
}
}
if (maxDurationTime > 0) {
- new TimerThread(maxDurationTime).start();
+ newEndTimerThread(maxDurationTime).start();
}
final StatsThread statsThread = newStatsThread();
@@ -873,11 +882,9 @@
statsThread.resetStats();
isWarmingUp = false;
}
- statsThread.start();
- for (final Thread t : threads) {
- t.join();
- }
+ statsThread.start();
+ joinAllWorkerThreads();
stopRequested = true;
statsThread.join();
} catch (final InterruptedException e) {
@@ -891,4 +898,10 @@
return 0;
}
+
+ protected void joinAllWorkerThreads() throws InterruptedException {
+ for (final Thread t : workerThreads) {
+ t.join();
+ }
+ }
}
diff --git a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
index 114c9ae..973337a 100644
--- a/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
+++ b/opendj-ldap-toolkit/src/main/java/com/forgerock/opendj/ldap/tools/SearchRate.java
@@ -22,7 +22,7 @@
*
*
* Copyright 2010 Sun Microsystems, Inc.
- * Portions Copyright 2011-2014 ForgeRock AS
+ * Portions Copyright 2011-2015 ForgeRock AS
*/
package com.forgerock.opendj.ldap.tools;
@@ -137,7 +137,7 @@
}
final SearchStatsHandler handler = new SearchStatsHandler(startTime);
-
+ incrementIterationCount();
return connection.searchAsync(sr, handler).onSuccess(handler).onFailure(handler);
}
}
diff --git a/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties b/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties
index ed2f4e7..1bbba1d 100755
--- a/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties
+++ b/opendj-ldap-toolkit/src/main/resources/com/forgerock/opendj/ldap/tools/tools.properties
@@ -22,7 +22,7 @@
#
#
# Copyright 2010 Sun Microsystems, Inc.
-# Portions copyright 2012-2014 ForgeRock AS.
+# Portions copyright 2012-2015 ForgeRock AS.
#
#
# Utility messages
@@ -536,3 +536,6 @@
specified, but only only one at a time is supported
ERR_ADDRATE_DELMODE_RAND_THRESHOLD_AGE=A age based deletion threshold should not be used \
with a random deletion mode
+ERR_ADDRATE_SIZE_THRESHOLD_LOWER_THAN_ITERATIONS=The size threshold must be lower than \
+ the maximum number of add operations
+INFO_ADDRATE_DESCRIPTION_NOPURGE=Disable the purge phase when the tool stops.
diff --git a/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/AddRateITCase.java b/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/AddRateITCase.java
index cabd601..eb6b999 100644
--- a/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/AddRateITCase.java
+++ b/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/AddRateITCase.java
@@ -21,55 +21,54 @@
* CDDL HEADER END
*
*
- * Copyright 2014 ForgeRock AS.
+ * Copyright 2014-2015 ForgeRock AS.
*/
package com.forgerock.opendj.ldap.tools;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.ERR_ADDRATE_DELMODE_OFF_THRESHOLD_ON;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.ERR_ADDRATE_DELMODE_RAND_THRESHOLD_AGE;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.ERR_ADDRATE_THRESHOLD_SIZE_AND_AGE;
-import static com.forgerock.opendj.ldap.tools.ToolsMessages.INFO_ADDRATE_TOOL_DESCRIPTION;
-import static org.fest.assertions.Assertions.assertThat;
-import static org.forgerock.util.Utils.closeSilently;
+import static org.fest.assertions.Assertions.*;
+import static org.forgerock.util.Utils.*;
+
+import static com.forgerock.opendj.ldap.tools.ToolsMessages.*;
import java.io.PrintStream;
import org.forgerock.opendj.ldap.ByteStringBuilder;
import org.forgerock.opendj.ldap.TestCaseUtils;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
public class AddRateITCase extends ToolsITCase {
+
private static final String TEMPLATE_NAME = "addrate.template";
private static final String ADD_PERCENT_TEXT = "Add%";
private static final int THROUGHPUT_COLUMN = 1;
private static final int ERR_PER_SEC_COLUMN_NUMBER = 8;
- @DataProvider
- public Object[][] addRateArgs() throws Exception {
- String[] commonArgs =
- args("-h", TestCaseUtils.getServerSocketAddress().getHostName(),
- "-p", Integer.toString(TestCaseUtils.getServerSocketAddress().getPort()),
- "-c", "1", "-t", "1", "-i", "1", "-m", "1000", "-S");
+ private ByteStringBuilder out;
+ private ByteStringBuilder err;
+ private PrintStream outStream;
+ private PrintStream errStream;
- return new Object[][] {
- // Check if the help message is correctly prompted
- { args("-H"), INFO_ADDRATE_TOOL_DESCRIPTION.get(), "" },
+ @BeforeMethod
+ private void refreshStreams() {
+ out = new ByteStringBuilder();
+ err = new ByteStringBuilder();
+ outStream = new PrintStream(out.asOutputStream());
+ errStream = new PrintStream(err.asOutputStream());
+ }
- // Should report inconsistent use of options
- { args(commonArgs, "-C", "off", "-a", "3", TEMPLATE_NAME), "",
- ERR_ADDRATE_DELMODE_OFF_THRESHOLD_ON.get() },
- { args(commonArgs, "-C", "off", "-s", "30000", TEMPLATE_NAME), "",
- ERR_ADDRATE_DELMODE_OFF_THRESHOLD_ON.get() },
- { args(commonArgs, "-C", "fifo", "-a", "3", "-s", "20000", TEMPLATE_NAME), "",
- ERR_ADDRATE_THRESHOLD_SIZE_AND_AGE.get() },
- { args(commonArgs, "-C", "random", "-a", "3", TEMPLATE_NAME), "",
- ERR_ADDRATE_DELMODE_RAND_THRESHOLD_AGE.get() },
+ @AfterMethod
+ private void closeStreams() {
+ closeSilently(outStream, errStream);
+ }
- // Correct test case
- { args(commonArgs, "-s", "10", TEMPLATE_NAME), ADD_PERCENT_TEXT, "" },
-
- };
+ private String[] commonsArgs() {
+ return new String[] {
+ "-h", TestCaseUtils.getServerSocketAddress().getHostName(),
+ "-p", Integer.toString(TestCaseUtils.getServerSocketAddress().getPort()),
+ "-c", "1", "-t", "1", "-i", "1", "-m", "100", "-S"};
}
public String[] args(String[] startLine, String... args) {
@@ -81,34 +80,51 @@
return res;
}
- @Test(dataProvider = "addRateArgs")
- public void testITAddRate(String[] arguments, Object expectedOut, Object expectedErr) throws Exception {
- ByteStringBuilder out = new ByteStringBuilder();
- ByteStringBuilder err = new ByteStringBuilder();
+ @DataProvider
+ public Object[][] invalidAddRateArgs() throws Exception {
+ return new Object[][] {
+ // Should report inconsistent use of options
+ { args("-C", "off", "-a", "3", TEMPLATE_NAME), ERR_ADDRATE_DELMODE_OFF_THRESHOLD_ON.get() },
+ { args("-C", "off", "-s", "30000", TEMPLATE_NAME), ERR_ADDRATE_DELMODE_OFF_THRESHOLD_ON.get() },
+ { args("-C", "fifo", "-a", "3", "-s", "20000", TEMPLATE_NAME), ERR_ADDRATE_THRESHOLD_SIZE_AND_AGE.get() },
+ { args("-C", "random", "-a", "3", TEMPLATE_NAME), ERR_ADDRATE_DELMODE_RAND_THRESHOLD_AGE.get() },
+ { args("-s", "999", TEMPLATE_NAME), ERR_ADDRATE_SIZE_THRESHOLD_LOWER_THAN_ITERATIONS.get() }
+ };
+ }
- PrintStream outStream = new PrintStream(out.asOutputStream());
- PrintStream errStream = new PrintStream(err.asOutputStream());
+ @Test(dataProvider = "invalidAddRateArgs")
+ public void addRateExceptions(String[] arguments, Object expectedErr) throws Exception {
+ AddRate addRate = new AddRate(outStream, errStream);
+ int retCode = addRate.run(args(commonsArgs(), arguments));
+ checkOuputStreams(out, err, "", expectedErr);
+ assertThat(retCode).isNotEqualTo(0);
+ }
- try {
- AddRate addRate = new AddRate(outStream, errStream);
- addRate.run(arguments);
+ @Test
+ public void addRateDisplaysHelp() throws Exception {
+ AddRate addRate = new AddRate(outStream, errStream);
+ int retCode = addRate.run(args("-H"));
+ checkOuputStreams(out, err, INFO_ADDRATE_TOOL_DESCRIPTION.get(), "");
+ assertThat(retCode).isEqualTo(0);
+ }
- checkOuputStreams(out, err, expectedOut, expectedErr);
- String outContent = out.toString();
+ @Test(timeOut = 10000)
+ public void addRateSimpleRun() throws Exception {
+ AddRate addRate = new AddRate(outStream, errStream);
+ int retCode = addRate.run(args(commonsArgs(), "-s", "10", TEMPLATE_NAME));
+ checkOuputStreams(out, err, ADD_PERCENT_TEXT, "");
+ assertThat(retCode).isEqualTo(0);
+ String outContent = out.toString();
- if (outContent.contains(ADD_PERCENT_TEXT)) {
- // Check that there was no error in search
- String[] addRateResLines = outContent.split(System.getProperty("line.separator"));
- // Skip header line
- for (int i = 1; i < addRateResLines.length; i++) {
- String[] addhRateLineData = addRateResLines[i].split(",");
- assertThat(addhRateLineData[ERR_PER_SEC_COLUMN_NUMBER].trim()).isEqualTo("0.0");
- assertThat(addhRateLineData[THROUGHPUT_COLUMN].trim()).isNotEqualTo("0.0");
- }
-
+ if (outContent.contains(ADD_PERCENT_TEXT)) {
+ // Check that there was no error
+ String[] addRateResLines = outContent.split(System.getProperty("line.separator"));
+ // Skip header line
+ for (int i = 1; i < addRateResLines.length; i++) {
+ String[] lineData = addRateResLines[i].split(",");
+ assertThat(lineData[ERR_PER_SEC_COLUMN_NUMBER].trim()).isEqualTo("0.0");
+ assertThat(lineData[THROUGHPUT_COLUMN].trim()).isNotEqualTo("0.0");
}
- } finally {
- closeSilently(outStream, errStream);
}
}
diff --git a/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsITCase.java b/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsITCase.java
index faf7e1e..c2793df 100644
--- a/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsITCase.java
+++ b/opendj-ldap-toolkit/src/test/java/com/forgerock/opendj/ldap/tools/ToolsITCase.java
@@ -21,7 +21,7 @@
* CDDL HEADER END
*
*
- * Copyright 2014 ForgeRock AS.
+ * Copyright 2014-2015 ForgeRock AS.
*/
package com.forgerock.opendj.ldap.tools;
@@ -72,7 +72,7 @@
checkOutputStream(err, expectedError);
}
- private void checkOutputStream(ByteStringBuilder out, Object expectedOutput) {
+ protected void checkOutputStream(ByteStringBuilder out, Object expectedOutput) {
String lineSeparator = System.getProperty("line.separator");
String toCompare = expectedOutput.toString();
--
Gitblit v1.10.0