|
|
@@ -0,0 +1,191 @@
|
|
|
+/*
|
|
|
+ * 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.benchmark.breaker;
|
|
|
+
|
|
|
+import org.elasticsearch.action.bulk.BulkRequestBuilder;
|
|
|
+import org.elasticsearch.action.search.SearchResponse;
|
|
|
+import org.elasticsearch.action.search.SearchType;
|
|
|
+import org.elasticsearch.client.Client;
|
|
|
+import org.elasticsearch.common.breaker.CircuitBreaker;
|
|
|
+import org.elasticsearch.common.settings.ImmutableSettings;
|
|
|
+import org.elasticsearch.common.settings.Settings;
|
|
|
+import org.elasticsearch.indices.IndexAlreadyExistsException;
|
|
|
+import org.elasticsearch.indices.breaker.HierarchyCircuitBreakerService;
|
|
|
+import org.elasticsearch.node.Node;
|
|
|
+import org.elasticsearch.node.NodeBuilder;
|
|
|
+import org.elasticsearch.search.aggregations.bucket.terms.Terms;
|
|
|
+
|
|
|
+import java.util.UUID;
|
|
|
+import java.util.concurrent.atomic.AtomicLong;
|
|
|
+
|
|
|
+import static junit.framework.Assert.assertNotNull;
|
|
|
+import static org.elasticsearch.common.settings.ImmutableSettings.settingsBuilder;
|
|
|
+import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
|
|
|
+import static org.elasticsearch.search.aggregations.AggregationBuilders.terms;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Benchmarks for different implementations of the circuit breaker
|
|
|
+ */
|
|
|
+public class CircuitBreakerBenchmark {
|
|
|
+
|
|
|
+ private static final String INDEX = UUID.randomUUID().toString();
|
|
|
+ private static final int QUERIES = 100;
|
|
|
+ private static final int BULK_SIZE = 100;
|
|
|
+ private static final int NUM_DOCS = 2_000_000;
|
|
|
+ private static final int AGG_SIZE = 25;
|
|
|
+
|
|
|
+ private static void switchToNoop(Client client) {
|
|
|
+ Settings settings = settingsBuilder()
|
|
|
+ .put(HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_TYPE_SETTING, CircuitBreaker.Type.NOOP)
|
|
|
+ .build();
|
|
|
+ client.admin().cluster().prepareUpdateSettings().setTransientSettings(settings).execute().actionGet();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void switchToMemory(Client client) {
|
|
|
+ Settings settings = settingsBuilder()
|
|
|
+ .put(HierarchyCircuitBreakerService.REQUEST_CIRCUIT_BREAKER_TYPE_SETTING, CircuitBreaker.Type.MEMORY)
|
|
|
+ .build();
|
|
|
+ client.admin().cluster().prepareUpdateSettings().setTransientSettings(settings).execute().actionGet();
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void runSingleThreadedQueries(Client client) {
|
|
|
+ long totalTime = 0;
|
|
|
+ for (int i = 0; i < QUERIES; i++) {
|
|
|
+ if (i % 10 == 0) {
|
|
|
+ System.out.println("--> query #" + i);
|
|
|
+ }
|
|
|
+ SearchResponse resp = client.prepareSearch(INDEX).setQuery(matchAllQuery())
|
|
|
+ .addAggregation(
|
|
|
+ terms("myterms")
|
|
|
+ .size(AGG_SIZE)
|
|
|
+ .field("num")
|
|
|
+ ).setSearchType(SearchType.COUNT).get();
|
|
|
+ Terms terms = resp.getAggregations().get("myterms");
|
|
|
+ assertNotNull("term aggs were calculated", terms);
|
|
|
+ totalTime += resp.getTookInMillis();
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("--> single threaded average time: " + (totalTime / QUERIES) + "ms");
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void runMultiThreadedQueries(final Client client) throws Exception {
|
|
|
+ final AtomicLong totalThreadedTime = new AtomicLong(0);
|
|
|
+ int THREADS = 10;
|
|
|
+ Thread threads[] = new Thread[THREADS];
|
|
|
+ for (int i = 0; i < THREADS; i++) {
|
|
|
+ threads[i] = new Thread(new Runnable() {
|
|
|
+ @Override
|
|
|
+ public void run() {
|
|
|
+ long tid = Thread.currentThread().getId();
|
|
|
+ for (int i = 0; i < QUERIES; i++) {
|
|
|
+ if (i % 30 == 0) {
|
|
|
+ System.out.println("--> [" + tid + "] query # "+ i);
|
|
|
+ }
|
|
|
+ SearchResponse resp = client.prepareSearch(INDEX).setQuery(matchAllQuery())
|
|
|
+ .addAggregation(
|
|
|
+ terms("myterms")
|
|
|
+ .size(AGG_SIZE)
|
|
|
+ .field("num")
|
|
|
+ ).setSearchType(SearchType.COUNT).get();
|
|
|
+ Terms terms = resp.getAggregations().get("myterms");
|
|
|
+ assertNotNull("term aggs were calculated", terms);
|
|
|
+ totalThreadedTime.addAndGet(resp.getTookInMillis());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("--> starting " + THREADS + " threads for parallel aggregating");
|
|
|
+ for (Thread t : threads) {
|
|
|
+ t.start();
|
|
|
+ }
|
|
|
+
|
|
|
+ for (Thread t : threads) {
|
|
|
+ t.join();
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("--> threaded average time: " + (totalThreadedTime.get() / (THREADS * QUERIES)) + "ms");
|
|
|
+ }
|
|
|
+
|
|
|
+ public static void main(String args[]) throws Exception {
|
|
|
+ Node node = NodeBuilder.nodeBuilder().settings(ImmutableSettings.settingsBuilder()).node();
|
|
|
+ final Client client = node.client();
|
|
|
+ try {
|
|
|
+ try {
|
|
|
+ client.admin().indices().prepareDelete(INDEX).get();
|
|
|
+ } catch (Exception e) {
|
|
|
+ // Ignore
|
|
|
+ }
|
|
|
+ try {
|
|
|
+ client.admin().indices().prepareCreate(INDEX).setSettings(
|
|
|
+ settingsBuilder().put("number_of_shards", 2).put("number_of_replicas", 0)).get();
|
|
|
+ } catch (IndexAlreadyExistsException e) {}
|
|
|
+ client.admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet();
|
|
|
+
|
|
|
+
|
|
|
+ System.out.println("--> indexing: " + NUM_DOCS + " documents...");
|
|
|
+ BulkRequestBuilder bulkBuilder = client.prepareBulk();
|
|
|
+ for (int i = 0; i < NUM_DOCS; i++) {
|
|
|
+ bulkBuilder.add(client.prepareIndex(INDEX, "doc").setSource("num", i));
|
|
|
+ if (i % BULK_SIZE == 0) {
|
|
|
+ // Send off bulk request
|
|
|
+ bulkBuilder.get();
|
|
|
+ // Create a new holder
|
|
|
+ bulkBuilder = client.prepareBulk();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ bulkBuilder.get();
|
|
|
+ client.admin().indices().prepareRefresh(INDEX).get();
|
|
|
+ SearchResponse countResp = client.prepareSearch(INDEX).setQuery(matchAllQuery()).setSearchType(SearchType.COUNT).get();
|
|
|
+ assert countResp.getHits().getTotalHits() == NUM_DOCS : "all docs should be indexed";
|
|
|
+
|
|
|
+ final int warmupCount = 100;
|
|
|
+ for (int i = 0; i < warmupCount; i++) {
|
|
|
+ if (i % 15 == 0) {
|
|
|
+ System.out.println("--> warmup #" + i);
|
|
|
+ }
|
|
|
+ SearchResponse resp = client.prepareSearch(INDEX).setQuery(matchAllQuery())
|
|
|
+ .addAggregation(
|
|
|
+ terms("myterms")
|
|
|
+ .size(AGG_SIZE)
|
|
|
+ .field("num")
|
|
|
+ ).setSearchType(SearchType.COUNT).get();
|
|
|
+ Terms terms = resp.getAggregations().get("myterms");
|
|
|
+ assertNotNull("term aggs were calculated", terms);
|
|
|
+ }
|
|
|
+
|
|
|
+ System.out.println("--> running single-threaded tests");
|
|
|
+ runSingleThreadedQueries(client);
|
|
|
+ System.out.println("--> switching to NOOP breaker");
|
|
|
+ switchToNoop(client);
|
|
|
+ runSingleThreadedQueries(client);
|
|
|
+ switchToMemory(client);
|
|
|
+
|
|
|
+ System.out.println("--> running multi-threaded tests");
|
|
|
+ runMultiThreadedQueries(client);
|
|
|
+ System.out.println("--> switching to NOOP breaker");
|
|
|
+ switchToNoop(client);
|
|
|
+ runMultiThreadedQueries(client);
|
|
|
+ } finally {
|
|
|
+ client.close();
|
|
|
+ node.stop();
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|