From 9bcbad89194862ef37832b9936ba8fc5fa58c47b Mon Sep 17 00:00:00 2001 From: Keith Wall Date: Fri, 7 Mar 2014 17:24:48 +0000 Subject: QPID-5612: JMS benchmarking tool [Part of the existing Java Broker Performance Test suite] Simple tool that uses the existing performance test suite to give a message throughput statistics (msg/s) and (Kbytes/s) for a use case involving a producer/consumer (on separate connections) with persistent messages on transactional sessions. The test scales the number of connections through 1, 2, 5 and 10 and reports separate statistics for each. The duration of the test and message size can be overridden from the command line via system properties -Dqpid.disttest.duration and -Dqpid.disttest.messageSize respectively. git-svn-id: https://svn.apache.org/repos/asf/qpid/trunk@1575336 13f79535-47bb-0310-9956-ffa450edef68 --- .../java/org/apache/qpid/disttest/Benchmark.java | 246 +++++++++++++++++++++ .../org/apache/qpid/disttest/ControllerRunner.java | 14 +- .../apache/qpid/disttest/ResultsFileWriter.java | 112 ---------- .../disttest/controller/config/ConfigReader.java | 25 ++- .../config/JavaScriptConfigEvaluator.java | 27 ++- .../disttest/controller/config/ProducerConfig.java | 42 +++- .../disttest/results/BenchmarkResultWriter.java | 68 ++++++ .../qpid/disttest/results/ResultsCsvWriter.java | 115 ++++++++++ .../qpid/disttest/results/ResultsWriter.java | 34 +++ .../java/perftests/src/main/resources/Benchmark.js | 77 +++++++ .../qpid/disttest/ResultsFileWriterTest.java | 85 ------- .../config/JavaScriptConfigEvaluatorTest.java | 29 +++ .../controller/config/ProducerConfigTest.java | 15 ++ .../disttest/results/ResultsFileWriterTest.java | 86 +++++++ 14 files changed, 764 insertions(+), 211 deletions(-) create mode 100644 qpid/java/perftests/src/main/java/org/apache/qpid/disttest/Benchmark.java delete mode 100644 qpid/java/perftests/src/main/java/org/apache/qpid/disttest/ResultsFileWriter.java create mode 100644 qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/BenchmarkResultWriter.java create mode 100644 qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/ResultsCsvWriter.java create mode 100644 qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/ResultsWriter.java create mode 100644 qpid/java/perftests/src/main/resources/Benchmark.js delete mode 100644 qpid/java/perftests/src/test/java/org/apache/qpid/disttest/ResultsFileWriterTest.java create mode 100644 qpid/java/perftests/src/test/java/org/apache/qpid/disttest/results/ResultsFileWriterTest.java (limited to 'qpid/java') diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/Benchmark.java b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/Benchmark.java new file mode 100644 index 0000000000..4ebbaf7da3 --- /dev/null +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/Benchmark.java @@ -0,0 +1,246 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.disttest; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import javax.naming.Context; +import javax.naming.InitialContext; + +import org.apache.qpid.disttest.controller.Controller; +import org.apache.qpid.disttest.controller.ResultsForAllTests; +import org.apache.qpid.disttest.controller.config.Config; +import org.apache.qpid.disttest.controller.config.ConfigReader; +import org.apache.qpid.disttest.jms.ControllerJmsDelegate; +import org.apache.qpid.disttest.results.BenchmarkResultWriter; +import org.apache.qpid.disttest.results.ResultsWriter; +import org.apache.qpid.disttest.results.aggregation.Aggregator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Performs a performance benchmark using the performance test suite. The performance test + * script (Benchmark.js) is normally found on the classpath. + * + * Typical command line usage: + * + * java -cp ".:./lib/*" -Dqpid.disttest.duration=1000 -Dqpid.disttest.messageSize=2048 -Dqpid.dest_syntax=BURL + * org.apache.qpid.disttest.Benchmark + * report-message-totals=false jndi-config=etc/perftests-jndi.properties + * + * The classpath must contain the JMS client and the performance test JAR. + * + */ +public class Benchmark +{ + private static final Logger LOGGER = LoggerFactory.getLogger(Benchmark.class); + + private static final String REPORT_MESSAGE_TOTALS = "report-message-totals"; + private static final String JNDI_CONFIG_PROP = "jndi-config"; + private static final String JNDI_CONFIG_DEFAULT = "perftests-jndi.properties"; + private static final String TEST_CONFIG_PROP = "test-config"; + + @SuppressWarnings("serial") + private Map _cliOptions = new HashMap() + {{ + put(JNDI_CONFIG_PROP, JNDI_CONFIG_DEFAULT); + put(TEST_CONFIG_PROP, "/Benchmark.js"); + put(REPORT_MESSAGE_TOTALS, "false"); + }}; + + private final ConfigFileHelper _configFileHelper = new ConfigFileHelper(); + + private final Aggregator _aggregator = new Aggregator(); + + public static void main(String[] args) throws Exception + { + Benchmark benchmark = new Benchmark(); + benchmark.parseArgumentsIntoConfig(args); + benchmark.doBenchMark(); + } + + private void parseArgumentsIntoConfig(String[] args) + { + ArgumentParser argumentParser = new ArgumentParser(); + argumentParser.parseArgumentsIntoConfig(getCliOptions(), args); + } + + + private Context getContext() + { + String jndiConfig = getJndiConfig(); + try + { + final Properties properties = new Properties(); + properties.load(new FileInputStream(jndiConfig)); + return new InitialContext(properties); + } + catch (Exception e) + { + throw new DistributedTestException("Exception while loading JNDI properties from '" + jndiConfig + "'", e); + } + } + + + private void doBenchMark() throws Exception + { + Context context = getContext(); + ControllerJmsDelegate jmsDelegate = new ControllerJmsDelegate(context); + + try + { + runTests(jmsDelegate); + } + finally + { + jmsDelegate.closeConnections(); + } + } + + private String getJndiConfig() + { + return getCliOptions().get(JNDI_CONFIG_PROP); + } + + private boolean getReportMessageTotals() + { + return Boolean.parseBoolean(getCliOptions().get(REPORT_MESSAGE_TOTALS)); + } + + private Map getCliOptions() + { + return _cliOptions; + } + private void runTests(ControllerJmsDelegate jmsDelegate) + { + Controller controller = new Controller(jmsDelegate, DistributedTestConstants.REGISTRATION_TIMEOUT, DistributedTestConstants.COMMAND_RESPONSE_TIMEOUT); + + String testConfigPath = getCliOptions().get(TEST_CONFIG_PROP); + List testConfigFiles = _configFileHelper.getTestConfigFiles(testConfigPath); + createClients(testConfigFiles); + + try + { + List results = new ArrayList(); + + for (String testConfigFile : testConfigFiles) + { + final Config testConfig = buildTestConfigFrom(testConfigFile); + controller.setConfig(testConfig); + + controller.awaitClientRegistrations(); + + LOGGER.info("Running test : " + testConfigFile); + ResultsForAllTests testResult = runTest(controller, testConfigFile); + results.add(testResult); + } + } + catch(Exception e) + { + LOGGER.error("Problem running test", e); + } + finally + { + controller.stopAllRegisteredClients(); + } + } + + private ResultsForAllTests runTest(Controller controller, String testConfigFile) + { + ResultsWriter _resultsWriter = new BenchmarkResultWriter(getReportMessageTotals()); + + final Config testConfig = buildTestConfigFrom(testConfigFile); + controller.setConfig(testConfig); + + ResultsForAllTests rawResultsForAllTests = controller.runAllTests(); + ResultsForAllTests resultsForAllTests = _aggregator.aggregateResults(rawResultsForAllTests); + + _resultsWriter.writeResults(resultsForAllTests, testConfigFile); + + return resultsForAllTests; + } + + private void createClients(final List testConfigFiles) + { + int maxNumberOfClients = 0; + for (String testConfigFile : testConfigFiles) + { + final Config testConfig = buildTestConfigFrom(testConfigFile); + final int numClients = testConfig.getTotalNumberOfClients(); + maxNumberOfClients = Math.max(numClients, maxNumberOfClients); + } + + //we must create the required test clients, running in single-jvm mode + for (int i = 1; i <= maxNumberOfClients; i++) + { + ClientRunner clientRunner = new ClientRunner(); + clientRunner.setJndiPropertiesFileLocation(getJndiConfig()); + clientRunner.runClients(); + } + } + + private Config buildTestConfigFrom(String testConfigFile) + { + ConfigReader configReader = new ConfigReader(); + Config testConfig; + InputStream configStream = null; + try + { + configStream = getClass().getResourceAsStream(testConfigFile); + if (configStream != null) + { + testConfig = configReader.readConfig(new InputStreamReader(configStream), testConfigFile.endsWith(".js")); + } + else + { + testConfig = configReader.getConfigFromFile(testConfigFile); + } + } + catch (IOException e) + { + throw new DistributedTestException("Exception while loading test config from '" + + testConfigFile + "'. Tried both classpath and filesystem", e); + } + finally + { + if (configStream != null) + { + try + { + configStream.close(); + } + catch (IOException e) + { + } + } + } + + return testConfig; + } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/ControllerRunner.java b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/ControllerRunner.java index 449130a328..a790011663 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/ControllerRunner.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/ControllerRunner.java @@ -20,7 +20,7 @@ package org.apache.qpid.disttest; import java.io.File; -import java.io.FileNotFoundException; +import java.io.IOException; import java.util.ArrayList; import java.util.List; @@ -32,6 +32,8 @@ import org.apache.qpid.disttest.controller.config.Config; import org.apache.qpid.disttest.controller.config.ConfigReader; import org.apache.qpid.disttest.db.ResultsDbWriter; import org.apache.qpid.disttest.jms.ControllerJmsDelegate; +import org.apache.qpid.disttest.results.ResultsCsvWriter; +import org.apache.qpid.disttest.results.ResultsWriter; import org.apache.qpid.disttest.results.aggregation.Aggregator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -55,7 +57,7 @@ public class ControllerRunner extends AbstractRunner private final ConfigFileHelper _configFileHelper = new ConfigFileHelper(); - private ResultsFileWriter _resultsFileWriter; + private ResultsWriter _resultsFileWriter; private ResultsDbWriter _resultsDbWriter; @@ -108,7 +110,7 @@ public class ControllerRunner extends AbstractRunner { String outputDirString = getCliOptions().get(ControllerRunner.OUTPUT_DIR_PROP); File outputDir = new File(outputDirString); - _resultsFileWriter = new ResultsFileWriter(outputDir); + _resultsFileWriter = new ResultsCsvWriter(outputDir); } private void runTests(ControllerJmsDelegate jmsDelegate) @@ -155,7 +157,7 @@ public class ControllerRunner extends AbstractRunner ResultsForAllTests rawResultsForAllTests = controller.runAllTests(); ResultsForAllTests resultsForAllTests = _aggregator.aggregateResults(rawResultsForAllTests); - _resultsFileWriter.writeResultsToFile(resultsForAllTests, testConfigFile); + _resultsFileWriter.writeResults(resultsForAllTests, testConfigFile); if(_resultsDbWriter != null) { _resultsDbWriter.writeResults(resultsForAllTests); @@ -194,9 +196,9 @@ public class ControllerRunner extends AbstractRunner { testConfig = configReader.getConfigFromFile(testConfigFile); } - catch (FileNotFoundException e) + catch (IOException e) { - throw new DistributedTestException("Exception while loading test config from " + testConfigFile, e); + throw new DistributedTestException("Exception while loading test config from '" + testConfigFile + "'", e); } return testConfig; } diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/ResultsFileWriter.java b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/ResultsFileWriter.java deleted file mode 100644 index 81b717403d..0000000000 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/ResultsFileWriter.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.qpid.disttest; - -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.util.List; - -import org.apache.qpid.disttest.controller.ResultsForAllTests; -import org.apache.qpid.disttest.results.aggregation.TestResultAggregator; -import org.apache.qpid.disttest.results.formatting.CSVFormatter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ResultsFileWriter -{ - private static final Logger LOGGER = LoggerFactory.getLogger(ResultsFileWriter.class); - - static final String TEST_SUMMARY_FILE_NAME = "test-summary.csv"; - - private final File _outputDir; - - private CSVFormatter _csvFormater = new CSVFormatter(); - - private TestResultAggregator _testResultAggregator = new TestResultAggregator(); - - public ResultsFileWriter(File outputDir) - { - _outputDir = outputDir; - } - - public void writeResultsToFile(ResultsForAllTests resultsForAllTests, String testConfigFile) - { - final String outputFile = generateOutputCsvNameFrom(testConfigFile); - writeResultsToOutputFile(resultsForAllTests, outputFile); - } - - public void writeResultsSummary(List allResultsList) - { - ResultsForAllTests combinedResults = _testResultAggregator.aggregateTestResults(allResultsList); - writeResultsToOutputFile(combinedResults, new File(_outputDir, TEST_SUMMARY_FILE_NAME).getAbsolutePath()); - } - - /** - * generateOutputCsvNameFrom("/config/testConfigFile.js", "/output") returns /output/testConfigFile.csv - */ - private String generateOutputCsvNameFrom(String testConfigFile) - { - final String filenameOnlyWithExtension = new File(testConfigFile).getName(); - final String cvsFile = filenameOnlyWithExtension.replaceFirst(".?\\w*$", ".csv"); - - return new File(_outputDir, cvsFile).getAbsolutePath(); - } - - private void writeResultsToOutputFile(ResultsForAllTests resultsForAllTests, String outputFile) - { - FileWriter writer = null; - try - { - final String outputCsv = _csvFormater.format(resultsForAllTests); - writer = new FileWriter(outputFile); - writer.write(outputCsv); - LOGGER.info("Wrote " + resultsForAllTests.getTestResults().size() + " test result(s) to output file " + outputFile); - } - catch (IOException e) - { - throw new DistributedTestException("Unable to write output file " + outputFile, e); - } - finally - { - if (writer != null) - { - try - { - writer.close(); - } - catch (IOException e) - { - LOGGER.error("Failed to close stream for file " + outputFile, e); - } - } - } - } - - void setCsvFormater(CSVFormatter csvFormater) - { - _csvFormater = csvFormater; - } - - void setTestResultAggregator(TestResultAggregator testResultAggregator) - { - _testResultAggregator = testResultAggregator; - } - -} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConfigReader.java b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConfigReader.java index bd7d239a90..1f0368e87e 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConfigReader.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ConfigReader.java @@ -21,6 +21,7 @@ package org.apache.qpid.disttest.controller.config; import java.io.FileNotFoundException; import java.io.FileReader; +import java.io.IOException; import java.io.Reader; import java.io.StringReader; @@ -36,7 +37,7 @@ public class ConfigReader { private static final Logger LOGGER = LoggerFactory.getLogger(ConfigReader.class); - public Config getConfigFromFile(String fileName) throws FileNotFoundException + public Config getConfigFromFile(String fileName) throws IOException { Reader reader = getConfigReader(fileName); @@ -44,7 +45,24 @@ public class ConfigReader return config; } - protected Reader getConfigReader(String fileName) throws FileNotFoundException + public Config readConfig(Reader reader) + { + return readConfig(reader, false); + } + + public Config readConfig(Reader reader, boolean isJavascript) + { + if (isJavascript) + { + return readJson(new StringReader(new JavaScriptConfigEvaluator().evaluateJavaScript(reader))); + } + else + { + return readJson(reader); + } + } + + private Reader getConfigReader(String fileName) throws IOException { Reader reader = null; if (fileName.endsWith(".js")) @@ -60,7 +78,8 @@ public class ConfigReader return reader; } - public Config readConfig(Reader reader) + + private Config readJson(Reader reader) { Gson gson = new GsonBuilder() .registerTypeAdapter(PropertyValue.class, new PropertyValueAdapter()) diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/JavaScriptConfigEvaluator.java b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/JavaScriptConfigEvaluator.java index bbc22af00f..d760ffe06d 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/JavaScriptConfigEvaluator.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/JavaScriptConfigEvaluator.java @@ -20,9 +20,10 @@ */ package org.apache.qpid.disttest.controller.config; -import java.io.FileNotFoundException; import java.io.FileReader; +import java.io.IOException; import java.io.InputStreamReader; +import java.io.Reader; import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; @@ -41,7 +42,26 @@ public class JavaScriptConfigEvaluator public static final String TEST_CONFIG_VARIABLE_NAME = "jsonObject"; - public String evaluateJavaScript(String fileName) throws FileNotFoundException + public String evaluateJavaScript(String fileName) throws IOException + { + FileReader fileReader = null; + try + { + fileReader = new FileReader(fileName); + String result = evaluateJavaScript(fileReader); + LOGGER.debug("Evaluated javascript file " + fileName + ". Generated the following JSON: " + result); + return result; + } + finally + { + if (fileReader != null) + { + fileReader.close(); + } + } + } + + public String evaluateJavaScript(Reader fileReader) { ScriptEngineManager mgr = new ScriptEngineManager(); ScriptEngine engine = mgr.getEngineByName("JavaScript"); @@ -49,7 +69,7 @@ public class JavaScriptConfigEvaluator { engine.eval(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("json2.js"))); engine.eval(new InputStreamReader(getClass().getClassLoader().getResourceAsStream("test-utils.js"))); - engine.eval(new FileReader(fileName)); + engine.eval(fileReader); engine.eval("jsonString = JSON.stringify(" + TEST_CONFIG_VARIABLE_NAME + ")"); } catch (ScriptException e) @@ -58,7 +78,6 @@ public class JavaScriptConfigEvaluator } String result = (String) engine.get("jsonString"); - LOGGER.debug("Evaluated javascript file " + fileName + ". Generated the following JSON: " + result); return result; } } diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ProducerConfig.java b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ProducerConfig.java index 88c188d3ac..a5f6fb84cf 100644 --- a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ProducerConfig.java +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/controller/config/ProducerConfig.java @@ -21,10 +21,17 @@ package org.apache.qpid.disttest.controller.config; import javax.jms.Message; +import org.apache.commons.lang.ObjectUtils; import org.apache.qpid.disttest.message.CreateProducerCommand; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class ProducerConfig extends ParticipantConfig { + private static final Logger LOGGER = LoggerFactory.getLogger(ProducerConfig.class); + + public static final String MESSAGE_SIZE_OVERRIDE_SYSTEM_PROPERTY = "qpid.disttest.messageSize"; + private int _deliveryMode; private int _messageSize; private int _priority; @@ -33,6 +40,9 @@ public class ProducerConfig extends ParticipantConfig private long _startDelay; private String _messageProviderName; + /** used to ensure we only log about the overridden message size once */ + private boolean _alreadyLoggedAboutOverriddenMessageSize; + // For Gson public ProducerConfig() { @@ -78,7 +88,10 @@ public class ProducerConfig extends ParticipantConfig command.setSessionName(sessionName); command.setDeliveryMode(_deliveryMode); - command.setMessageSize(_messageSize); + + Integer messageSize = (Integer)ObjectUtils.defaultIfNull(getOverriddenMessageSize(), _messageSize); + + command.setMessageSize(messageSize); command.setPriority(_priority); command.setTimeToLive(_timeToLive); command.setInterval(_interval); @@ -87,4 +100,31 @@ public class ProducerConfig extends ParticipantConfig return command; } + + private Integer getOverriddenMessageSize() + { + String overriddenMessageSizeString = System.getProperty(MESSAGE_SIZE_OVERRIDE_SYSTEM_PROPERTY); + if(overriddenMessageSizeString != null) + { + try + { + int overriddenMessageSize = Integer.valueOf(overriddenMessageSizeString); + + if(!_alreadyLoggedAboutOverriddenMessageSize) + { + LOGGER.info("Applied overridden maximum duration " + overriddenMessageSize); + _alreadyLoggedAboutOverriddenMessageSize = true; + } + + return overriddenMessageSize; + } + catch (NumberFormatException e) + { + LOGGER.error("Couldn't parse overridden message size " + overriddenMessageSizeString, e); + } + } + + return null; + } + } diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/BenchmarkResultWriter.java b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/BenchmarkResultWriter.java new file mode 100644 index 0000000000..a184b53668 --- /dev/null +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/BenchmarkResultWriter.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.disttest.results; + +import java.util.List; + +import org.apache.qpid.disttest.controller.ResultsForAllTests; +import org.apache.qpid.disttest.message.ParticipantResult; +import org.apache.qpid.disttest.results.aggregation.ITestResult; + +public class BenchmarkResultWriter implements ResultsWriter +{ + private final boolean _reportMessageTotals; + + public BenchmarkResultWriter(boolean reportMessageTotals) + { + _reportMessageTotals = reportMessageTotals; + } + + @Override + public void writeResults(ResultsForAllTests resultsForAllTests, String testConfigFile) + { + for (ITestResult allResult : resultsForAllTests.getAllParticipantsResult().getTestResults()) + { + ParticipantResult allRowData = allResult.getParticipantResults().iterator().next(); + + if (allRowData.getErrorMessage() == null) + { + final String output; + if (_reportMessageTotals) + { + output = String.format("%s : %,d (total payload/bytes) : %,d (time taken/ms) : %,d (total messages) : %,d (messages/s) %,.2f (Kbytes/s)", + allResult.getName(), allRowData.getTotalPayloadProcessed(), allRowData.getTimeTaken(), allRowData.getNumberOfMessagesProcessed(), allRowData.getMessageThroughput(), allRowData.getThroughput()); + } + else + { + output = String.format("%s : %,d (messages/s) %,.2f (Kbytes/s)", allResult.getName(), allRowData.getMessageThroughput(), allRowData.getThroughput()); + } + System.out.println(output); + } + else + { + System.err.println(allRowData.getErrorMessage()); + } + } + } + + @Override + public void writeResultsSummary(List allResultsList) + { + } +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/ResultsCsvWriter.java b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/ResultsCsvWriter.java new file mode 100644 index 0000000000..a041233ea3 --- /dev/null +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/ResultsCsvWriter.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.disttest.results; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; + +import org.apache.qpid.disttest.DistributedTestException; +import org.apache.qpid.disttest.controller.ResultsForAllTests; +import org.apache.qpid.disttest.results.aggregation.TestResultAggregator; +import org.apache.qpid.disttest.results.formatting.CSVFormatter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ResultsCsvWriter implements ResultsWriter +{ + private static final Logger LOGGER = LoggerFactory.getLogger(ResultsCsvWriter.class); + + static final String TEST_SUMMARY_FILE_NAME = "test-summary.csv"; + + private final File _outputDir; + + private CSVFormatter _csvFormater = new CSVFormatter(); + + private TestResultAggregator _testResultAggregator = new TestResultAggregator(); + + public ResultsCsvWriter(File outputDir) + { + _outputDir = outputDir; + } + + @Override + public void writeResults(ResultsForAllTests resultsForAllTests, String testConfigFile) + { + final String outputFile = generateOutputCsvNameFrom(testConfigFile); + writeResultsToOutputFile(resultsForAllTests, outputFile); + } + + @Override + public void writeResultsSummary(List allResultsList) + { + ResultsForAllTests combinedResults = _testResultAggregator.aggregateTestResults(allResultsList); + writeResultsToOutputFile(combinedResults, new File(_outputDir, TEST_SUMMARY_FILE_NAME).getAbsolutePath()); + } + + /** + * generateOutputCsvNameFrom("/config/testConfigFile.js", "/output") returns /output/testConfigFile.csv + */ + private String generateOutputCsvNameFrom(String testConfigFile) + { + final String filenameOnlyWithExtension = new File(testConfigFile).getName(); + final String cvsFile = filenameOnlyWithExtension.replaceFirst(".?\\w*$", ".csv"); + + return new File(_outputDir, cvsFile).getAbsolutePath(); + } + + private void writeResultsToOutputFile(ResultsForAllTests resultsForAllTests, String outputFile) + { + FileWriter writer = null; + try + { + final String outputCsv = _csvFormater.format(resultsForAllTests); + writer = new FileWriter(outputFile); + writer.write(outputCsv); + LOGGER.info("Wrote " + resultsForAllTests.getTestResults().size() + " test result(s) to output file " + outputFile); + } + catch (IOException e) + { + throw new DistributedTestException("Unable to write output file " + outputFile, e); + } + finally + { + if (writer != null) + { + try + { + writer.close(); + } + catch (IOException e) + { + LOGGER.error("Failed to close stream for file " + outputFile, e); + } + } + } + } + + public void setCsvFormater(CSVFormatter csvFormater) + { + _csvFormater = csvFormater; + } + + void setTestResultAggregator(TestResultAggregator testResultAggregator) + { + _testResultAggregator = testResultAggregator; + } + +} diff --git a/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/ResultsWriter.java b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/ResultsWriter.java new file mode 100644 index 0000000000..4cc8c9cf87 --- /dev/null +++ b/qpid/java/perftests/src/main/java/org/apache/qpid/disttest/results/ResultsWriter.java @@ -0,0 +1,34 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.disttest.results; + +import java.util.List; + +import org.apache.qpid.disttest.controller.ResultsForAllTests; + +public interface ResultsWriter +{ + + void writeResults(ResultsForAllTests resultsForAllTests, String testConfigFile); + + void writeResultsSummary(List allResultsList); + +} \ No newline at end of file diff --git a/qpid/java/perftests/src/main/resources/Benchmark.js b/qpid/java/perftests/src/main/resources/Benchmark.js new file mode 100644 index 0000000000..bcdcc1edfb --- /dev/null +++ b/qpid/java/perftests/src/main/resources/Benchmark.js @@ -0,0 +1,77 @@ + +var jsonObject = { + _tests:[] +}; + +var duration = 30000; +var queueName = "direct://amq.direct//benchmark?durable='true'"; + +var numbersOfParticipants = [1, 2, 5, 10]; + +for(participantIndex=0; participantIndex < numbersOfParticipants.length; participantIndex++) +{ + var numberOfProducers = numbersOfParticipants[participantIndex]; + var numberOfConsumers = numbersOfParticipants[participantIndex]; + var test = { + "_name": "" + numberOfProducers + " producer(s) and " + numberOfConsumers + " consumer(s), each on separate connections, persistent messaging with transactional sessions", + "_queues":[ + { + "_name": queueName, + "_durable": true + } + ], + "_clients": + QPID.times(numberOfProducers, + { + "_name": "producingClient__PRODUCING_CLIENT_INDEX", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_acknowledgeMode": 0, + "_producers": [ + { + "_name": "Producer__PRODUCING_CLIENT_INDEX", + "_destinationName": queueName, + "_maximumDuration": duration, + "_deliveryMode": 2, + "_messageSize": 1024 + } + ] + } + ] + } + ] + }, + "__PRODUCING_CLIENT_INDEX") + .concat(QPID.times(numberOfConsumers, + { + "_name": "consumingClient__CONSUMING_CLIENT_INDEX", + "_connections":[ + { + "_name": "connection1", + "_factory": "connectionfactory", + "_sessions": [ + { + "_sessionName": "session1", + "_acknowledgeMode": 0, + "_consumers": [ + { + "_name": "Consumer__CONSUMING_CLIENT_INDEX", + "_destinationName": queueName, + "_maximumDuration": duration + } + ] + } + ] + } + ] + }, + "__CONSUMING_CLIENT_INDEX")) + }; + + jsonObject._tests= jsonObject._tests.concat(test); +} diff --git a/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/ResultsFileWriterTest.java b/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/ResultsFileWriterTest.java deleted file mode 100644 index ab55e8003d..0000000000 --- a/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/ResultsFileWriterTest.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you 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 org.apache.qpid.disttest; - -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -import java.io.File; -import java.util.Arrays; - -import org.apache.qpid.disttest.controller.ResultsForAllTests; -import org.apache.qpid.disttest.results.aggregation.TestResultAggregator; -import org.apache.qpid.disttest.results.formatting.CSVFormatter; -import org.apache.qpid.test.utils.QpidTestCase; -import org.apache.qpid.test.utils.TestFileUtils; -import org.apache.qpid.util.FileUtils; - -public class ResultsFileWriterTest extends QpidTestCase -{ - private CSVFormatter _csvFormater = mock(CSVFormatter.class); - private TestResultAggregator _testResultAggregator = mock(TestResultAggregator.class); - - private File _outputDir = TestFileUtils.createTestDirectory(); - - private ResultsFileWriter _resultsFileWriter = new ResultsFileWriter(_outputDir); - - @Override - public void setUp() - { - _resultsFileWriter.setCsvFormater(_csvFormater); - _resultsFileWriter.setTestResultAggregator(_testResultAggregator); - } - - public void testWriteResultsToFile() - { - ResultsForAllTests resultsForAllTests = mock(ResultsForAllTests.class); - - String expectedCsvContents = "expected-csv-contents"; - when(_csvFormater.format(resultsForAllTests)).thenReturn(expectedCsvContents); - - _resultsFileWriter.writeResultsToFile(resultsForAllTests, "config.json"); - - File resultsFile = new File(_outputDir, "config.csv"); - - assertEquals(expectedCsvContents, FileUtils.readFileAsString(resultsFile)); - } - - public void testWriteResultsSummary() - { - ResultsForAllTests results1 = mock(ResultsForAllTests.class); - ResultsForAllTests results2 = mock(ResultsForAllTests.class); - ResultsForAllTests summaryResults = mock(ResultsForAllTests.class); - - when(_testResultAggregator.aggregateTestResults(Arrays.asList(results1, results2))) - .thenReturn(summaryResults); - - String expectedSummaryFileContents = "expected-summary-file"; - - when(_csvFormater.format(summaryResults)) - .thenReturn(expectedSummaryFileContents); - - _resultsFileWriter.writeResultsSummary(Arrays.asList(results1, results2)); - - File summaryFile = new File(_outputDir, ResultsFileWriter.TEST_SUMMARY_FILE_NAME); - - assertEquals(expectedSummaryFileContents, FileUtils.readFileAsString(summaryFile)); - } - -} diff --git a/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/JavaScriptConfigEvaluatorTest.java b/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/JavaScriptConfigEvaluatorTest.java index 55c1d4a7bd..174bd8092c 100644 --- a/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/JavaScriptConfigEvaluatorTest.java +++ b/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/JavaScriptConfigEvaluatorTest.java @@ -22,6 +22,7 @@ package org.apache.qpid.disttest.controller.config; import static org.apache.commons.beanutils.PropertyUtils.getProperty; +import java.io.FileReader; import java.util.List; import java.util.TreeMap; @@ -59,6 +60,34 @@ public class JavaScriptConfigEvaluatorTest extends QpidTestCase assertEquals("Unexpected iterating attribute", "1", getProperty(country1, "_regions.[0]._towns.[0]._iteratingAttribute")); } + public void testEvaluateJavaScriptWithReader() throws Exception + { + String jsFilePath = TestFileUtils.createTempFileFromResource(this, "JavaScriptConfigEvaluatorTest-test-config.js").getAbsolutePath(); + + FileReader fileReader = new FileReader(jsFilePath); + String rawConfig = new JavaScriptConfigEvaluator().evaluateJavaScript(fileReader); + + Object configAsObject = getObject(rawConfig); + + // Tests are produced by the QPID.iterations js function + assertEquals("Unexpected number of countries", 2, getPropertyAsList(configAsObject, "_countries").size()); + + Object country0 = getProperty(configAsObject, "_countries.[0]"); + assertEquals("Unexpected country name", "Country", getProperty(country0, "_name")); + assertEquals("Unexpected country iteration number", 0, getPropertyAsInt(country0, "_iterationNumber")); + + assertEquals("Unexpected number of regions", 2, getPropertyAsList(country0, "_regions").size()); + // Region names are produced by the QPID.times js function + assertEquals("Unexpected region name", "repeatingRegion0", getProperty(country0, "_regions.[0]._name")); + assertEquals("Unexpected region name", "repeatingRegion1", getProperty(country0, "_regions.[1]._name")); + // Iterating attribute are produced by the QPID.iterations js function + assertEquals("Unexpected iterating attribute", "0", getProperty(country0, "_regions.[0]._towns.[0]._iteratingAttribute")); + + Object country1 = getProperty(configAsObject, "_countries.[1]"); + assertEquals("Unexpected country iteration number", 1, getPropertyAsInt(country1, "_iterationNumber")); + assertEquals("Unexpected iterating attribute", "1", getProperty(country1, "_regions.[0]._towns.[0]._iteratingAttribute")); + } + private int getPropertyAsInt(Object configAsObject, String property) throws Exception { Number propertyValue = (Number) getProperty(configAsObject, property); diff --git a/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ProducerConfigTest.java b/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ProducerConfigTest.java index 44fca4bb7c..ea9a406b1d 100644 --- a/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ProducerConfigTest.java +++ b/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/controller/config/ProducerConfigTest.java @@ -41,6 +41,21 @@ public class ProducerConfigTest extends QpidTestCase assertEquals(Message.DEFAULT_TIME_TO_LIVE, p.getTimeToLive()); } + public void testMessageSizeDefault() + { + CreateProducerCommand producer = new ProducerConfig().createCommand("session1"); + assertEquals("Unexpected default message size", 1024, producer.getMessageSize()); + } + + public void testMessageSizeDefaultOverride() + { + final long overriddenMessageSize = 4096; + setTestSystemProperty(ProducerConfig.MESSAGE_SIZE_OVERRIDE_SYSTEM_PROPERTY, String.valueOf(overriddenMessageSize)); + + CreateProducerCommand producer2 = new ProducerConfig().createCommand("session1"); + assertEquals("Unexpected message size", overriddenMessageSize, producer2.getMessageSize()); + } + public void testCreateProducerCommand() { String destination = "url:/destination"; diff --git a/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/results/ResultsFileWriterTest.java b/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/results/ResultsFileWriterTest.java new file mode 100644 index 0000000000..db306ea1a4 --- /dev/null +++ b/qpid/java/perftests/src/test/java/org/apache/qpid/disttest/results/ResultsFileWriterTest.java @@ -0,0 +1,86 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 org.apache.qpid.disttest.results; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.util.Arrays; + +import org.apache.qpid.disttest.controller.ResultsForAllTests; +import org.apache.qpid.disttest.results.ResultsCsvWriter; +import org.apache.qpid.disttest.results.aggregation.TestResultAggregator; +import org.apache.qpid.disttest.results.formatting.CSVFormatter; +import org.apache.qpid.test.utils.QpidTestCase; +import org.apache.qpid.test.utils.TestFileUtils; +import org.apache.qpid.util.FileUtils; + +public class ResultsFileWriterTest extends QpidTestCase +{ + private CSVFormatter _csvFormater = mock(CSVFormatter.class); + private TestResultAggregator _testResultAggregator = mock(TestResultAggregator.class); + + private File _outputDir = TestFileUtils.createTestDirectory(); + + private ResultsCsvWriter _resultsFileWriter = new ResultsCsvWriter(_outputDir); + + @Override + public void setUp() + { + _resultsFileWriter.setCsvFormater(_csvFormater); + _resultsFileWriter.setTestResultAggregator(_testResultAggregator); + } + + public void testWriteResultsToFile() + { + ResultsForAllTests resultsForAllTests = mock(ResultsForAllTests.class); + + String expectedCsvContents = "expected-csv-contents"; + when(_csvFormater.format(resultsForAllTests)).thenReturn(expectedCsvContents); + + _resultsFileWriter.writeResults(resultsForAllTests, "config.json"); + + File resultsFile = new File(_outputDir, "config.csv"); + + assertEquals(expectedCsvContents, FileUtils.readFileAsString(resultsFile)); + } + + public void testWriteResultsSummary() + { + ResultsForAllTests results1 = mock(ResultsForAllTests.class); + ResultsForAllTests results2 = mock(ResultsForAllTests.class); + ResultsForAllTests summaryResults = mock(ResultsForAllTests.class); + + when(_testResultAggregator.aggregateTestResults(Arrays.asList(results1, results2))) + .thenReturn(summaryResults); + + String expectedSummaryFileContents = "expected-summary-file"; + + when(_csvFormater.format(summaryResults)) + .thenReturn(expectedSummaryFileContents); + + _resultsFileWriter.writeResultsSummary(Arrays.asList(results1, results2)); + + File summaryFile = new File(_outputDir, ResultsCsvWriter.TEST_SUMMARY_FILE_NAME); + + assertEquals(expectedSummaryFileContents, FileUtils.readFileAsString(summaryFile)); + } + +} -- cgit v1.2.1