You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@accumulo.apache.org by kt...@apache.org on 2018/07/23 21:34:50 UTC
[accumulo-testing] branch master updated: Created performance test
framework (#21)
This is an automated email from the ASF dual-hosted git repository.
kturner pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/accumulo-testing.git
The following commit(s) were added to refs/heads/master by this push:
new 930ca61 Created performance test framework (#21)
930ca61 is described below
commit 930ca61facf40481edd5ebd5e5e997b374061435
Author: Keith Turner <ke...@deenlo.com>
AuthorDate: Mon Jul 23 17:34:48 2018 -0400
Created performance test framework (#21)
---
README.md | 74 ++++++++-
bin/performance-test | 97 +++++++++++
conf/.gitignore | 1 +
conf/cluster-control.sh.uno | 86 ++++++++++
core/pom.xml | 4 +
.../testing/core/performance/Environment.java | 24 +++
.../testing/core/performance/Parameter.java | 31 ++++
.../testing/core/performance/PerformanceTest.java | 25 +++
.../accumulo/testing/core/performance/Report.java | 109 +++++++++++++
.../accumulo/testing/core/performance/Result.java | 54 +++++++
.../accumulo/testing/core/performance/Stats.java | 34 ++++
.../core/performance/SystemConfiguration.java | 37 +++++
.../testing/core/performance/impl/Compare.java | 117 ++++++++++++++
.../core/performance/impl/ContextualReport.java | 39 +++++
.../testing/core/performance/impl/Csv.java | 129 +++++++++++++++
.../testing/core/performance/impl/ListTests.java | 37 +++++
.../core/performance/impl/MergeSiteConfig.java | 52 ++++++
.../core/performance/impl/PerfTestRunner.java | 70 ++++++++
.../core/performance/tests/ScanExecutorPT.java | 177 +++++++++++++++++++++
.../core/performance/tests/ScanFewFamiliesPT.java | 114 +++++++++++++
.../testing/core/performance/util/TestData.java | 62 ++++++++
.../core/performance/util/TestExecutor.java | 59 +++++++
22 files changed, 1431 insertions(+), 1 deletion(-)
diff --git a/README.md b/README.md
index 8826cc3..c1e8d91 100644
--- a/README.md
+++ b/README.md
@@ -53,7 +53,7 @@ The YARN application can be killed at any time using the YARN resource manager o
## Continuous Ingest & Query
The Continuous Ingest test runs many ingest clients that continually create linked lists of data
-in Accumulo. During ingest, query applications can be run to continously walk and verify the the
+in Accumulo. During ingest, query applications can be run to continuously walk and verify the
linked lists and put a query load on Accumulo. At some point, the ingest clients are stopped and
a MapReduce job is run to ensure that there are no holes in any linked list.
@@ -135,6 +135,78 @@ Run the command below stop the agitator:
./bin/accumulo-testing agitator stop
+## Performance Test
+
+To run performance test a `cluster-control.sh` script is needed to assist with starting, stopping,
+wiping, and confguring an Accumulo instance. This script should define the following functions.
+
+```bash
+function get_version {
+ case $1 in
+ ACCUMULO)
+ # TODO echo accumulo version
+ ;;
+ HADOOP)
+ # TODO echo hadoop version
+ ;;
+ ZOOKEEPER)
+ # TODO echo zookeeper version
+ ;;
+ *)
+ return 1
+ esac
+}
+
+function start_cluster {
+ # TODO start Hadoop and Zookeeper if needed
+}
+
+function setup_accumulo {
+ # TODO kill any running Accumulo instance
+ # TODO setup a fresh install of Accumulo w/o starting it
+}
+
+function get_config_file {
+ local file_to_get=$1
+ local dest_dir=$2
+ # TODO copy $file_to_get from Accumulo conf dir to $dest_dir
+}
+
+function put_config_file {
+ local config_file=$1
+ # TODO copy $config_file to Accumulo conf dir
+}
+
+function put_server_code {
+ local jar_file=$1
+ # TODO add $jar_file to Accumulo's server side classpath. Could put it in $ACCUMULO_HOME/lib/ext
+}
+
+function start_accumulo {
+ # TODO start accumulo
+}
+
+function stop_cluster {
+ # TODO kill Accumulo, Hadoop, and Zookeeper
+}
+```
+
+
+
+An example script for [Uno] is provided. To use this doe the following and set
+`UNO_HOME` after copying.
+
+ cp conf/cluster-control.sh.uno conf/cluster-control.sh
+
+After the cluster control script is setup, the following will run performance
+test and produce json result files.
+
+ ./bin/performance-test run <output dir>
+
+There are some utilities for working with the json result files, run the performance-test script
+with no options to see them.
+
+[Uno]: https://github.com/apache/fluo-uno
[modules]: core/src/main/resources/randomwalk/modules
[image]: core/src/main/resources/randomwalk/modules/Image.xml
[ti]: https://travis-ci.org/apache/accumulo-testing.svg?branch=master
diff --git a/bin/performance-test b/bin/performance-test
new file mode 100755
index 0000000..93014e5
--- /dev/null
+++ b/bin/performance-test
@@ -0,0 +1,97 @@
+#! /usr/bin/env bash
+
+# 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.
+
+bin_dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
+at_home=$( cd "$( dirname "$bin_dir" )" && pwd )
+at_version=2.0.0-SNAPSHOT
+
+function print_usage() {
+ cat <<EOF
+
+Usage: performance-test <command> (<argument>)
+
+Possible commands:
+ run <output dir> Runs performance tests.
+ compare <result 1> <result 2> Compares results of two test.
+ csv {files} Converts results to CSV
+EOF
+}
+
+
+function build_shade_jar() {
+ at_shaded_jar="$at_home/core/target/accumulo-testing-core-$at_version-shaded.jar"
+ if [ ! -f "$at_shaded_jar" ]; then
+ echo "Building $at_shaded_jar"
+ cd "$at_home" || exit 1
+ mvn clean package -P create-shade-jar -D skipTests -D accumulo.version=$(get_version "ACCUMULO") -D hadoop.version=$(get_version "HADOOP") -D zookeeper.version=$(get_version "ZOOKEEPER")
+ fi
+}
+
+log4j_config="$at_home/conf/log4j.properties"
+if [ ! -f "$log4j_config" ]; then
+ log4j_config="$at_home/conf/log4j.properties.example"
+ if [ ! -f "$log4j_config" ]; then
+ echo "Could not find logj4.properties or log4j.properties.example in $at_home/conf"
+ exit 1
+ fi
+fi
+
+
+if [ ! -f "$at_home/conf/cluster-control.sh" ]; then
+ echo "Could not find cluster-control.sh"
+ exit 1
+fi
+
+. $at_home/conf/cluster-control.sh
+build_shade_jar
+CP="$at_home/core/target/accumulo-testing-core-$at_version-shaded.jar"
+perf_pkg="org.apache.accumulo.testing.core.performance.impl"
+case "$1" in
+ run)
+ if [ -z "$2" ]; then
+ echo "ERROR: <output dir> needs to be set"
+ print_usage
+ exit 1
+ fi
+ mkdir -p "$2"
+ start_cluster
+ CLASSPATH="$CP" java -Dlog4j.configuration="file:$log4j_config" ${perf_pkg}.ListTests | while read test_class; do
+ echo "Running test $test_class"
+ pt_tmp=$(mktemp -d -t accumulo_pt_XXXXXXX)
+ setup_accumulo
+ get_config_file accumulo-site.xml "$pt_tmp"
+ CLASSPATH="$CP" java -Dlog4j.configuration="file:$log4j_config" ${perf_pkg}.MergeSiteConfig "$test_class" "$pt_tmp"
+ put_config_file "$pt_tmp/accumulo-site.xml"
+ put_server_code "$at_home/core/target/accumulo-testing-core-$at_version.jar"
+ start_accumulo
+ get_config_file accumulo-client.properties "$pt_tmp"
+ CLASSPATH="$CP" java -Dlog4j.configuration="file:$log4j_config" ${perf_pkg}.PerfTestRunner "$pt_tmp/accumulo-client.properties" "$test_class" "$(get_version 'ACCUMULO')" "$2"
+ done
+ stop_cluster
+ ;;
+ compare)
+ CLASSPATH="$CP" java -Dlog4j.configuration="file:$log4j_config" ${perf_pkg}.Compare "$2" "$3"
+ ;;
+ csv)
+ CLASSPATH="$CP" java -Dlog4j.configuration="file:$log4j_config" ${perf_pkg}.Csv "${@:2}"
+ ;;
+ *)
+ echo "Unknown command : $1"
+ print_usage
+ exit 1
+esac
+
diff --git a/conf/.gitignore b/conf/.gitignore
index 7c4fb33..ca4a321 100644
--- a/conf/.gitignore
+++ b/conf/.gitignore
@@ -1,3 +1,4 @@
/accumulo-testing.properties
/accumulo-testing-env.sh
/log4j.properties
+/cluster-control.sh
diff --git a/conf/cluster-control.sh.uno b/conf/cluster-control.sh.uno
new file mode 100644
index 0000000..2b9abdb
--- /dev/null
+++ b/conf/cluster-control.sh.uno
@@ -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.
+
+# internal functions just for uno cluster control
+
+UNO_HOME=/home/ubuntu/git/uno
+UNO=$UNO_HOME/bin/uno
+
+function get_ah {
+ echo "$($UNO env | grep ACCUMULO_HOME | sed 's/export ACCUMULO_HOME=//' | sed 's/"//g')"
+}
+
+
+# functions required for accumulo testing cluster control
+
+function get_version {
+ case $1 in
+ ACCUMULO)
+ (
+ # run following in sub shell so it does not pollute
+ . $UNO_HOME/conf/uno.conf
+ echo $ACCUMULO_VERSION
+ )
+ ;;
+ HADOOP)
+ (
+ # run following in sub shell so it does not pollute
+ . $UNO_HOME/conf/uno.conf
+ echo $HADOOP_VERSION
+ )
+ ;;
+ ZOOKEEPER)
+ (
+ # run following in sub shell so it does not pollute
+ . $UNO_HOME/conf/uno.conf
+ echo $ZOOKEEPER_VERSION
+ )
+ ;;
+ *)
+ return 1
+ esac
+}
+
+function start_cluster {
+ $UNO setup accumulo
+}
+
+function setup_accumulo {
+ $UNO setup accumulo --no-deps
+}
+
+function get_config_file {
+ local ah=$(get_ah)
+ cp "$ah/conf/$1" "$2"
+}
+
+function put_config_file {
+ local ah=$(get_ah)
+ cp "$1" "$ah/conf"
+}
+
+function put_server_code {
+ local ah=$(get_ah)
+ cp "$1" "$ah/lib/ext"
+}
+
+function start_accumulo {
+ $UNO stop accumulo --no-deps &> /dev/null
+ $UNO start accumulo --no-deps &> /dev/null
+}
+
+function stop_cluster {
+ $UNO kill
+}
diff --git a/core/pom.xml b/core/pom.xml
index 4655949..0d03f95 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -119,6 +119,10 @@
</excludes>
</filter>
</filters>
+ <transformers>
+ <!-- Hadoop uses service loader to find filesystem impls, without this may not find them -->
+ <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
+ </transformers>
</configuration>
</execution>
</executions>
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/Environment.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/Environment.java
new file mode 100644
index 0000000..941fb8c
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/Environment.java
@@ -0,0 +1,24 @@
+/*
+ * 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.accumulo.testing.core.performance;
+
+import org.apache.accumulo.core.client.Connector;
+
+public interface Environment {
+ Connector getConnector();
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/Parameter.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/Parameter.java
new file mode 100644
index 0000000..41eb3d4
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/Parameter.java
@@ -0,0 +1,31 @@
+/*
+ * 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.accumulo.testing.core.performance;
+
+public class Parameter {
+
+ public final String id;
+ public final String data;
+ public final String description;
+
+ public Parameter(String id, String data, String description) {
+ this.id = id;
+ this.data = data;
+ this.description = description;
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/PerformanceTest.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/PerformanceTest.java
new file mode 100644
index 0000000..04ef98f
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/PerformanceTest.java
@@ -0,0 +1,25 @@
+/*
+ * 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.accumulo.testing.core.performance;
+
+public interface PerformanceTest {
+
+ SystemConfiguration getConfiguration();
+
+ Report runTest(Environment env) throws Exception;
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/Report.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/Report.java
new file mode 100644
index 0000000..28924e3
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/Report.java
@@ -0,0 +1,109 @@
+/*
+ * 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.accumulo.testing.core.performance;
+
+import java.util.List;
+import java.util.LongSummaryStatistics;
+
+import org.apache.accumulo.testing.core.performance.Result.Purpose;
+
+import com.google.common.collect.ImmutableList;
+
+public class Report {
+ public final String id;
+ public final String description;
+ public final List<Result> results;
+ public final List<Parameter> parameters;
+
+ public Report(String id, String description, List<Result> results, List<Parameter> parameters) {
+ this.id = id;
+ this.description = description;
+ this.results = ImmutableList.copyOf(results);
+ this.parameters = ImmutableList.copyOf(parameters);
+ }
+
+ public static class Builder {
+ private String id;
+ private String description = "";
+ private final ImmutableList.Builder<Result> results = new ImmutableList.Builder<>();
+ private final ImmutableList.Builder<Parameter> parameters = new ImmutableList.Builder<>();
+
+ private Builder() {}
+
+ public Builder id(String id) {
+ this.id = id;
+ return this;
+ }
+
+ public Builder description(String desc) {
+ this.description = desc;
+ return this;
+ }
+
+ public Builder result(String id, LongSummaryStatistics stats, String description) {
+ results.add(new Result(id, new Stats(stats.getMin(), stats.getMax(), stats.getSum(), stats.getAverage(), stats.getCount()), description,
+ Purpose.COMPARISON));
+ return this;
+ }
+
+ public Builder result(String id, Number data, String description) {
+ results.add(new Result(id, data, description, Purpose.COMPARISON));
+ return this;
+ }
+
+ public Builder result(String id, long amount, long time, String description) {
+ results.add(new Result(id, amount / (time / 1000.0), description, Purpose.COMPARISON));
+ return this;
+ }
+
+ public Builder info(String id, LongSummaryStatistics stats, String description) {
+ results.add(new Result(id, new Stats(stats.getMin(), stats.getMax(), stats.getSum(), stats.getAverage(), stats.getCount()), description,
+ Purpose.INFORMATIONAL));
+ return this;
+ }
+
+ public Builder info(String id, long amount, long time, String description) {
+ results.add(new Result(id, amount / (time / 1000.0), description, Purpose.INFORMATIONAL));
+ return this;
+ }
+
+ public Builder info(String id, Number data, String description) {
+ results.add(new Result(id, data, description, Purpose.INFORMATIONAL));
+ return this;
+ }
+
+ public Builder parameter(String id, Number data, String description) {
+ parameters.add(new Parameter(id, data.toString(), description));
+ return this;
+ }
+
+ public Builder parameter(String id, String data, String description) {
+ parameters.add(new Parameter(id, data, description));
+ return this;
+ }
+
+ public Report build() {
+ return new Report(id, description, results.build(), parameters.build());
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/Result.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/Result.java
new file mode 100644
index 0000000..78d07fa
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/Result.java
@@ -0,0 +1,54 @@
+/*
+ * 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.accumulo.testing.core.performance;
+
+public class Result {
+
+ public final String id;
+ public final Number data;
+ public final Stats stats;
+ public final String description;
+ public final Purpose purpose;
+
+ public enum Purpose {
+ /**
+ * Use for results that are unrelated to the main objective or are not useful for comparison.
+ */
+ INFORMATIONAL,
+ /**
+ * Use for results that related to the main objective and are useful for comparison.
+ */
+ COMPARISON
+ }
+
+ public Result(String id, Number data, String description, Purpose purpose) {
+ this.id = id;
+ this.data = data;
+ this.stats = null;
+ this.description = description;
+ this.purpose = purpose;
+ }
+
+ public Result(String id, Stats stats, String description, Purpose purpose) {
+ this.id = id;
+ this.stats = stats;
+ this.data = null;
+ this.description = description;
+ this.purpose = purpose;
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/Stats.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/Stats.java
new file mode 100644
index 0000000..cd9c859
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/Stats.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.accumulo.testing.core.performance;
+
+public class Stats {
+ public final long min;
+ public final long max;
+ public final long sum;
+ public final double average;
+ public final long count;
+
+ public Stats(long min, long max, long sum, double average, long count) {
+ this.min = min;
+ this.max = max;
+ this.sum = sum;
+ this.average = average;
+ this.count = count;
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/SystemConfiguration.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/SystemConfiguration.java
new file mode 100644
index 0000000..60f02e5
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/SystemConfiguration.java
@@ -0,0 +1,37 @@
+/*
+ * 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.accumulo.testing.core.performance;
+
+import java.util.Collections;
+import java.util.Map;
+
+import com.google.common.collect.ImmutableMap;
+
+public class SystemConfiguration {
+
+ private Map<String,String> accumuloSite = Collections.emptyMap();
+
+ public SystemConfiguration setAccumuloConfig(Map<String,String> props) {
+ accumuloSite = ImmutableMap.copyOf(props);
+ return this;
+ }
+
+ public Map<String,String> getAccumuloSite() {
+ return accumuloSite;
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/Compare.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/Compare.java
new file mode 100644
index 0000000..e14f47d
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/Compare.java
@@ -0,0 +1,117 @@
+/*
+ * 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.accumulo.testing.core.performance.impl;
+
+import java.io.BufferedReader;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+
+import org.apache.accumulo.testing.core.performance.Result;
+import org.apache.accumulo.testing.core.performance.Result.Purpose;
+
+import com.google.common.collect.Sets;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonStreamParser;
+
+public class Compare {
+
+ private static class TestId {
+
+ final String testClass;
+ final String id;
+
+ public TestId(String testClass, String id) {
+ this.testClass = testClass;
+ this.id = id;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(testClass, id);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+
+ if (obj instanceof TestId) {
+ TestId other = (TestId) obj;
+
+ return id.equals(other.id) && testClass.equals(other.testClass);
+ }
+
+ return false;
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ Map<TestId,Double> oldResults = flatten(readReports(args[0]));
+ Map<TestId,Double> newResults = flatten(readReports(args[1]));
+
+ for (TestId testId : Sets.union(oldResults.keySet(), newResults.keySet())) {
+ Double oldResult = oldResults.get(testId);
+ Double newResult = newResults.get(testId);
+
+ if (oldResult == null || newResult == null) {
+ System.out.printf("%s %s %.2f %.2f\n", testId.testClass, testId.id, oldResult, newResult);
+ } else {
+ double change = (newResult - oldResult) / oldResult;
+ System.out.printf("%s %s %.2f %.2f %.2f%s\n", testId.testClass, testId.id, oldResult, newResult, change * 100, "%");
+ }
+ }
+ }
+
+ static Collection<ContextualReport> readReports(String file) throws Exception {
+ try (BufferedReader reader = Files.newBufferedReader(Paths.get(file))) {
+ Gson gson = new GsonBuilder().create();
+ JsonStreamParser p = new JsonStreamParser(reader);
+ List<ContextualReport> rl = new ArrayList<>();
+
+ while (p.hasNext()) {
+ JsonElement e = p.next();
+ ContextualReport results = gson.fromJson(e, ContextualReport.class);
+ rl.add(results);
+ }
+
+ return rl;
+ }
+ }
+
+ private static Map<TestId,Double> flatten(Collection<ContextualReport> results) {
+ HashMap<TestId,Double> flattened = new HashMap<>();
+
+ for (ContextualReport cr : results) {
+ for (Result r : cr.results) {
+ if (r.purpose == Purpose.COMPARISON) {
+ flattened.put(new TestId(cr.testClass, r.id), r.data.doubleValue());
+ }
+ }
+ }
+
+ return flattened;
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/ContextualReport.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/ContextualReport.java
new file mode 100644
index 0000000..5104c31
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/ContextualReport.java
@@ -0,0 +1,39 @@
+/*
+ * 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.accumulo.testing.core.performance.impl;
+
+import java.time.Instant;
+
+import org.apache.accumulo.testing.core.performance.Report;
+
+public class ContextualReport extends Report {
+
+ public final String testClass;
+ public final String accumuloVersion;
+ public final String startTime;
+ public final String finishTime;
+
+ public ContextualReport(String testClass, String accumuloVersion, Instant startTime, Instant finishTime, Report r) {
+ super(r.id, r.description, r.results, r.parameters);
+ this.testClass = testClass;
+ this.accumuloVersion = accumuloVersion;
+ this.startTime = startTime.toString();
+ this.finishTime = finishTime.toString();
+
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/Csv.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/Csv.java
new file mode 100644
index 0000000..6433c3c
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/Csv.java
@@ -0,0 +1,129 @@
+/*
+ * 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.accumulo.testing.core.performance.impl;
+
+import static java.util.stream.Collectors.joining;
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toSet;
+
+import java.time.Instant;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.StringJoiner;
+import java.util.TreeMap;
+import java.util.stream.Stream;
+
+import org.apache.accumulo.testing.core.performance.Result;
+import org.apache.accumulo.testing.core.performance.Result.Purpose;
+
+import com.google.common.collect.Iterables;
+
+public class Csv {
+
+ private static class RowId implements Comparable<RowId> {
+
+ final Instant startTime;
+ final String accumuloVersion;
+
+ public RowId(Instant startTime, String accumuloVersion) {
+ this.startTime = startTime;
+ this.accumuloVersion = accumuloVersion;
+ }
+
+ @Override
+ public int compareTo(RowId o) {
+ int cmp = startTime.compareTo(o.startTime);
+ if (cmp == 0) {
+ cmp = accumuloVersion.compareTo(o.accumuloVersion);
+ }
+ return cmp;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(startTime, accumuloVersion);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof RowId) {
+ RowId orid = (RowId) o;
+ return startTime.equals(orid.startTime) && accumuloVersion.equals(orid.accumuloVersion);
+ }
+
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return startTime + " " + accumuloVersion;
+ }
+
+ }
+
+ public static void main(String[] args) throws Exception {
+ Map<RowId, Map<String, Double>> rows = new TreeMap<>();
+
+ for (String file : args) {
+ Collection<ContextualReport> reports = Compare.readReports(file);
+
+ Instant minStart = reports.stream().map(cr -> cr.startTime).map(Instant::parse).min(Instant::compareTo).get();
+
+ String version = Iterables.getOnlyElement(reports.stream().map(cr -> cr.accumuloVersion).collect(toSet()));
+
+ Map<String, Double> row = new HashMap<>();
+
+ for (ContextualReport report : reports) {
+
+ String id = report.id != null ? report.id : report.testClass.substring(report.testClass.lastIndexOf('.')+1);
+
+ for (Result result : report.results) {
+ if(result.purpose == Purpose.COMPARISON) {
+ row.put(id+"."+result.id, result.data.doubleValue());
+ }
+ }
+ }
+
+ rows.put(new RowId(minStart, version), row);
+ }
+
+ List<String> allCols = rows.values().stream().flatMap(row -> row.keySet().stream()).distinct().sorted().collect(toList());
+
+ // print header
+ print(Stream.concat(Stream.of("Start Time","Version"), allCols.stream()).collect(joining(",")));
+
+ rows.forEach((id, row)->{
+ StringJoiner joiner = new StringJoiner(",");
+ joiner.add(id.startTime.toString());
+ joiner.add(id.accumuloVersion);
+ for (String col : allCols) {
+ joiner.add(row.getOrDefault(col, Double.valueOf(0)).toString());
+ }
+
+ print(joiner.toString());
+ });
+
+ }
+
+ private static void print(String s) {
+ System.out.println(s);
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/ListTests.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/ListTests.java
new file mode 100644
index 0000000..f966765
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/ListTests.java
@@ -0,0 +1,37 @@
+/*
+ * 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.accumulo.testing.core.performance.impl;
+
+import org.apache.accumulo.testing.core.performance.PerformanceTest;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.reflect.ClassPath;
+import com.google.common.reflect.ClassPath.ClassInfo;
+
+public class ListTests {
+ public static void main(String[] args) throws Exception {
+
+ ImmutableSet<ClassInfo> classes = ClassPath.from(ListTests.class.getClassLoader()).getTopLevelClasses();
+
+ for (ClassInfo classInfo : classes) {
+ if (classInfo.getName().endsWith("PT") && PerformanceTest.class.isAssignableFrom(classInfo.load())) {
+ System.out.println(classInfo.getName());
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/MergeSiteConfig.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/MergeSiteConfig.java
new file mode 100644
index 0000000..2f79a88
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/MergeSiteConfig.java
@@ -0,0 +1,52 @@
+/*
+ * 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.accumulo.testing.core.performance.impl;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayOutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import org.apache.accumulo.testing.core.performance.PerformanceTest;
+import org.apache.hadoop.conf.Configuration;
+
+public class MergeSiteConfig {
+ public static void main(String[] args) throws Exception {
+ String className = args[0];
+ Path confFile = Paths.get(args[1], "accumulo-site.xml");
+
+ PerformanceTest perfTest = Class.forName(className).asSubclass(PerformanceTest.class).newInstance();
+
+ Configuration conf = new Configuration(false);
+ byte[] newConf;
+
+
+ try(BufferedInputStream in = new BufferedInputStream(Files.newInputStream(confFile))){
+ conf.addResource(in);
+ perfTest.getConfiguration().getAccumuloSite().forEach((k,v) -> conf.set(k, v));
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ conf.writeXml(baos);
+ baos.close();
+ newConf = baos.toByteArray();
+ }
+
+
+ Files.write(confFile, newConf);
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/PerfTestRunner.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/PerfTestRunner.java
new file mode 100644
index 0000000..4ce6b1a
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/impl/PerfTestRunner.java
@@ -0,0 +1,70 @@
+/*
+ * 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.accumulo.testing.core.performance.impl;
+
+import java.io.Writer;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.time.Instant;
+import java.time.ZoneId;
+import java.time.format.DateTimeFormatter;
+
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.testing.core.performance.Environment;
+import org.apache.accumulo.testing.core.performance.PerformanceTest;
+import org.apache.accumulo.testing.core.performance.Report;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+public class PerfTestRunner {
+ public static void main(String[] args) throws Exception {
+ String clientProps = args[0];
+ String className = args[1];
+ String accumuloVersion = args[2];
+ String outputDir = args[3];
+
+ PerformanceTest perfTest = Class.forName(className).asSubclass(PerformanceTest.class).newInstance();
+
+ Connector conn = Connector.builder().usingProperties(clientProps).build();
+
+ Instant start = Instant.now();
+
+ Report result = perfTest.runTest(new Environment() {
+ @Override
+ public Connector getConnector() {
+ return conn;
+ }
+ });
+
+ Instant stop = Instant.now();
+
+ Gson gson = new GsonBuilder().setPrettyPrinting().create();
+
+ ContextualReport report = new ContextualReport(className, accumuloVersion, start, stop, result);
+
+ DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
+ String time = Instant.now().atZone(ZoneId.systemDefault()).format(formatter);
+ Path outputFile = Paths.get(outputDir, perfTest.getClass().getSimpleName() + "_" + time + ".json");
+
+ try (Writer writer = Files.newBufferedWriter(outputFile)) {
+ gson.toJson(report, writer);
+ }
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/tests/ScanExecutorPT.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/tests/ScanExecutorPT.java
new file mode 100644
index 0000000..ecfa182
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/tests/ScanExecutorPT.java
@@ -0,0 +1,177 @@
+/*
+ * 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.accumulo.testing.core.performance.tests;
+
+import java.util.HashMap;
+import java.util.LongSummaryStatistics;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Random;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.Scanner;
+import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.client.admin.NewTableConfiguration;
+import org.apache.accumulo.core.conf.Property;
+import org.apache.accumulo.core.data.Key;
+import org.apache.accumulo.core.data.Range;
+import org.apache.accumulo.core.data.Value;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.core.spi.scan.IdleRatioScanPrioritizer;
+import org.apache.accumulo.testing.core.performance.Environment;
+import org.apache.accumulo.testing.core.performance.PerformanceTest;
+import org.apache.accumulo.testing.core.performance.Report;
+import org.apache.accumulo.testing.core.performance.SystemConfiguration;
+import org.apache.accumulo.testing.core.performance.util.TestData;
+import org.apache.accumulo.testing.core.performance.util.TestExecutor;
+import org.apache.hadoop.io.Text;
+
+import com.google.common.collect.Iterables;
+
+public class ScanExecutorPT implements PerformanceTest {
+
+ private static final int NUM_SHORT_SCANS_THREADS = 5;
+ private static final int NUM_LONG_SCANS = 50;
+
+ private static final int NUM_ROWS = 10000;
+ private static final int NUM_FAMS = 10;
+ private static final int NUM_QUALS = 10;
+
+ private static final String SCAN_EXECUTOR_THREADS = "2";
+ private static final String SCAN_PRIORITIZER = IdleRatioScanPrioritizer.class.getName();
+
+ private static final String TEST_DESC = "Scan Executor Test. Test running lots of short scans while long scans are running in the background. Each short scan reads a random row and family. A scan prioritizer that favors short scans is configured. If the scan prioritizer is not working properly, then the short "
+ + "scans will be orders of magnitude slower.";
+
+ @Override
+ public SystemConfiguration getConfiguration() {
+ Map<String,String> siteCfg = new HashMap<>();
+
+ siteCfg.put(Property.TSERV_SCAN_MAX_OPENFILES.getKey(), "200");
+ siteCfg.put(Property.TSERV_MINTHREADS.getKey(), "200");
+ siteCfg.put(Property.TSERV_SCAN_EXECUTORS_PREFIX.getKey() + "se1.threads", SCAN_EXECUTOR_THREADS);
+ siteCfg.put(Property.TSERV_SCAN_EXECUTORS_PREFIX.getKey() + "se1.prioritizer", SCAN_PRIORITIZER);
+
+ return new SystemConfiguration().setAccumuloConfig(siteCfg);
+ }
+
+ @Override
+ public Report runTest(Environment env) throws Exception {
+
+ String tableName = "scept";
+
+ Map<String,String> props = new HashMap<>();
+ props.put(Property.TABLE_SCAN_DISPATCHER_OPTS.getKey() + "executor", "se1");
+
+ env.getConnector().tableOperations().create(tableName, new NewTableConfiguration().setProperties(props));
+
+ long t1 = System.currentTimeMillis();
+ TestData.generate(env.getConnector(), tableName, NUM_ROWS, NUM_FAMS, NUM_QUALS);
+ long t2 = System.currentTimeMillis();
+ env.getConnector().tableOperations().compact(tableName, null, null, true, true);
+ long t3 = System.currentTimeMillis();
+
+ AtomicBoolean stop = new AtomicBoolean(false);
+
+ TestExecutor<Long> longScans = startLongScans(env, tableName, stop);
+
+ LongSummaryStatistics shortStats1 = runShortScans(env, tableName, 50000);
+ LongSummaryStatistics shortStats2 = runShortScans(env, tableName, 500000);
+
+ stop.set(true);
+ long t4 = System.currentTimeMillis();
+
+ LongSummaryStatistics longStats = longScans.stream().mapToLong(l -> l).summaryStatistics();
+
+ longScans.close();
+
+ Report.Builder builder = Report.builder();
+
+ builder.id("sexec").description(TEST_DESC);
+ builder.info("write", NUM_ROWS * NUM_FAMS * NUM_QUALS, t2 - t1, "Data write rate entries/sec ");
+ builder.info("compact", NUM_ROWS * NUM_FAMS * NUM_QUALS, t3 - t2, "Compact rate entries/sec ");
+ builder.info("short_times1", shortStats1, "Times in ms for each short scan. First run.");
+ builder.info("short_times2", shortStats2, "Times in ms for each short scan. Second run.");
+ builder.result("short", shortStats2.getAverage(), "Average times in ms for short scans from 2nd run.");
+ builder.info("long_counts", longStats, "Entries read by each long scan threads");
+ builder.info("long", longStats.getSum(), (t4-t3), "Combined rate in entries/second of all long scans");
+ builder.parameter("short_threads", NUM_SHORT_SCANS_THREADS, "Threads used to run short scans.");
+ builder.parameter("long_threads", NUM_LONG_SCANS, "Threads running long scans. Each thread repeatedly scans entire table for duration of test.");
+ builder.parameter("rows", NUM_ROWS, "Rows in test table");
+ builder.parameter("familes", NUM_FAMS, "Families per row in test table");
+ builder.parameter("qualifiers", NUM_QUALS, "Qualifiers per family in test table");
+ builder.parameter("server_scan_threads", SCAN_EXECUTOR_THREADS, "Server side scan handler threads");
+ builder.parameter("prioritizer", SCAN_PRIORITIZER, "Server side scan prioritizer");
+
+ return builder.build();
+ }
+
+ private static long scan(String tableName, Connector c, byte[] row, byte[] fam) throws TableNotFoundException {
+ long t1 = System.currentTimeMillis();
+ int count = 0;
+ try (Scanner scanner = c.createScanner(tableName, Authorizations.EMPTY)) {
+ scanner.setRange(Range.exact(new Text(row), new Text(fam)));
+ if (Iterables.size(scanner) != NUM_QUALS) {
+ throw new RuntimeException("bad count " + count);
+ }
+ }
+
+ return System.currentTimeMillis() - t1;
+ }
+
+ private long scan(String tableName, Connector c, AtomicBoolean stop) throws TableNotFoundException {
+ long count = 0;
+ while (!stop.get()) {
+ try (Scanner scanner = c.createScanner(tableName, Authorizations.EMPTY)) {
+ for (Entry<Key,Value> entry : scanner) {
+ count++;
+ if (stop.get()) {
+ return count;
+ }
+ }
+ }
+ }
+ return count;
+ }
+
+ private LongSummaryStatistics runShortScans(Environment env, String tableName, int numScans) throws InterruptedException, ExecutionException {
+
+ try(TestExecutor<Long> executor = new TestExecutor<>(NUM_SHORT_SCANS_THREADS)) {
+ Random rand = new Random();
+
+ for (int i = 0; i < numScans; i++) {
+ byte[] row = TestData.row(rand.nextInt(NUM_ROWS));
+ byte[] fam = TestData.fam(rand.nextInt(NUM_FAMS));
+ executor.submit(() -> scan(tableName, env.getConnector(), row, fam));
+ }
+
+ return executor.stream().mapToLong(l -> l).summaryStatistics();
+ }
+ }
+
+ private TestExecutor<Long> startLongScans(Environment env, String tableName, AtomicBoolean stop) {
+ TestExecutor<Long> longScans = new TestExecutor<>(NUM_LONG_SCANS);
+
+ for (int i = 0; i < NUM_LONG_SCANS; i++) {
+ longScans.submit(() -> scan(tableName, env.getConnector(), stop));
+ }
+ return longScans;
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/tests/ScanFewFamiliesPT.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/tests/ScanFewFamiliesPT.java
new file mode 100644
index 0000000..3dcf261
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/tests/ScanFewFamiliesPT.java
@@ -0,0 +1,114 @@
+/*
+ * 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.accumulo.testing.core.performance.tests;
+
+import java.util.HashSet;
+import java.util.LongSummaryStatistics;
+import java.util.Random;
+import java.util.Set;
+
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.client.Scanner;
+import org.apache.accumulo.core.client.TableNotFoundException;
+import org.apache.accumulo.core.security.Authorizations;
+import org.apache.accumulo.testing.core.performance.Environment;
+import org.apache.accumulo.testing.core.performance.PerformanceTest;
+import org.apache.accumulo.testing.core.performance.Report;
+import org.apache.accumulo.testing.core.performance.SystemConfiguration;
+import org.apache.accumulo.testing.core.performance.util.TestData;
+import org.apache.hadoop.io.Text;
+
+import com.google.common.base.Strings;
+import com.google.common.collect.Iterables;
+
+public class ScanFewFamiliesPT implements PerformanceTest {
+
+ private static final String DESC = "This test times fetching a few column famlies when rows have many column families.";
+
+ private static final int NUM_ROWS = 500;
+ private static final int NUM_FAMS = 10000;
+ private static final int NUM_QUALS = 1;
+
+ @Override
+ public SystemConfiguration getConfiguration() {
+ return new SystemConfiguration();
+ }
+
+ @Override
+ public Report runTest(Environment env) throws Exception {
+
+ String tableName = "bigFamily";
+
+ env.getConnector().tableOperations().create(tableName);
+
+ long t1 = System.currentTimeMillis();
+ TestData.generate(env.getConnector(), tableName, NUM_ROWS, NUM_FAMS, NUM_QUALS);
+ long t2 = System.currentTimeMillis();
+ env.getConnector().tableOperations().compact(tableName, null, null, true, true);
+ long t3 = System.currentTimeMillis();
+ // warm up run
+ runScans(env, tableName, 1);
+
+ Report.Builder builder = Report.builder();
+
+ for (int numFams : new int[] {1, 2, 4, 8, 16}) {
+ LongSummaryStatistics stats = runScans(env, tableName, numFams);
+ String fams = Strings.padStart(numFams + "", 2, '0');
+ builder.info("f" + fams + "_stats", stats, "Times in ms to fetch " + numFams + " families from all rows");
+ builder.result("f" + fams, stats.getAverage(), "Average time in ms to fetch " + numFams + " families from all rows");
+ }
+
+ builder.id("sfewfam");
+ builder.description(DESC);
+ builder.info("write", NUM_ROWS * NUM_FAMS * NUM_QUALS, t2 - t1, "Data write rate entries/sec ");
+ builder.info("compact", NUM_ROWS * NUM_FAMS * NUM_QUALS, t3 - t2, "Compact rate entries/sec ");
+ builder.parameter("rows", NUM_ROWS, "Rows in test table");
+ builder.parameter("familes", NUM_FAMS, "Families per row in test table");
+ builder.parameter("qualifiers", NUM_QUALS, "Qualifiers per family in test table");
+
+ return builder.build();
+ }
+
+ private LongSummaryStatistics runScans(Environment env, String tableName, int numFamilies) throws TableNotFoundException {
+ Random rand = new Random();
+ LongSummaryStatistics stats = new LongSummaryStatistics();
+ for (int i = 0; i < 50; i++) {
+ stats.accept(scan(tableName, env.getConnector(), rand, numFamilies));
+ }
+ return stats;
+ }
+
+ private static long scan(String tableName, Connector c, Random rand, int numFamilies) throws TableNotFoundException {
+
+ Set<Text> families = new HashSet<>(numFamilies);
+ while(families.size() < numFamilies) {
+ families.add(new Text(TestData.fam(rand.nextInt(NUM_FAMS))));
+ }
+
+ long t1 = System.currentTimeMillis();
+ int count = 0;
+ try (Scanner scanner = c.createScanner(tableName, Authorizations.EMPTY)) {
+ families.forEach(scanner::fetchColumnFamily);
+ if (Iterables.size(scanner) != NUM_ROWS * NUM_QUALS * numFamilies) {
+ throw new RuntimeException("bad count " + count);
+ }
+ }
+
+ return System.currentTimeMillis() - t1;
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/util/TestData.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/util/TestData.java
new file mode 100644
index 0000000..a626a25
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/util/TestData.java
@@ -0,0 +1,62 @@
+/*
+ * 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.accumulo.testing.core.performance.util;
+
+import org.apache.accumulo.core.client.BatchWriter;
+import org.apache.accumulo.core.client.Connector;
+import org.apache.accumulo.core.data.Mutation;
+import org.apache.accumulo.core.util.FastFormat;
+
+public class TestData {
+
+ private static final byte[] EMPTY = new byte[0];
+
+ public static byte[] row(long r) {
+ return FastFormat.toZeroPaddedString(r, 16, 16, EMPTY);
+ }
+
+ public static byte[] fam(int f) {
+ return FastFormat.toZeroPaddedString(f, 8, 16, EMPTY);
+ }
+
+ public static byte[] qual(int q) {
+ return FastFormat.toZeroPaddedString(q, 8, 16, EMPTY);
+ }
+
+ public static byte[] val(long v) {
+ return FastFormat.toZeroPaddedString(v, 9, 16, EMPTY);
+ }
+
+ public static void generate(Connector conn, String tableName, int rows, int fams, int quals) throws Exception {
+ try (BatchWriter writer = conn.createBatchWriter(tableName)) {
+ int v = 0;
+ for (int r = 0; r < rows; r++) {
+ Mutation m = new Mutation(row(r));
+ for (int f = 0; f < fams; f++) {
+ byte[] fam = fam(f);
+ for (int q = 0; q < quals; q++) {
+ byte[] qual = qual(q);
+ byte[] val = val(v++);
+ m.put(fam, qual, val);
+ }
+ }
+ writer.addMutation(m);
+ }
+ }
+ }
+}
diff --git a/core/src/main/java/org/apache/accumulo/testing/core/performance/util/TestExecutor.java b/core/src/main/java/org/apache/accumulo/testing/core/performance/util/TestExecutor.java
new file mode 100644
index 0000000..c4530a4
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/testing/core/performance/util/TestExecutor.java
@@ -0,0 +1,59 @@
+/*
+ * 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.accumulo.testing.core.performance.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.stream.Stream;
+
+public class TestExecutor<T> implements Iterable<T>, AutoCloseable {
+ private ExecutorService es;
+ private List<Future<T>> futures = new ArrayList<>();
+
+ public TestExecutor(int numThreads) {
+ es = Executors.newFixedThreadPool(numThreads);
+ }
+
+ public void submit(Callable<T> task) {
+ futures.add(es.submit(task));
+ }
+
+ @Override
+ public void close() {
+ es.shutdownNow();
+ }
+
+ public Stream<T> stream() {
+ return futures.stream().map(f -> {try {
+ return f.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw new RuntimeException(e);
+ }});
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ return stream().iterator();
+ }
+}