├── .gitignore
├── .whitesource
├── ConfigurationExample.conf
├── LICENSE
├── README.md
├── docker
├── Dockerfile
└── go.sh
├── pom.xml
└── src
└── main
├── java
└── io
│ └── logz
│ └── benchmarks
│ └── elasticsearch
│ ├── ElasticsearchBenchmarkToolMain.java
│ ├── benchmark
│ ├── BenchmarkPlan.java
│ └── BenchmarkStep.java
│ ├── configuration
│ ├── BaseConfiguration.java
│ ├── ConfigurationParser.java
│ ├── ElasticsearchConfiguration.java
│ ├── IndexingConfiguration.java
│ ├── OptimizeConfiguration.java
│ └── SearchConfiguration.java
│ ├── controllers
│ ├── BaseController.java
│ ├── IndexingController.java
│ ├── NoopController.java
│ ├── OptimizeController.java
│ └── SearchController.java
│ ├── elasticsearch
│ └── ElasticsearchController.java
│ ├── exceptions
│ ├── CouldNotCompleteBulkOperationException.java
│ ├── CouldNotExecuteSearchException.java
│ ├── CouldNotOptimizeException.java
│ └── InvalidConfigurationException.java
│ └── metrics
│ ├── GeneralMbean.java
│ ├── IndexingMbean.java
│ └── SearchMbean.java
└── resources
├── logback.xml
└── templates
├── documents
├── document1.json
├── document2.json
├── document3.json
├── document4.json
└── document5.json
└── searches
├── datehistogram-with-sub-range-agg-2-hours.json
├── datehistogram-with-terms-and-sub-cardinality-30-minutes.json
├── kibana-discover-last-30-minutes.json
├── simple-search-return-all.json
└── terms-10000-size-with-complex-query.json
/.gitignore:
--------------------------------------------------------------------------------
1 | *.class
2 |
3 | # Mobile Tools for Java (J2ME)
4 | .mtj.tmp/
5 |
6 | # Package Files #
7 | *.jar
8 | *.war
9 | *.ear
10 |
11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
12 | hs_err_pid*
13 | /elasticsearch-benchmark-tool.iml
14 | /.idea/
15 | /target/*
16 |
--------------------------------------------------------------------------------
/.whitesource:
--------------------------------------------------------------------------------
1 | {
2 | "generalSettings": {
3 | "shouldScanRepo": true
4 | },
5 | "checkRunSettings": {
6 | "vulnerableCheckRunConclusionLevel": "failure"
7 | }
8 | }
--------------------------------------------------------------------------------
/ConfigurationExample.conf:
--------------------------------------------------------------------------------
1 | # Hocon based configuration
2 |
3 | elasticsearch = {
4 | elasticsearchAddress = "es"
5 | elasticsearchProtocol = "http"
6 | elasticsearchPort = 9200
7 | numberOfShards = 1
8 | numberOfReplicas = 0
9 | userName = "user"
10 | password = "password"
11 | indexPrefix = "index-prefix-"
12 | documentsPath = "/templates/documents"
13 | searchesPath = "/templates/searches"
14 | }
15 |
16 | steps = [
17 |
18 | {
19 | duration = "10m"
20 | indexing = {
21 | numberOfThreads = 30
22 | }
23 | search = {
24 | numberOfThreads = 5
25 | }
26 | }
27 | {
28 | duration = "5s"
29 | indexing = {
30 | }
31 | search = {
32 | numberOfThreads = 5
33 | }
34 | optimize = {
35 | numberOfSegments = 1
36 | }
37 | }
38 | {
39 | duration = "1s"
40 | noop = {
41 | }
42 | }
43 | {
44 | duration = "10s"
45 | search = {
46 | numberOfThreads = 2
47 | }
48 | }
49 | ]
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # THIS PROJECT IS NO LONGER MAINTAINED
2 |
3 | # elasticsearch-benchmark-tool
4 | Stress test tool that benchmark indexing and searching in Elasticsearch
5 |
6 |
7 | ## How it is working
8 | The tool is getting a configuration file, which represent the test plan.
9 | Each step can have 1 or more of the following controllers:
10 | - Indexing - Index 1k bulks of log like documents (5 different options, with some fields getting different data based on common cardinality factor and always changing timestamp)
11 | - Search - Iterate over 5 different Kibana like searches
12 | - Optimize - Force merge the index
13 | - Noop - Do nothing
14 |
15 | The tool produce metrics as JMX counters, and the [Jmx2Graphite](https://github.com/logzio/jmx2graphite) tool is sending those to your graphite server.
16 |
17 | ## Configuration Example
18 | Can be found under ConfigurationExample.conf
19 |
20 | ## Run
21 | ```bash
22 | docker run --rm -it -e GRAPHITE_SERVER="your-graphite-server.com" \
23 | -e GRAPHITE_PREFIX="Prefix.under.graphite.root" \
24 | -e SERVICE_HOST="BENCHMARK_TEST_NAME" \
25 | -v /your/configuration.conf:/config.conf \
26 | -v /your/templates:/templates \
27 | logzio/elasticsearch-benchmark-tool
28 | ```
29 |
30 | ## Build
31 | ```bash
32 | mvn clean package docker:build
33 | ```
34 |
--------------------------------------------------------------------------------
/docker/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM openjdk:8-alpine
2 |
3 | MAINTAINER roi@logz.io
4 |
5 | RUN apk add --no-cache bash
6 |
7 | ADD go.sh /root
8 |
9 | RUN chmod a+x /root/go.sh
10 |
11 | CMD /root/go.sh
12 |
13 | ADD packages/ /root
--------------------------------------------------------------------------------
/docker/go.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ "$GRAPHITE_SERVER" == "" ]; then
4 |
5 | echo "GRAPHITE_SERVER must be passed as environment variable!"
6 | exit 1
7 | fi
8 |
9 | if [ "$GRAPHITE_PREFIX" == "" ]; then
10 |
11 | echo "GRAPHITE_PREFIX must be passed as environment variable!"
12 | exit 1
13 | fi
14 |
15 | if [ "$SERVICE_HOST" == "" ]; then
16 |
17 | echo "SERVICE_HOST must be passed as environment variable!"
18 | exit 1
19 | fi
20 |
21 | if [ "$INTERVAL_IN_SEC" == "" ]; then
22 | INTERVAL_IN_SEC=10
23 | fi
24 |
25 | if [ "$CONFIG_FILE" == "" ]; then
26 | CONFIG_FILE="/config.conf"
27 | fi
28 |
29 | # Find the jar
30 | JAR=`ls /root/ | grep elasticsearch`
31 | JAVA_AGENT="-javaagent:/root/jmx2graphite-1.2.4-javaagent.jar=GRAPHITE_HOSTNAME=$GRAPHITE_SERVER;SERVICE_NAME=$GRAPHITE_PREFIX;SERVICE_HOST=$SERVICE_HOST;INTERVAL_IN_SEC=$INTERVAL_IN_SEC"
32 |
33 | java $JAVA_AGENT -jar /root/$JAR --test-config $CONFIG_FILE
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | io.logz
8 | elasticsearch-benchmark-tool
9 | 1.0
10 | jar
11 |
12 |
13 |
14 | org.apache.maven.plugins
15 | maven-compiler-plugin
16 | 3.5.1
17 |
18 | 1.8
19 | 1.8
20 |
21 |
22 |
23 |
24 | org.apache.maven.plugins
25 | maven-assembly-plugin
26 | 2.6
27 |
28 |
29 | jar-with-dependencies
30 |
31 |
32 |
33 | io.logz.benchmarks.elasticsearch.ElasticsearchBenchmarkToolMain
34 |
35 |
36 |
37 |
38 |
39 | make-assembly
40 | package
41 |
42 | single
43 |
44 |
45 |
46 |
47 |
48 |
49 | com.spotify
50 | docker-maven-plugin
51 | 0.4.13
52 |
53 | logzio/elasticsearch-benchmark-tool
54 | docker
55 |
56 |
57 | /packages
58 | ${project.build.directory}
59 | ${project.build.finalName}-jar-with-dependencies.jar
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | commons-cli
70 | commons-cli
71 | 1.3.1
72 |
73 |
74 | com.google.guava
75 | guava
76 | 19.0
77 |
78 |
79 | ch.qos.logback
80 | logback-classic
81 | 1.1.7
82 |
83 |
84 | ch.qos.logback
85 | logback-core
86 | 1.1.7
87 |
88 |
89 | org.slf4j
90 | slf4j-api
91 | 1.7.21
92 |
93 |
94 | com.udojava
95 | JMXWrapper
96 | 1.2
97 |
98 |
99 | io.searchbox
100 | jest
101 | 2.0.4
102 |
103 |
104 | org.elasticsearch
105 | elasticsearch
106 | 2.4.0
107 |
108 |
109 | com.typesafe
110 | config
111 | 1.3.0
112 |
113 |
114 | com.fasterxml.jackson.core
115 | jackson-core
116 | 2.9.9
117 |
118 |
119 | com.fasterxml.jackson.core
120 | jackson-databind
121 | 2.9.10.8
122 |
123 |
124 | org.reflections
125 | reflections
126 | 0.9.10
127 |
128 |
129 |
130 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/ElasticsearchBenchmarkToolMain.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch;
2 | import com.udojava.jmx.wrapper.JMXBeanWrapper;
3 | import io.logz.benchmarks.elasticsearch.benchmark.BenchmarkPlan;
4 | import io.logz.benchmarks.elasticsearch.configuration.ConfigurationParser;
5 | import io.logz.benchmarks.elasticsearch.exceptions.InvalidConfigurationException;
6 | import io.logz.benchmarks.elasticsearch.metrics.GeneralMbean;
7 | import io.logz.benchmarks.elasticsearch.metrics.IndexingMbean;
8 | import io.logz.benchmarks.elasticsearch.metrics.SearchMbean;
9 | import org.apache.commons.cli.CommandLine;
10 | import org.apache.commons.cli.CommandLineParser;
11 | import org.apache.commons.cli.DefaultParser;
12 | import org.apache.commons.cli.HelpFormatter;
13 | import org.apache.commons.cli.Option;
14 | import org.apache.commons.cli.Options;
15 | import org.apache.commons.cli.ParseException;
16 | import org.slf4j.Logger;
17 | import org.slf4j.LoggerFactory;
18 |
19 | import javax.management.InstanceAlreadyExistsException;
20 | import javax.management.IntrospectionException;
21 | import javax.management.MBeanRegistrationException;
22 | import javax.management.MBeanServer;
23 | import javax.management.MalformedObjectNameException;
24 | import javax.management.NotCompliantMBeanException;
25 | import javax.management.ObjectName;
26 | import java.lang.management.ManagementFactory;
27 |
28 | /**
29 | * Created by roiravhon on 9/19/16.
30 | */
31 | public class ElasticsearchBenchmarkToolMain {
32 |
33 | private static final Logger logger = LoggerFactory.getLogger(ElasticsearchBenchmarkToolMain.class);
34 |
35 | public static void main(String[] args) {
36 | try {
37 |
38 | CommandLine cmd = parseCliArguments(args);
39 | String configFile = cmd.getOptionValue("test-config", null);
40 |
41 | registerMbeans();
42 | BenchmarkPlan plan = ConfigurationParser.parseConfiguration(configFile);
43 |
44 | // Make sure we cleanup nicely, no matter what
45 | Runtime.getRuntime().addShutdownHook(new Thread(() -> {
46 | plan.abortPlan();
47 | plan.cleanupPlan();
48 | }));
49 |
50 | plan.execute();
51 | plan.printStats();
52 | System.exit(0);
53 | }
54 | catch (RuntimeException e) {
55 | logger.error(e.getMessage(), e);
56 | logger.error("This is fatal, bailing out..");
57 | System.exit(1);
58 | } catch (InvalidConfigurationException e) {
59 | logger.error("You configuration is invalid!");
60 | logger.error(e.getMessage(), e);
61 | System.exit(1);
62 | }
63 | }
64 |
65 | private static CommandLine parseCliArguments(String[] args) {
66 | Options options = new Options();
67 | logger.info("Parsing CLI arguments");
68 |
69 | Option testConfig = new Option("t", "test-config", true, "The test configuration");
70 | testConfig.setRequired(true);
71 | options.addOption(testConfig);
72 |
73 | CommandLineParser parser = new DefaultParser();
74 | HelpFormatter formatter = new HelpFormatter();
75 |
76 | try {
77 | return parser.parse(options, args);
78 |
79 | } catch (ParseException e) {
80 | System.out.println(e.getMessage());
81 | formatter.printHelp("elasticsearch-benchmark-tool", options);
82 |
83 | throw new RuntimeException();
84 | }
85 | }
86 |
87 | private static void registerMbeans() {
88 | try {
89 | MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
90 |
91 | IndexingMbean indexingMbean = IndexingMbean.getInstance();
92 | SearchMbean searchMbean = SearchMbean.getInstance();
93 | GeneralMbean generalMbean = GeneralMbean.getInstance();
94 |
95 | JMXBeanWrapper indexingMbeanWrapper = new JMXBeanWrapper(indexingMbean);
96 | JMXBeanWrapper searchMbeanWrapper = new JMXBeanWrapper(searchMbean);
97 | JMXBeanWrapper generalMbeanWrapper = new JMXBeanWrapper(generalMbean);
98 |
99 | mbs.registerMBean(indexingMbeanWrapper, new ObjectName("io.logz.benchmarks.elasticsearch:type=Indexing,name=Indexing Metrics"));
100 | mbs.registerMBean(searchMbeanWrapper, new ObjectName("io.logz.benchmarks.elasticsearch:type=Search,name=Search Metrics"));
101 | mbs.registerMBean(generalMbeanWrapper, new ObjectName("io.logz.benchmarks.elasticsearch:type=General,name=General Metrics"));
102 |
103 | } catch (IntrospectionException | MalformedObjectNameException | NotCompliantMBeanException |
104 | InstanceAlreadyExistsException | MBeanRegistrationException e) {
105 | throw new RuntimeException("Could not initialize JMX metrics!", e);
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/benchmark/BenchmarkPlan.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.benchmark;
2 |
3 | import io.logz.benchmarks.elasticsearch.configuration.ElasticsearchConfiguration;
4 | import io.logz.benchmarks.elasticsearch.elasticsearch.ElasticsearchController;
5 | import io.logz.benchmarks.elasticsearch.metrics.GeneralMbean;
6 | import io.logz.benchmarks.elasticsearch.metrics.IndexingMbean;
7 | import io.logz.benchmarks.elasticsearch.metrics.SearchMbean;
8 |
9 | import java.util.LinkedList;
10 | import java.util.List;
11 |
12 | /**
13 | * Created by roiravhon on 9/19/16.
14 | */
15 | public class BenchmarkPlan {
16 |
17 | private final List benchmarkSteps;
18 | private final ElasticsearchController esController;
19 | private final GeneralMbean generalMbean;
20 | private BenchmarkStep currentStep;
21 | private boolean aborting = false;
22 | private boolean done = false;
23 |
24 | public BenchmarkPlan(ElasticsearchConfiguration esConfig) {
25 | benchmarkSteps = new LinkedList<>();
26 | esController = new ElasticsearchController(esConfig);
27 | generalMbean = GeneralMbean.getInstance();
28 | }
29 |
30 | public void addStep(BenchmarkStep step) {
31 | benchmarkSteps.add(step);
32 | }
33 |
34 | public void execute() {
35 | prepareForExecution();
36 | benchmarkSteps.forEach(benchmarkStep -> {
37 | if (!aborting) {
38 | currentStep = benchmarkStep;
39 | benchmarkStep.executeStep();
40 | generalMbean.incrementStep();
41 | }
42 | });
43 |
44 | done = true;
45 | }
46 |
47 | public void printStats() {
48 |
49 | IndexingMbean indexingMbean = IndexingMbean.getInstance();
50 | SearchMbean searchMbean = SearchMbean.getInstance();
51 | GeneralMbean generalMbean = GeneralMbean.getInstance();
52 |
53 | System.out.println("##############################################################################");
54 | System.out.println("");
55 |
56 | System.out.println("Benchmark statistics (" + generalMbean.getCurrStep() + " steps):");
57 | System.out.println("");
58 |
59 | System.out.println("\t Total documents successfully indexed: " + indexingMbean.getNumberOfSuccessfulDocumentsIndexed());
60 | System.out.println("\t Total documents that fail to index: " + indexingMbean.getNumberOfFailedDocumentsIndexed());
61 | System.out.println("");
62 |
63 | System.out.println("\t Total successful searches " + searchMbean.getNumberOfSuccessfulSearches());
64 | System.out.println("\t Total failed searches " + searchMbean.getNumberOfFailedSearches());
65 | System.out.println("\t Total documents fetched " + searchMbean.getNumberOfFetchedDocuments());
66 | System.out.println("\t Total successful query time, in MS " + searchMbean.getTotalSuccessfulSearchesTimeMs());
67 | System.out.println("\t Average successful query time, in MS " + searchMbean.getAverageSearchTimeMs());
68 | System.out.println("");
69 |
70 | System.out.println("");
71 | System.out.println("##############################################################################");
72 | }
73 |
74 | public void cleanupPlan() {
75 | esController.deleteIndex();
76 | }
77 |
78 | public void abortPlan() {
79 | if (!done) {
80 | aborting = true;
81 | if (currentStep != null)
82 | currentStep.abortStep();
83 | }
84 | }
85 |
86 | public ElasticsearchController getEsController() {
87 | return esController;
88 | }
89 |
90 | private void prepareForExecution() {
91 | esController.createIndex();
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/benchmark/BenchmarkStep.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.benchmark;
2 |
3 | import io.logz.benchmarks.elasticsearch.controllers.BaseController;
4 | import io.logz.benchmarks.elasticsearch.controllers.IndexingController;
5 | import io.logz.benchmarks.elasticsearch.controllers.SearchController;
6 | import org.slf4j.Logger;
7 | import org.slf4j.LoggerFactory;
8 |
9 | import java.util.LinkedList;
10 | import java.util.List;
11 |
12 | /**
13 | * Created by roiravhon on 9/19/16.
14 | */
15 | public class BenchmarkStep {
16 |
17 | private static final Logger logger = LoggerFactory.getLogger(BenchmarkStep.class);
18 | private final List controllers;
19 | private final long durationMillis;
20 |
21 | public BenchmarkStep(long durationMillis) {
22 | this.durationMillis = durationMillis;
23 | controllers = new LinkedList<>();
24 | }
25 |
26 | public void addController(BaseController controller) {
27 | controllers.add(controller);
28 | }
29 |
30 | public void executeStep() {
31 |
32 | logger.info("Starting to execute step: " + toString());
33 | controllers.forEach(BaseController::run);
34 |
35 | try {
36 | logger.info("Step started. Waiting for {} millis.", durationMillis);
37 | Thread.sleep(durationMillis);
38 | logger.info("Time out!");
39 |
40 | } catch (InterruptedException e) {
41 | logger.error("Got interrupted while running! gracefully shutting down.");
42 |
43 | } finally {
44 | logger.info("Stopping step: {}", toString());
45 | controllers.forEach(BaseController::stop);
46 | }
47 | }
48 |
49 | public void abortStep() {
50 | logger.info("Aborting step: {}", toString());
51 | controllers.forEach(BaseController::stop);
52 | }
53 |
54 | @Override
55 | public String toString() {
56 |
57 | StringBuilder allControllersNames = new StringBuilder();
58 | controllers.forEach(controller -> allControllersNames.append(controller.getControllerName()).append(", "));
59 |
60 | return "Controllers: " + allControllersNames.toString() + "for: " + durationMillis + " ms";
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/configuration/BaseConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.configuration;
2 |
3 | import io.logz.benchmarks.elasticsearch.exceptions.InvalidConfigurationException;
4 |
5 | /**
6 | * Created by roiravhon on 9/21/16.
7 | */
8 | public interface BaseConfiguration {
9 |
10 | void validateConfig() throws InvalidConfigurationException;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/configuration/ConfigurationParser.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.configuration;
2 |
3 | import com.fasterxml.jackson.databind.ObjectMapper;
4 | import com.typesafe.config.Config;
5 | import com.typesafe.config.ConfigException;
6 | import com.typesafe.config.ConfigFactory;
7 | import com.typesafe.config.ConfigObject;
8 | import io.logz.benchmarks.elasticsearch.benchmark.BenchmarkPlan;
9 | import io.logz.benchmarks.elasticsearch.benchmark.BenchmarkStep;
10 | import io.logz.benchmarks.elasticsearch.controllers.IndexingController;
11 | import io.logz.benchmarks.elasticsearch.controllers.NoopController;
12 | import io.logz.benchmarks.elasticsearch.controllers.OptimizeController;
13 | import io.logz.benchmarks.elasticsearch.controllers.SearchController;
14 | import io.logz.benchmarks.elasticsearch.exceptions.InvalidConfigurationException;
15 | import org.slf4j.Logger;
16 | import org.slf4j.LoggerFactory;
17 |
18 | import java.io.File;
19 | import java.util.List;
20 |
21 | /**
22 | * Created by roiravhon on 9/19/16.
23 | */
24 | public class ConfigurationParser {
25 |
26 | private static final Logger logger = LoggerFactory.getLogger(ConfigurationParser.class);
27 |
28 | public static BenchmarkPlan parseConfiguration(String configurationFile) throws InvalidConfigurationException {
29 | try {
30 | Config config = ConfigFactory.parseFile(new File(configurationFile));
31 | ConfigObject elasticsearchConfig = config.getObject("elasticsearch");
32 | List extends ConfigObject> stepsConfig = config.getObjectList("steps");
33 |
34 | ObjectMapper objectMapper = new ObjectMapper();
35 |
36 | // Parse elasticsearch configuration
37 | ElasticsearchConfiguration esConfig = objectMapper.convertValue(elasticsearchConfig.unwrapped(), ElasticsearchConfiguration.class);
38 | esConfig.validateConfig();
39 |
40 | // Build initial benchmark plan
41 | BenchmarkPlan benchmarkPlan = new BenchmarkPlan(esConfig);
42 |
43 | for (ConfigObject currConfigObject: stepsConfig) {
44 |
45 | Config currConfig = currConfigObject.toConfig();
46 |
47 | long duration = currConfig.getDuration("duration").toMillis();
48 | BenchmarkStep step = new BenchmarkStep(duration);
49 |
50 | if (currConfig.hasPath("indexing")) {
51 | IndexingConfiguration indexingConfig = objectMapper.convertValue(currConfig.getObject("indexing").unwrapped(), IndexingConfiguration.class);
52 | indexingConfig.validateConfig();
53 | step.addController(new IndexingController(indexingConfig, benchmarkPlan.getEsController()));
54 | }
55 |
56 | if (currConfig.hasPath("search")) {
57 | SearchConfiguration searchConfig = objectMapper.convertValue(currConfig.getObject("search").unwrapped(), SearchConfiguration.class);
58 | searchConfig.validateConfig();
59 | step.addController(new SearchController(searchConfig, benchmarkPlan.getEsController()));
60 | }
61 |
62 | if (currConfig.hasPath("optimize")) {
63 | OptimizeConfiguration optimizeConfig = objectMapper.convertValue(currConfig.getObject("optimize").unwrapped(), OptimizeConfiguration.class);
64 | optimizeConfig.validateConfig();
65 | step.addController(new OptimizeController(optimizeConfig, benchmarkPlan.getEsController()));
66 | }
67 |
68 | if (currConfig.hasPath("noop")) {
69 | step.addController(new NoopController());
70 | }
71 |
72 | // Adding the final step
73 | benchmarkPlan.addStep(step);
74 | }
75 |
76 | return benchmarkPlan;
77 |
78 | } catch (ConfigException e) {
79 | throw new RuntimeException("Could not parse configuration file!", e);
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/configuration/ElasticsearchConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.configuration;
2 |
3 | import io.logz.benchmarks.elasticsearch.exceptions.InvalidConfigurationException;
4 |
5 | /**
6 | * Created by roiravhon on 9/21/16.
7 | */
8 | public class ElasticsearchConfiguration implements BaseConfiguration {
9 |
10 | private String elasticsearchAddress;
11 | private String elasticsearchProtocol = "http";
12 | private int elasticsearchPort = 9200;
13 | private int numberOfShards = 1;
14 | private int numberOfReplicas = 0;
15 | private String userName;
16 | private String password;
17 | private String indexPrefix;
18 | private String documentsPath;
19 | private String searchesPath;
20 |
21 | // For Jackson
22 | @SuppressWarnings("unused")
23 | public ElasticsearchConfiguration() {
24 |
25 | }
26 |
27 | @SuppressWarnings("unused")
28 | public ElasticsearchConfiguration(String elasticsearchAddress, String elasticsearchProtocol, Integer elasticsearchPort, Integer numberOfShards, Integer numberOfReplicas, String userName, String password, String indexPrefix) {
29 | this.elasticsearchAddress = elasticsearchAddress;
30 | this.elasticsearchProtocol = elasticsearchProtocol;
31 | this.elasticsearchPort = elasticsearchPort;
32 | this.numberOfShards = numberOfShards;
33 | this.numberOfReplicas = numberOfReplicas;
34 | this.userName = userName;
35 | this.password = password;
36 | this.indexPrefix = indexPrefix;
37 | }
38 |
39 | public String getElasticsearchAddress() {
40 | return elasticsearchAddress;
41 | }
42 |
43 | public String getElasticsearchProtocol() {
44 | return elasticsearchProtocol;
45 | }
46 |
47 | public int getElasticsearchPort() {
48 | return elasticsearchPort;
49 | }
50 |
51 | public int getNumberOfShards() {
52 | return numberOfShards;
53 | }
54 |
55 | public int getNumberOfReplicas() {
56 | return numberOfReplicas;
57 | }
58 |
59 | public String getUserName() {
60 | return userName;
61 | }
62 |
63 | public String getPassword() {
64 | return password;
65 | }
66 |
67 | public String getIndexPrefix() {
68 | return indexPrefix;
69 | }
70 |
71 | public String getDocumentsPath() {
72 | return documentsPath;
73 | }
74 |
75 | public String getSearchesPath() {
76 | return searchesPath;
77 | }
78 |
79 | @Override
80 | public void validateConfig() throws InvalidConfigurationException {
81 |
82 | if (elasticsearchAddress == null || elasticsearchAddress == "") {
83 | throw new InvalidConfigurationException("elasticsearchAddress must be set!");
84 | }
85 |
86 | if (!elasticsearchProtocol.equals("http")) {
87 | throw new InvalidConfigurationException("Only supporting http protocol for Elasticsearch for now");
88 | }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/configuration/IndexingConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.configuration;
2 |
3 | import io.logz.benchmarks.elasticsearch.exceptions.InvalidConfigurationException;
4 |
5 | /**
6 | * Created by roiravhon on 9/21/16.
7 | */
8 | public class IndexingConfiguration implements BaseConfiguration{
9 |
10 | private static final int MAX_NUMBER_OF_THREADS = 10000;
11 | private static final int MIN_NUMBER_OF_THREADS = 1;
12 | private int numberOfThreads = 5;
13 | private int bulkSize = 1000;
14 |
15 | // For Jackson
16 | @SuppressWarnings("unused")
17 | public IndexingConfiguration() {
18 |
19 | }
20 |
21 | @SuppressWarnings("unused")
22 | public IndexingConfiguration(int numberOfThreads, int bulkSize) {
23 | this.numberOfThreads = numberOfThreads;
24 | this.bulkSize = bulkSize;
25 | }
26 |
27 | public int getNumberOfThreads() {
28 | return numberOfThreads;
29 | }
30 |
31 | public int getBulkSize() {
32 | return bulkSize;
33 | }
34 |
35 | @Override
36 | public void validateConfig() throws InvalidConfigurationException {
37 |
38 | if (numberOfThreads < MIN_NUMBER_OF_THREADS || numberOfThreads >= MAX_NUMBER_OF_THREADS) {
39 | throw new InvalidConfigurationException("Indexing numberOfThreads must be between " + MIN_NUMBER_OF_THREADS + " and " + MAX_NUMBER_OF_THREADS);
40 | }
41 |
42 | if (bulkSize <= 0) {
43 | throw new InvalidConfigurationException("Bulk size must be more than 0! can't work with " + bulkSize);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/configuration/OptimizeConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.configuration;
2 |
3 | import io.logz.benchmarks.elasticsearch.exceptions.InvalidConfigurationException;
4 |
5 | /**
6 | * Created by roiravhon on 9/21/16.
7 | */
8 | public class OptimizeConfiguration implements BaseConfiguration {
9 |
10 | private static final int MIN_NUMBER_OF_SEGMENTS = 1;
11 | private static final int MAX_NUMBER_OF_SEGMENTS = 10;
12 | private int numberOfSegments = 1;
13 |
14 | // For Jackson
15 | @SuppressWarnings("unused")
16 | public OptimizeConfiguration() {
17 |
18 | }
19 |
20 | @SuppressWarnings("unused")
21 | public OptimizeConfiguration(int maxNumberOfSegments) {
22 | this.numberOfSegments = maxNumberOfSegments;
23 | }
24 |
25 | public int getNumberOfSegments() {
26 | return numberOfSegments;
27 | }
28 |
29 | @Override
30 | public void validateConfig() throws InvalidConfigurationException {
31 | if (numberOfSegments < MIN_NUMBER_OF_SEGMENTS || numberOfSegments >= MAX_NUMBER_OF_SEGMENTS) {
32 | throw new InvalidConfigurationException("Optimize numberOfSegments must be between " + MIN_NUMBER_OF_SEGMENTS + " and " + MAX_NUMBER_OF_SEGMENTS);
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/configuration/SearchConfiguration.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.configuration;
2 |
3 | import io.logz.benchmarks.elasticsearch.exceptions.InvalidConfigurationException;
4 |
5 | /**
6 | * Created by roiravhon on 9/21/16.
7 | */
8 | public class SearchConfiguration implements BaseConfiguration {
9 |
10 | private static final int MIN_NUMBER_OF_THREADS = 1;
11 | private static final int MAX_NUMBER_OF_THREADS = 10000;
12 | private int numberOfThreads = 5;
13 |
14 | // For Jackson
15 | @SuppressWarnings("unused")
16 | public SearchConfiguration() {
17 |
18 | }
19 |
20 | @SuppressWarnings("unused")
21 | public SearchConfiguration(int numberOfThreads) {
22 | this.numberOfThreads = numberOfThreads;
23 | }
24 |
25 | public int getNumberOfThreads() {
26 | return numberOfThreads;
27 | }
28 |
29 | @Override
30 | public void validateConfig() throws InvalidConfigurationException {
31 |
32 | if (numberOfThreads < MIN_NUMBER_OF_THREADS || numberOfThreads >= MAX_NUMBER_OF_THREADS) {
33 | throw new InvalidConfigurationException("Search numberOfThreads must be between " + MIN_NUMBER_OF_THREADS + " and " + MAX_NUMBER_OF_THREADS);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/controllers/BaseController.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.controllers;
2 |
3 | /**
4 | * Created by roiravhon on 9/19/16.
5 | */
6 | public interface BaseController {
7 |
8 | String getControllerName();
9 | void run();
10 | void stop();
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/controllers/IndexingController.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.controllers;
2 |
3 | import com.google.common.util.concurrent.ThreadFactoryBuilder;
4 | import io.logz.benchmarks.elasticsearch.configuration.ConfigurationParser;
5 | import io.logz.benchmarks.elasticsearch.configuration.ElasticsearchConfiguration;
6 | import io.logz.benchmarks.elasticsearch.configuration.IndexingConfiguration;
7 | import io.logz.benchmarks.elasticsearch.elasticsearch.ElasticsearchController;
8 | import io.logz.benchmarks.elasticsearch.exceptions.CouldNotCompleteBulkOperationException;
9 | import io.logz.benchmarks.elasticsearch.metrics.IndexingMbean;
10 | import io.searchbox.core.Bulk;
11 | import io.searchbox.core.Index;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 |
15 | import java.util.Arrays;
16 | import java.util.concurrent.ExecutorService;
17 | import java.util.concurrent.Executors;
18 | import java.util.concurrent.ThreadFactory;
19 | import java.util.stream.Collectors;
20 | import java.util.stream.IntStream;
21 |
22 | /**
23 | * Created by roiravhon on 9/19/16.
24 | */
25 | public class IndexingController implements BaseController {
26 |
27 | private static final Logger logger = LoggerFactory.getLogger(IndexingController.class);
28 |
29 | private final IndexingMbean indexingMbean;
30 | private final IndexingConfiguration configuration;
31 | private final ElasticsearchController esController;
32 | private final ExecutorService threadPool;
33 |
34 | public IndexingController(IndexingConfiguration configuration, ElasticsearchController esController) {
35 |
36 | this.configuration = configuration;
37 | this.esController = esController;
38 | indexingMbean = IndexingMbean.getInstance();
39 |
40 | // Creating the thread pool
41 | ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("indexing-thread-%d").build();
42 | threadPool = Executors.newFixedThreadPool(configuration.getNumberOfThreads(), namedThreadFactory);
43 | }
44 |
45 | @Override
46 | public String getControllerName() {
47 | return "Indexing";
48 | }
49 |
50 | @Override
51 | public void run() {
52 |
53 | // Creating indexing threads
54 | IntStream.range(0, configuration.getNumberOfThreads())
55 | .forEach((threadNumber) -> threadPool.submit(() -> startIndexing(threadNumber)));
56 | }
57 |
58 | @Override
59 | public void stop() {
60 |
61 | threadPool.shutdownNow();
62 | }
63 |
64 | private void startIndexing(int threadNumber) {
65 |
66 | logger.debug("Starting indexing thread #{}", threadNumber);
67 | while (true) {
68 | try {
69 |
70 | if (Thread.interrupted()) {
71 | logger.debug("Got interrupt, stopping indexing thread #{}", threadNumber);
72 | return;
73 | }
74 |
75 | // Create bulk
76 | Bulk currBulk = new Bulk.Builder()
77 | .defaultIndex(esController.getIndexName())
78 | .defaultType(esController.getDefaultType())
79 | .addAction(esController.getMultipleDocuments(configuration.getBulkSize())
80 | .stream()
81 | .map((doc) -> new Index.Builder(doc).build())
82 | .collect(Collectors.toList()))
83 | .build();
84 |
85 | try {
86 | // Lets index the bulk!
87 | int numberOfFailedDocuments = esController.executeBulk(currBulk);
88 |
89 | // Update metrics
90 | indexingMbean.incrementSuccessfulDocuments(configuration.getBulkSize() - numberOfFailedDocuments);
91 | indexingMbean.incrementFailedDocuements(numberOfFailedDocuments);
92 |
93 | } catch (CouldNotCompleteBulkOperationException e) {
94 |
95 | // Assuming all documents failed..
96 | indexingMbean.incrementFailedDocuements(configuration.getBulkSize());
97 | }
98 |
99 | } catch (Throwable throwable) {
100 | logger.debug("Got unexpected exception while indexing!", throwable);
101 | }
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/controllers/NoopController.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.controllers;
2 |
3 | /**
4 | * Created by roiravhon on 9/19/16.
5 | */
6 | public class NoopController implements BaseController{
7 |
8 | @Override
9 | public String getControllerName() {
10 | return "Noop";
11 | }
12 |
13 | @Override
14 | public void run() {
15 |
16 | }
17 |
18 | @Override
19 | public void stop() {
20 |
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/controllers/OptimizeController.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.controllers;
2 |
3 | import io.logz.benchmarks.elasticsearch.configuration.OptimizeConfiguration;
4 | import io.logz.benchmarks.elasticsearch.elasticsearch.ElasticsearchController;
5 | import io.logz.benchmarks.elasticsearch.exceptions.CouldNotOptimizeException;
6 | import io.searchbox.indices.ForceMerge;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 |
10 | /**
11 | * Created by roiravhon on 9/19/16.
12 | */
13 | public class OptimizeController implements BaseController {
14 |
15 | private static final Logger logger = LoggerFactory.getLogger(OptimizeController.class);
16 |
17 | private final OptimizeConfiguration configuration;
18 | private final ElasticsearchController esController;
19 |
20 | public OptimizeController(OptimizeConfiguration configuration, ElasticsearchController esController) {
21 |
22 | this.configuration = configuration;
23 | this.esController = esController;
24 | }
25 |
26 | @Override
27 | public String getControllerName() {
28 | return "Optimize";
29 | }
30 |
31 | @Override
32 | public void run() {
33 | logger.debug("Starting to optimize! (force merge)");
34 | ForceMerge forceMerge = new ForceMerge.Builder()
35 | .maxNumSegments(configuration.getNumberOfSegments())
36 | .build();
37 | try {
38 | esController.executeForceMerge(forceMerge);
39 | } catch (CouldNotOptimizeException e) {
40 | logger.info("Could not optimize!", e);
41 | }
42 | }
43 |
44 | @Override
45 | public void stop() {
46 | // We cant cancel optimize..
47 | }
48 | }
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/controllers/SearchController.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.controllers;
2 |
3 | import com.google.common.base.Stopwatch;
4 | import com.google.common.util.concurrent.ThreadFactoryBuilder;
5 | import io.logz.benchmarks.elasticsearch.configuration.SearchConfiguration;
6 | import io.logz.benchmarks.elasticsearch.elasticsearch.ElasticsearchController;
7 | import io.logz.benchmarks.elasticsearch.exceptions.CouldNotExecuteSearchException;
8 | import io.logz.benchmarks.elasticsearch.metrics.SearchMbean;
9 | import io.searchbox.core.Search;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | import java.util.concurrent.ExecutorService;
14 | import java.util.concurrent.Executors;
15 | import java.util.concurrent.ThreadFactory;
16 | import java.util.concurrent.TimeUnit;
17 | import java.util.stream.IntStream;
18 |
19 | /**
20 | * Created by roiravhon on 9/19/16.
21 | */
22 | public class SearchController implements BaseController {
23 |
24 | private static final Logger logger = LoggerFactory.getLogger(SearchController.class);
25 |
26 | private final SearchConfiguration configuration;
27 | private final ElasticsearchController esController;
28 | private final SearchMbean searchMbean;
29 | private final ExecutorService threadPool;
30 |
31 | public SearchController(SearchConfiguration configuration, ElasticsearchController esController) {
32 |
33 | this.configuration = configuration;
34 | this.esController = esController;
35 |
36 | searchMbean = SearchMbean.getInstance();
37 |
38 | // Creating the executor service
39 | ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNameFormat("search-thread-%d").build();
40 | threadPool = Executors.newFixedThreadPool(configuration.getNumberOfThreads(), namedThreadFactory);
41 | }
42 |
43 | @Override
44 | public String getControllerName() {
45 | return "Search";
46 | }
47 |
48 | @Override
49 | public void run() {
50 |
51 | IntStream.range(0, configuration.getNumberOfThreads())
52 | .forEach((threadNumber) -> threadPool.submit(() -> startSearching(threadNumber)));
53 | }
54 |
55 | @Override
56 | public void stop() {
57 |
58 | threadPool.shutdownNow();
59 | }
60 |
61 | @SuppressWarnings("WeakerAccess")
62 | public void startSearching(int threadNumber) {
63 |
64 | logger.debug("Starting searching thread #{}", threadNumber);
65 | while (true) {
66 | try {
67 |
68 | if (Thread.interrupted())
69 | return;
70 |
71 | Stopwatch stopwatch = Stopwatch.createUnstarted();
72 |
73 | try {
74 | String currSearch = esController.getSearch();
75 |
76 | Search search = new Search.Builder(currSearch)
77 | .addIndex(esController.getSearchIndex())
78 | .addType(esController.getDefaultType())
79 | .build();
80 |
81 | stopwatch.start();
82 | int docCount = esController.executeSearch(search);
83 | stopwatch.stop();
84 |
85 | searchMbean.incrementSuccessfulSearches();
86 | searchMbean.incrementNumberOfFetchedDocuments(docCount);
87 | searchMbean.incrementTotalSearchTimeMs(stopwatch.elapsed(TimeUnit.MILLISECONDS));
88 |
89 | } catch (CouldNotExecuteSearchException e) {
90 | stopwatch.stop();
91 | logger.debug("Could not execute search", e);
92 | searchMbean.incrementNumberOfFailedSearches();
93 | }
94 |
95 | } catch (Throwable throwable){
96 | logger.debug("Got unexpected exception while searching!", throwable);
97 | }
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/elasticsearch/ElasticsearchController.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.elasticsearch;
2 |
3 | import com.google.common.io.Files;
4 | import com.google.common.io.Resources;
5 | import io.logz.benchmarks.elasticsearch.configuration.ElasticsearchConfiguration;
6 | import io.logz.benchmarks.elasticsearch.exceptions.CouldNotCompleteBulkOperationException;
7 | import io.logz.benchmarks.elasticsearch.exceptions.CouldNotExecuteSearchException;
8 | import io.logz.benchmarks.elasticsearch.exceptions.CouldNotOptimizeException;
9 | import io.searchbox.client.JestClient;
10 | import io.searchbox.client.JestClientFactory;
11 | import io.searchbox.client.JestResult;
12 | import io.searchbox.client.config.HttpClientConfig;
13 | import io.searchbox.core.Bulk;
14 | import io.searchbox.core.BulkResult;
15 | import io.searchbox.core.Search;
16 | import io.searchbox.core.SearchResult;
17 | import io.searchbox.indices.CreateIndex;
18 | import io.searchbox.indices.DeleteIndex;
19 | import io.searchbox.indices.ForceMerge;
20 | import io.searchbox.indices.mapping.PutMapping;
21 | import org.apache.commons.lang3.StringUtils;
22 | import org.elasticsearch.common.settings.Settings;
23 | import org.reflections.Reflections;
24 | import org.reflections.scanners.ResourcesScanner;
25 | import org.reflections.util.ClasspathHelper;
26 | import org.reflections.util.ConfigurationBuilder;
27 | import org.reflections.util.FilterBuilder;
28 | import org.slf4j.Logger;
29 | import org.slf4j.LoggerFactory;
30 |
31 | import java.io.File;
32 | import java.io.IOException;
33 | import java.net.URL;
34 | import java.nio.charset.Charset;
35 | import java.nio.charset.StandardCharsets;
36 | import java.util.ArrayList;
37 | import java.util.Optional;
38 | import java.util.Set;
39 | import java.util.UUID;
40 | import java.util.concurrent.ThreadLocalRandom;
41 | import java.util.concurrent.atomic.AtomicInteger;
42 | import java.util.regex.Pattern;
43 | import java.util.stream.IntStream;
44 |
45 | /**
46 | * Created by roiravhon on 9/19/16.
47 | */
48 | public class ElasticsearchController {
49 |
50 | private static final Logger logger = LoggerFactory.getLogger(ElasticsearchController.class);
51 | private static final int INDEX_LENGTH = 8;
52 | private static final int FIELD_CARDINALITY = 100;
53 | private static final int MAX_CHARS_IN_FIELD = 10;
54 | private static final int MIN_CHARS_IN_FIELD = 5;
55 | private static final int JEST_READ_TIMEOUT = 2 * 60 * 1000;
56 | private static final String TEMPLATE_DOCUMENTS_RESOURCE_PATTERN = ".*templates/documents.*";
57 | private static final String TEMPLATE_SEARCHES_RESOURCE_PATTERN = ".*templates/searches.*";
58 | private static final String TIMESTAMP_PLACEHOLDER = "TIMESTAMP";
59 | private static final String RANDOM_STR_PLACEHOLDER = "RANDSTR";
60 | private static final String RANDOM_INT_PLACEHOLDER = "RANDINT";
61 | private static final String DEFAULT_TYPE = "benchmark";
62 |
63 | private final ElasticsearchConfiguration esConfig;
64 | private final ArrayList randomFieldsList;
65 | private final ArrayList rawDocumentsList;
66 | private final ArrayList searchesList;
67 | private final JestClient client;
68 | private final String indexName;
69 | private final Optional indexPrefix;
70 | private final AtomicInteger lastSelectedDocument;
71 | private final AtomicInteger lastSelectedSearch;
72 |
73 | private boolean indexCreated = false;
74 |
75 | public ElasticsearchController(ElasticsearchConfiguration esConfig) {
76 | this.esConfig = esConfig;
77 | lastSelectedDocument = new AtomicInteger(0);
78 | lastSelectedSearch = new AtomicInteger(0);
79 |
80 | indexPrefix = Optional.ofNullable(esConfig.getIndexPrefix());
81 |
82 | indexName = indexPrefix.orElse("") + getRandomString(INDEX_LENGTH);
83 |
84 | logger.info("Random test index name set to: {}", indexName);
85 |
86 | client = initializeJestClient(esConfig);
87 | randomFieldsList = generateRandomFieldList();
88 | rawDocumentsList = loadDocuments(esConfig.getDocumentsPath(), TEMPLATE_DOCUMENTS_RESOURCE_PATTERN);
89 | searchesList = loadDocuments(esConfig.getSearchesPath(), TEMPLATE_SEARCHES_RESOURCE_PATTERN);
90 | }
91 |
92 | private ArrayList loadDocuments(String path, String resource) {
93 | if (path != null) {
94 | try {
95 | return getExternalDirectoryContent(path);
96 | } catch (RuntimeException e) {
97 | logger.error("failed to load documents from path " + path + ". Fallback, going to load default documents");
98 | }
99 | }
100 |
101 | return getResourceDirectoryContent(resource);
102 | }
103 |
104 | public void createIndex() {
105 | Settings.Builder settingsBuilder = Settings.settingsBuilder();
106 | settingsBuilder.put("number_of_shards", esConfig.getNumberOfShards());
107 | settingsBuilder.put("number_of_replicas", esConfig.getNumberOfReplicas());
108 |
109 | // No nice way to build this with elasticsearch library
110 | String mappings = "{ \"" + DEFAULT_TYPE + "\" : { \"properties\" : { \"@timestamp\" : {\"type\" : \"date\", \"format\" : \"epoch_millis\"}}}}";
111 |
112 | try {
113 | logger.info("Creating test index on ES, named {} with {} shards and {} replicas", indexName, esConfig.getNumberOfShards(), esConfig.getNumberOfReplicas());
114 | client.execute(new CreateIndex.Builder(indexName).settings(settingsBuilder.build().getAsMap()).build());
115 | client.execute(new PutMapping.Builder(indexName, DEFAULT_TYPE, mappings).build());
116 |
117 | indexCreated = true;
118 |
119 | } catch (IOException e) {
120 | throw new RuntimeException("Could not create index in elasticsearch!", e);
121 | }
122 | }
123 |
124 | public void deleteIndex() {
125 | try {
126 | if (indexCreated) {
127 | logger.info("Deleting test index {} from ES", indexName);
128 | client.execute(new DeleteIndex.Builder(indexName).build());
129 | }
130 |
131 | } catch (IOException e) {
132 | throw new RuntimeException("Could not delete index from ES!", e);
133 | }
134 | }
135 |
136 | public ArrayList getMultipleDocuments(int numberOfDocument) {
137 |
138 | ArrayList tempList = new ArrayList<>(numberOfDocument + 1);
139 | IntStream.range(0, numberOfDocument).forEach((i) -> tempList.add(getDocument()));
140 | return tempList;
141 | }
142 |
143 | public String getDefaultType() {
144 | return DEFAULT_TYPE;
145 | }
146 |
147 | public String getIndexName() {
148 | return indexName;
149 | }
150 |
151 | public Optional getIndexPrefix() {
152 | return indexPrefix;
153 | }
154 |
155 | public String getSearchIndex() {
156 | return getIndexPrefix().isPresent() ? getIndexPrefix().get() + "*" : getIndexName();
157 | }
158 |
159 | // Return the number of failed documents
160 | public int executeBulk(Bulk bulk) throws CouldNotCompleteBulkOperationException {
161 | try {
162 |
163 | BulkResult result = client.execute(bulk);
164 | return result.getFailedItems().size();
165 |
166 | } catch (IOException e) {
167 | throw new CouldNotCompleteBulkOperationException(e);
168 | }
169 | }
170 |
171 | // Returns the number of documents found
172 | public int executeSearch(Search search) throws CouldNotExecuteSearchException {
173 |
174 | try {
175 | SearchResult result = client.execute(search);
176 |
177 | if (result.isSucceeded()) {
178 | return result.getTotal();
179 | } else {
180 | logger.debug(result.getErrorMessage());
181 | throw new CouldNotExecuteSearchException();
182 | }
183 | } catch (IOException e) {
184 | throw new CouldNotExecuteSearchException(e);
185 | }
186 | }
187 |
188 | public void executeForceMerge(ForceMerge forceMerge) throws CouldNotOptimizeException {
189 | try {
190 | JestResult result = client.execute(forceMerge);
191 | if (!result.isSucceeded())
192 | throw new CouldNotOptimizeException(result.getErrorMessage());
193 |
194 | } catch (IOException e) {
195 | throw new CouldNotOptimizeException(e);
196 | }
197 | }
198 |
199 | public String getSearch() {
200 | String currSearch = searchesList.get(lastSelectedSearch.get() % searchesList.size());
201 | lastSelectedSearch.incrementAndGet();
202 | return currSearch;
203 | }
204 |
205 | private String getDocument() {
206 |
207 | String currDocument = rawDocumentsList.get(lastSelectedDocument.get() % rawDocumentsList.size());
208 | currDocument = currDocument.replace(TIMESTAMP_PLACEHOLDER, String.valueOf(System.currentTimeMillis()));
209 |
210 | // Replacing all marks with values
211 | while (true) {
212 |
213 | currDocument = currDocument.replaceFirst(RANDOM_STR_PLACEHOLDER, getRandomField());
214 | currDocument = currDocument.replaceFirst(RANDOM_INT_PLACEHOLDER, String.valueOf(getRandomFieldInt()));
215 |
216 | // Breaking out if we are done replacing
217 | if (!currDocument.contains(RANDOM_STR_PLACEHOLDER) && !currDocument.contains(RANDOM_INT_PLACEHOLDER))
218 | break;
219 | }
220 |
221 | lastSelectedDocument.incrementAndGet();
222 | return currDocument;
223 | }
224 |
225 | private String getRandomField() {
226 | int index = ThreadLocalRandom.current().nextInt(0, FIELD_CARDINALITY);
227 | return randomFieldsList.get(index);
228 | }
229 |
230 | private int getRandomFieldInt() {
231 | return ThreadLocalRandom.current().nextInt(0, 10000 + 1);
232 | }
233 |
234 | private String getRandomString(int length) {
235 | try {
236 | return UUID.randomUUID().toString().substring(0, length);
237 | } catch (IndexOutOfBoundsException e) {
238 | throw new RuntimeException("Cannot create random string of size " + length + "! this is probably due to internal parameters changes you made.", e);
239 | }
240 | }
241 |
242 | private String getRandomStringInRandomSize(int minSize, int maxSize) {
243 | int length = ThreadLocalRandom.current().nextInt(minSize, maxSize + 1);
244 | return getRandomString(length);
245 | }
246 |
247 | private JestClient initializeJestClient(ElasticsearchConfiguration esConfig) {
248 | JestClientFactory factory = new JestClientFactory();
249 | HttpClientConfig.Builder httpClientBuilder = new HttpClientConfig
250 | .Builder(esConfig.getElasticsearchProtocol() + "://" + esConfig.getElasticsearchAddress() + ":" + esConfig.getElasticsearchPort())
251 | .multiThreaded(true)
252 | .readTimeout(JEST_READ_TIMEOUT);
253 |
254 | if (StringUtils.isNotEmpty(esConfig.getUserName()) && StringUtils.isNotEmpty(esConfig.getPassword())) {
255 | httpClientBuilder.defaultCredentials(esConfig.getUserName(), esConfig.getPassword());
256 | }
257 |
258 | factory.setHttpClientConfig(httpClientBuilder.build());
259 |
260 | logger.info("Creating Jest client to handle all ES operations");
261 | return factory.getObject();
262 | }
263 |
264 | private ArrayList generateRandomFieldList() {
265 | ArrayList tempRandomFieldList = new ArrayList<>(FIELD_CARDINALITY);
266 |
267 | IntStream.range(0, FIELD_CARDINALITY).forEach((i) ->
268 | tempRandomFieldList.add(getRandomStringInRandomSize(MIN_CHARS_IN_FIELD, MAX_CHARS_IN_FIELD)));
269 |
270 | return tempRandomFieldList;
271 | }
272 |
273 | private ArrayList getResourceDirectoryContent(String resourcePattern) {
274 | ArrayList tempFilesContentList = new ArrayList<>();
275 |
276 | Reflections reflections = new Reflections(new ConfigurationBuilder()
277 | .setUrls(ClasspathHelper.forPackage("io.logz"))
278 | .setScanners(new ResourcesScanner())
279 | .filterInputsBy(new FilterBuilder().include(resourcePattern)));
280 | Set properties = reflections.getResources(Pattern.compile(".*\\.json"));
281 |
282 | properties.forEach((resourceName) -> {
283 |
284 | URL resourceUrl = Resources.getResource(resourceName);
285 | try {
286 | tempFilesContentList.add(Resources.toString(resourceUrl, Charset.forName("utf-8")).replace("\n", ""));
287 |
288 | } catch (IOException e) {
289 | logger.info("Could not read file {}", resourceUrl.toString());
290 | }
291 | });
292 |
293 | if (tempFilesContentList.isEmpty())
294 | throw new RuntimeException("Did not find any files under "+ resourcePattern +"!");
295 |
296 | return tempFilesContentList;
297 | }
298 |
299 | private ArrayList getExternalDirectoryContent(String path) {
300 | ArrayList tempFilesContentList = new ArrayList<>();
301 |
302 | File directory = new File(path);
303 | if (!directory.isDirectory()) {
304 | throw new RuntimeException(path +" is not a directory!");
305 | }
306 |
307 | File[] documents = directory.listFiles();
308 |
309 | if (documents == null || documents.length == 0) {
310 | throw new RuntimeException("Did not find any files under "+ path +"!");
311 | }
312 |
313 | for (File doc : documents) {
314 | try {
315 | tempFilesContentList.add(String.join("", Files.readLines(doc, StandardCharsets.UTF_8)));
316 | } catch (IOException e) {
317 | logger.info("Could not read file {}", doc.getAbsolutePath());
318 | }
319 | }
320 |
321 | if (tempFilesContentList.isEmpty()) {
322 | throw new RuntimeException("Failed to load all files under " + path);
323 | }
324 |
325 | return tempFilesContentList;
326 | }
327 | }
328 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/exceptions/CouldNotCompleteBulkOperationException.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.exceptions;
2 |
3 | /**
4 | * Created by roiravhon on 9/26/16.
5 | */
6 | public class CouldNotCompleteBulkOperationException extends Exception {
7 |
8 | public CouldNotCompleteBulkOperationException() {
9 | }
10 |
11 | public CouldNotCompleteBulkOperationException(String message) {
12 | super(message);
13 | }
14 |
15 | public CouldNotCompleteBulkOperationException(String message, Throwable cause) {
16 | super(message, cause);
17 | }
18 |
19 | public CouldNotCompleteBulkOperationException(Throwable cause) {
20 | super(cause);
21 | }
22 |
23 | public CouldNotCompleteBulkOperationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
24 | super(message, cause, enableSuppression, writableStackTrace);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/exceptions/CouldNotExecuteSearchException.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.exceptions;
2 |
3 | /**
4 | * Created by roiravhon on 9/27/16.
5 | */
6 | public class CouldNotExecuteSearchException extends Exception {
7 | public CouldNotExecuteSearchException() {
8 | }
9 |
10 | public CouldNotExecuteSearchException(String message) {
11 | super(message);
12 | }
13 |
14 | public CouldNotExecuteSearchException(String message, Throwable cause) {
15 | super(message, cause);
16 | }
17 |
18 | public CouldNotExecuteSearchException(Throwable cause) {
19 | super(cause);
20 | }
21 |
22 | public CouldNotExecuteSearchException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
23 | super(message, cause, enableSuppression, writableStackTrace);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/exceptions/CouldNotOptimizeException.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.exceptions;
2 |
3 | /**
4 | * Created by roiravhon on 9/27/16.
5 | */
6 | public class CouldNotOptimizeException extends Exception {
7 | public CouldNotOptimizeException() {
8 | }
9 |
10 | public CouldNotOptimizeException(String message) {
11 | super(message);
12 | }
13 |
14 | public CouldNotOptimizeException(String message, Throwable cause) {
15 | super(message, cause);
16 | }
17 |
18 | public CouldNotOptimizeException(Throwable cause) {
19 | super(cause);
20 | }
21 |
22 | public CouldNotOptimizeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
23 | super(message, cause, enableSuppression, writableStackTrace);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/exceptions/InvalidConfigurationException.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.exceptions;
2 |
3 | /**
4 | * Created by roiravhon on 9/21/16.
5 | */
6 | public class InvalidConfigurationException extends Exception {
7 |
8 | public InvalidConfigurationException() {
9 | }
10 |
11 | public InvalidConfigurationException(String message) {
12 | super(message);
13 | }
14 |
15 | public InvalidConfigurationException(String message, Throwable cause) {
16 | super(message, cause);
17 | }
18 |
19 | public InvalidConfigurationException(Throwable cause) {
20 | super(cause);
21 | }
22 |
23 | public InvalidConfigurationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
24 | super(message, cause, enableSuppression, writableStackTrace);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/metrics/GeneralMbean.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.metrics;
2 |
3 | import com.udojava.jmx.wrapper.JMXBean;
4 | import com.udojava.jmx.wrapper.JMXBeanAttribute;
5 |
6 | import java.util.concurrent.atomic.AtomicInteger;
7 |
8 | /**
9 | * Created by roiravhon on 9/20/16.
10 | */
11 | @JMXBean(description = "General JMX metrics")
12 | public class GeneralMbean {
13 |
14 | private static GeneralMbean instance;
15 | private final AtomicInteger currStep;
16 |
17 | public static GeneralMbean getInstance() {
18 | if (instance == null) {
19 | instance = new GeneralMbean();
20 | }
21 |
22 | return instance;
23 | }
24 |
25 | private GeneralMbean() {
26 | currStep = new AtomicInteger(1);
27 | }
28 |
29 | @SuppressWarnings("unused")
30 | @JMXBeanAttribute(name = "currStep", description = "The current step running")
31 | public long getCurrStep() {
32 | return currStep.get();
33 | }
34 |
35 | public void incrementStep() {
36 | currStep.incrementAndGet();
37 | }
38 | }
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/metrics/IndexingMbean.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.metrics;
2 |
3 | import com.udojava.jmx.wrapper.JMXBean;
4 | import com.udojava.jmx.wrapper.JMXBeanAttribute;
5 |
6 | import java.util.concurrent.atomic.AtomicLong;
7 |
8 | /**
9 | * Created by roiravhon on 9/20/16.
10 | */
11 | @JMXBean(description = "Indexing JMX metrics")
12 | public class IndexingMbean {
13 |
14 | private static IndexingMbean instance;
15 | private final AtomicLong numberOfSuccessfulDocumentsIndexed;
16 | private final AtomicLong numberOfFailedDocumentsIndexed;
17 |
18 | public static IndexingMbean getInstance() {
19 | if (instance == null) {
20 | instance = new IndexingMbean();
21 | }
22 |
23 | return instance;
24 | }
25 |
26 | private IndexingMbean() {
27 | numberOfSuccessfulDocumentsIndexed = new AtomicLong();
28 | numberOfFailedDocumentsIndexed = new AtomicLong();
29 | }
30 |
31 | @SuppressWarnings("unused")
32 | @JMXBeanAttribute(name = "numberOfSuccessfulDocumentsIndexed", description = "The accumulated number of successful documents indexed")
33 | public long getNumberOfSuccessfulDocumentsIndexed() {
34 | return numberOfSuccessfulDocumentsIndexed.get();
35 | }
36 |
37 | @SuppressWarnings("unused")
38 | @JMXBeanAttribute(name = "numberOfFailedDocumentsIndexed", description = "The accumulated number of failed documents indexed")
39 | public long getNumberOfFailedDocumentsIndexed() {
40 | return numberOfFailedDocumentsIndexed.get();
41 | }
42 |
43 | public void incrementSuccessfulDocuments(long docCount) {
44 | numberOfSuccessfulDocumentsIndexed.addAndGet(docCount);
45 | }
46 |
47 | public void incrementFailedDocuements(long docCount) {
48 | numberOfFailedDocumentsIndexed.addAndGet(docCount);
49 | }
50 | }
--------------------------------------------------------------------------------
/src/main/java/io/logz/benchmarks/elasticsearch/metrics/SearchMbean.java:
--------------------------------------------------------------------------------
1 | package io.logz.benchmarks.elasticsearch.metrics;
2 |
3 | import com.udojava.jmx.wrapper.JMXBean;
4 | import com.udojava.jmx.wrapper.JMXBeanAttribute;
5 |
6 | import java.util.concurrent.atomic.AtomicInteger;
7 | import java.util.concurrent.atomic.AtomicLong;
8 |
9 | /**
10 | * Created by roiravhon on 9/20/16.
11 | */
12 | @JMXBean(description = "Search JMX metrics")
13 | public class SearchMbean {
14 |
15 | private static SearchMbean instance;
16 | private final AtomicLong numberOfSuccessfulSearches;
17 | private final AtomicLong totalSuccessfulSearchesTimeMs;
18 | private final AtomicLong numberOfFailedSearches;
19 | private final AtomicLong numberOfFetchedDocuments;
20 |
21 | public static SearchMbean getInstance() {
22 | if (instance == null) {
23 | instance = new SearchMbean();
24 | }
25 |
26 | return instance;
27 | }
28 |
29 | private SearchMbean() {
30 | numberOfSuccessfulSearches = new AtomicLong();
31 | totalSuccessfulSearchesTimeMs = new AtomicLong();
32 | numberOfFailedSearches = new AtomicLong();
33 | numberOfFetchedDocuments = new AtomicLong();
34 | }
35 |
36 | @SuppressWarnings("unused")
37 | @JMXBeanAttribute(name = "numberOfSuccessfulSearches", description = "The accumulated number of successful documents searched")
38 | public long getNumberOfSuccessfulSearches() {
39 | return numberOfSuccessfulSearches.get();
40 | }
41 |
42 | @SuppressWarnings("unused")
43 | @JMXBeanAttribute(name = "totalSuccessfulSearchesTimeMs", description = "The total time all successful queries took, in MS")
44 | public long getTotalSuccessfulSearchesTimeMs() {
45 | return totalSuccessfulSearchesTimeMs.get();
46 | }
47 |
48 | @SuppressWarnings("unused")
49 | @JMXBeanAttribute(name = "averageSearchTimeMs", description = "The average time each query tool, in MS")
50 | public long getAverageSearchTimeMs() {
51 | if (getNumberOfSuccessfulSearches() > 0) {
52 | return getTotalSuccessfulSearchesTimeMs() / getNumberOfSuccessfulSearches();
53 | }
54 | return 0;
55 | }
56 |
57 | @SuppressWarnings("unused")
58 | @JMXBeanAttribute(name = "numberOfFailedSearches", description = "The accumulated number of failed searches")
59 | public long getNumberOfFailedSearches() {
60 | return numberOfFailedSearches.get();
61 | }
62 |
63 | @SuppressWarnings("unused")
64 | @JMXBeanAttribute(name = "numberOfFetchedDocuments", description = "The accumulated number of retrieved documents from searches")
65 | public long getNumberOfFetchedDocuments() {
66 | return numberOfFetchedDocuments.get();
67 | }
68 |
69 | public void incrementSuccessfulSearches() {
70 | numberOfSuccessfulSearches.incrementAndGet();
71 | }
72 |
73 | public void incrementTotalSearchTimeMs(long searchTime) {
74 | totalSuccessfulSearchesTimeMs.addAndGet(searchTime);
75 | }
76 |
77 | public void incrementNumberOfFailedSearches() {
78 | numberOfFailedSearches.incrementAndGet();
79 | }
80 |
81 | public void incrementNumberOfFetchedDocuments(long docCount) {
82 | numberOfFetchedDocuments.addAndGet(docCount);
83 | }
84 | }
--------------------------------------------------------------------------------
/src/main/resources/logback.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | [%d{HH:mm:ss.SSS}] [%thread] %-5level %logger - %msg%n
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/main/resources/templates/documents/document1.json:
--------------------------------------------------------------------------------
1 | {
2 | "@timestamp": TIMESTAMP,
3 | "message": "RANDSTR",
4 | "itemA": "RANDSTR",
5 | "itemB": "RANDSTR",
6 | "clientId": RANDINT,
7 | "productId": RANDINT
8 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/documents/document2.json:
--------------------------------------------------------------------------------
1 | {
2 | "@timestamp": TIMESTAMP,
3 | "bookId": RANDINT,
4 | "orderId": RANDINT,
5 | "bookName": "RANDSTR"
6 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/documents/document3.json:
--------------------------------------------------------------------------------
1 | {
2 | "@timestamp": TIMESTAMP,
3 | "clientPassword": "RANDSTR",
4 | "clientName": "RANDSTR",
5 | "clientAge": RANDINT,
6 | "clientId": RANDINT
7 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/documents/document4.json:
--------------------------------------------------------------------------------
1 | {
2 | "@timestamp": TIMESTAMP,
3 | "metricName": "RANDSTR",
4 | "value": RANDINT
5 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/documents/document5.json:
--------------------------------------------------------------------------------
1 | {
2 | "@timestamp": TIMESTAMP,
3 | "message": "RANDSTR"
4 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/searches/datehistogram-with-sub-range-agg-2-hours.json:
--------------------------------------------------------------------------------
1 | {
2 | "query": {
3 | "bool": {
4 | "must": {
5 | "query_string": {
6 | "query": "*",
7 | "analyze_wildcard": true
8 | }
9 | },
10 | "filter": {
11 | "bool": {
12 | "must": [{
13 | "query_string": {
14 | "query": "*",
15 | "analyze_wildcard": true
16 | }
17 | }, {
18 | "range": {
19 | "@timestamp": {
20 | "gte": "now-2h",
21 | "lte": "now",
22 | "format": "epoch_millis"
23 | }
24 | }
25 | }],
26 | "must_not": []
27 | }
28 | }
29 | }
30 | },
31 | "size": 0,
32 | "aggs": {
33 | "2": {
34 | "date_histogram": {
35 | "field": "@timestamp",
36 | "interval": "1m",
37 | "min_doc_count": 1,
38 | "extended_bounds": {
39 | "min": "now-2h",
40 | "max": "now"
41 | }
42 | },
43 | "aggs": {
44 | "3": {
45 | "range": {
46 | "field": "value",
47 | "ranges": [{
48 | "from": 10,
49 | "to": 100
50 | }, {
51 | "from": 100,
52 | "to": 500
53 | }, {
54 | "from": 500,
55 | "to": 1000
56 | }],
57 | "keyed": true
58 | }
59 | }
60 | }
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/searches/datehistogram-with-terms-and-sub-cardinality-30-minutes.json:
--------------------------------------------------------------------------------
1 | {
2 | "size": 0,
3 | "query": {
4 | "bool": {
5 | "must" : {
6 | "query_string": {
7 | "query": "*",
8 | "analyze_wildcard": true
9 | }
10 | },
11 | "filter": {
12 | "bool": {
13 | "must": [{
14 | "range": {
15 | "@timestamp": {
16 | "gte": "now-30m",
17 | "lte": "now",
18 | "format": "epoch_millis"
19 | }
20 | }
21 | }],
22 | "must_not": []
23 | }
24 | }
25 | }
26 | },
27 | "aggs": {
28 | "2": {
29 | "date_histogram": {
30 | "field": "@timestamp",
31 | "interval": "1m",
32 | "min_doc_count": 1,
33 | "extended_bounds": {
34 | "min": "now-30m",
35 | "max": "now"
36 | }
37 | },
38 | "aggs": {
39 | "3": {
40 | "terms": {
41 | "field": "clientId",
42 | "size": 100,
43 | "order": {
44 | "1": "desc"
45 | }
46 | },
47 | "aggs": {
48 | "1": {
49 | "cardinality": {
50 | "field": "clientAge"
51 | }
52 | }
53 | }
54 | }
55 | }
56 | }
57 | }
58 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/searches/kibana-discover-last-30-minutes.json:
--------------------------------------------------------------------------------
1 | {
2 | "size": 500,
3 | "sort": [{
4 | "@timestamp": {
5 | "order": "desc",
6 | "unmapped_type": "boolean"
7 | }
8 | }],
9 | "highlight": {
10 | "pre_tags": ["@kibana-highlighted-field@"],
11 | "post_tags": ["@/kibana-highlighted-field@"],
12 | "require_field_match": false,
13 | "fragment_size": 2147483647
14 | },
15 | "query": {
16 | "bool": {
17 | "must": {
18 | "query_string": {
19 | "query": "*",
20 | "analyze_wildcard": true
21 | }
22 | },
23 | "filter": {
24 | "bool": {
25 | "must": [{
26 | "range": {
27 | "@timestamp": {
28 | "gte": "now-30m",
29 | "lte": "now",
30 | "format": "epoch_millis"
31 | }
32 | }
33 | }],
34 | "must_not": []
35 | }
36 | }
37 | }
38 | },
39 | "aggs": {
40 | "2": {
41 | "date_histogram": {
42 | "field": "@timestamp",
43 | "interval": "30s",
44 | "min_doc_count": 0,
45 | "extended_bounds": {
46 | "min": "now-30m",
47 | "max": "now"
48 | }
49 | }
50 | }
51 | },
52 | "script_fields": {},
53 | "fielddata_fields": ["@timestamp"]
54 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/searches/simple-search-return-all.json:
--------------------------------------------------------------------------------
1 | {
2 | "query": {
3 | "query_string": {
4 | "query": "_type: benchmark"
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/searches/terms-10000-size-with-complex-query.json:
--------------------------------------------------------------------------------
1 | {
2 | "query": {
3 | "bool": {
4 | "must": {
5 | "query_string": {
6 | "query": "clientId: [100 TO 10000] AND clientAge: [100 TO 150] "
7 | }
8 | },
9 | "filter": {}
10 | }
11 | },
12 | "aggs": {
13 | "names": {
14 | "terms": {
15 | "field": "@timestamp",
16 | "size": 10000
17 | }
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------