/* * Licensed to Elasticsearch under one or more contributor * license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright * ownership. Elasticsearch 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.elasticsearch.client.benchmark; import org.elasticsearch.client.benchmark.ops.bulk.BulkBenchmarkTask; import org.elasticsearch.client.benchmark.ops.bulk.BulkRequestExecutor; import org.elasticsearch.client.benchmark.ops.search.SearchBenchmarkTask; import org.elasticsearch.client.benchmark.ops.search.SearchRequestExecutor; import org.elasticsearch.common.SuppressForbidden; import java.io.Closeable; import java.lang.management.GarbageCollectorMXBean; import java.lang.management.ManagementFactory; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; public abstract class AbstractBenchmark<T extends Closeable> { private static final int SEARCH_BENCHMARK_ITERATIONS = 10_000; protected abstract T client(String benchmarkTargetHost) throws Exception; protected abstract BulkRequestExecutor bulkRequestExecutor(T client, String indexName, String typeName); protected abstract SearchRequestExecutor searchRequestExecutor(T client, String indexName); @SuppressForbidden(reason = "system out is ok for a command line tool") public final void run(String[] args) throws Exception { if (args.length < 1) { System.err.println("usage: [search|bulk]"); System.exit(1); } switch (args[0]) { case "search": runSearchBenchmark(args); break; case "bulk": runBulkIndexBenchmark(args); break; default: System.err.println("Unknown benchmark type [" + args[0] + "]"); System.exit(1); } } @SuppressForbidden(reason = "system out is ok for a command line tool") private void runBulkIndexBenchmark(String[] args) throws Exception { if (args.length != 7) { System.err.println( "usage: 'bulk' benchmarkTargetHostIp indexFilePath indexName typeName numberOfDocuments bulkSize"); System.exit(1); } String benchmarkTargetHost = args[1]; String indexFilePath = args[2]; String indexName = args[3]; String typeName = args[4]; int totalDocs = Integer.valueOf(args[5]); int bulkSize = Integer.valueOf(args[6]); int totalIterationCount = (int) Math.floor(totalDocs / bulkSize); // consider 40% of all iterations as warmup iterations int warmupIterations = (int) (0.4d * totalIterationCount); int iterations = totalIterationCount - warmupIterations; T client = client(benchmarkTargetHost); BenchmarkRunner benchmark = new BenchmarkRunner(warmupIterations, iterations, new BulkBenchmarkTask( bulkRequestExecutor(client, indexName, typeName), indexFilePath, warmupIterations, iterations, bulkSize)); try { runTrials(() -> { runGc(); benchmark.run(); }); } finally { client.close(); } } @SuppressForbidden(reason = "system out is ok for a command line tool") private void runSearchBenchmark(String[] args) throws Exception { if (args.length != 5) { System.err.println( "usage: 'search' benchmarkTargetHostIp indexName searchRequestBody throughputRates"); System.exit(1); } String benchmarkTargetHost = args[1]; String indexName = args[2]; String searchBody = args[3]; List<Integer> throughputRates = Arrays.asList(args[4].split(",")).stream().map(Integer::valueOf).collect(Collectors.toList()); T client = client(benchmarkTargetHost); try { runTrials(() -> { for (int throughput : throughputRates) { //GC between trials to reduce the likelihood of a GC occurring in the middle of a trial. runGc(); BenchmarkRunner benchmark = new BenchmarkRunner(SEARCH_BENCHMARK_ITERATIONS, SEARCH_BENCHMARK_ITERATIONS, new SearchBenchmarkTask( searchRequestExecutor(client, indexName), searchBody, SEARCH_BENCHMARK_ITERATIONS, SEARCH_BENCHMARK_ITERATIONS, throughput)); System.out.printf("Target throughput = %d ops / s%n", throughput); benchmark.run(); } }); } finally { client.close(); } } @SuppressForbidden(reason = "system out is ok for a command line tool") private void runTrials(Runnable runner) { int totalWarmupTrialRuns = 1; for (int run = 1; run <= totalWarmupTrialRuns; run++) { System.out.println("======================"); System.out.println(" Warmup trial run " + run + "/" + totalWarmupTrialRuns); System.out.println("======================"); runner.run(); } int totalTrialRuns = 5; for (int run = 1; run <= totalTrialRuns; run++) { System.out.println("================"); System.out.println(" Trial run " + run + "/" + totalTrialRuns); System.out.println("================"); runner.run(); } } /** * Requests a full GC and checks whether the GC did actually run after a request. It retries up to 5 times in case the GC did not * run in time. */ @SuppressForbidden(reason = "we need to request a system GC for the benchmark") private void runGc() { long previousCollections = getTotalGcCount(); int attempts = 0; do { // request a full GC ... System.gc(); // ... and give GC a chance to run try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); return; } attempts++; } while (previousCollections == getTotalGcCount() || attempts < 5); } private long getTotalGcCount() { List<GarbageCollectorMXBean> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans(); return gcMxBeans.stream().mapToLong(GarbageCollectorMXBean::getCollectionCount).sum(); } }