From 5568c4d155a1bb9913b58adfce75467dd15882ea Mon Sep 17 00:00:00 2001 From: Simon Brandhof Date: Mon, 1 Dec 2014 11:21:16 +0100 Subject: [PATCH] Add server benchmarks --- .../benchmark/IssueIndexBenchmarkTest.java | 38 +---- .../sonar/server/benchmark/ProgressTask.java | 57 +++++++ .../benchmark/SourceIndexBenchmarkTest.java | 150 ++++++++++++++++++ .../org/sonar/server/es/EsServerHolder.java | 10 +- .../org/sonar/server/tester/ServerTester.java | 9 +- 5 files changed, 228 insertions(+), 36 deletions(-) create mode 100644 server/sonar-data-test/src/test/java/org/sonar/server/benchmark/ProgressTask.java create mode 100644 server/sonar-data-test/src/test/java/org/sonar/server/benchmark/SourceIndexBenchmarkTest.java diff --git a/server/sonar-data-test/src/test/java/org/sonar/server/benchmark/IssueIndexBenchmarkTest.java b/server/sonar-data-test/src/test/java/org/sonar/server/benchmark/IssueIndexBenchmarkTest.java index 6d1eee42a19..820d1b6a9f7 100644 --- a/server/sonar-data-test/src/test/java/org/sonar/server/benchmark/IssueIndexBenchmarkTest.java +++ b/server/sonar-data-test/src/test/java/org/sonar/server/benchmark/IssueIndexBenchmarkTest.java @@ -24,6 +24,7 @@ import com.google.common.collect.Iterables; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.Maps; +import org.apache.commons.io.FileUtils; import org.apache.commons.lang.math.RandomUtils; import org.junit.ClassRule; import org.junit.Test; @@ -41,13 +42,13 @@ import org.sonar.server.issue.index.IssueIndexer; import org.sonar.server.search.QueryContext; import org.sonar.server.search.Result; import org.sonar.server.tester.ServerTester; +import org.sonar.server.user.MockUserSession; import java.util.Arrays; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Timer; -import java.util.TimerTask; import java.util.concurrent.atomic.AtomicLong; public class IssueIndexBenchmarkTest { @@ -88,7 +89,7 @@ public class IssueIndexBenchmarkTest { private void benchmarkIssueIndexing() { LOGGER.info("Indexing issues"); IssueIterator issues = new IssueIterator(PROJECTS, FILES_PER_PROJECT, ISSUES_PER_FILE); - ProgressTask progressTask = new ProgressTask("issues", issues.count()); + ProgressTask progressTask = new ProgressTask(LOGGER, "issues", issues.count()); Timer timer = new Timer("IssuesIndex"); timer.schedule(progressTask, ProgressTask.PERIOD_MS, ProgressTask.PERIOD_MS); @@ -98,10 +99,12 @@ public class IssueIndexBenchmarkTest { timer.cancel(); long period = end - start; - LOGGER.info(String.format("%d issues indexed in %d ms (%d/second)", issues.count.get(), period, 1000 * issues.count.get() / period)); + LOGGER.info(String.format("%d issues indexed in %d ms (%d docs/second)", issues.count.get(), period, 1000 * issues.count.get() / period)); + LOGGER.info(String.format("Index disk: " + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(tester.getEsServerHolder().getHomeDir())))); } private void benchmarkQueries() { + MockUserSession.set().setUserGroups("sonar-users"); benchmarkQuery("all issues", IssueQuery.builder().build()); benchmarkQuery("project issues", IssueQuery.builder().projectUuids(Arrays.asList("PROJECT33")).build()); benchmarkQuery("file issues", IssueQuery.builder().componentUuids(Arrays.asList("FILE333")).build()); @@ -197,33 +200,4 @@ public class IssueIndexBenchmarkTest { } return Iterators.cycle(values); } - - static class ProgressTask extends TimerTask { - public static final long PERIOD_MS = 60000L; - - private final String label; - private final AtomicLong counter; - private long previousCount = 0L; - private long previousTime = 0L; - - public ProgressTask(String label, AtomicLong counter) { - this.label = label; - this.counter = counter; - this.previousTime = System.currentTimeMillis(); - } - - @Override - public void run() { - long currentCount = counter.get(); - long now = System.currentTimeMillis(); - LOGGER.info("{} {} indexed ({} {}/second)", - currentCount, label, documentsPerSecond(currentCount - previousCount, now - previousTime), label); - this.previousCount = currentCount; - this.previousTime = now; - } - } - - static int documentsPerSecond(long nbIssues, long time) { - return (int) Math.round(nbIssues / (time / 1000.0)); - } } diff --git a/server/sonar-data-test/src/test/java/org/sonar/server/benchmark/ProgressTask.java b/server/sonar-data-test/src/test/java/org/sonar/server/benchmark/ProgressTask.java new file mode 100644 index 00000000000..4b5161273be --- /dev/null +++ b/server/sonar-data-test/src/test/java/org/sonar/server/benchmark/ProgressTask.java @@ -0,0 +1,57 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonar.server.benchmark; + +import org.slf4j.Logger; + +import java.util.TimerTask; +import java.util.concurrent.atomic.AtomicLong; + +class ProgressTask extends TimerTask { + + public static final long PERIOD_MS = 60000L; + + private final Logger logger; + private final String label; + private final AtomicLong counter; + private long previousCount = 0L; + private long previousTime = 0L; + + public ProgressTask(Logger logger, String label, AtomicLong counter) { + this.logger = logger; + this.label = label; + this.counter = counter; + this.previousTime = System.currentTimeMillis(); + } + + @Override + public void run() { + long currentCount = counter.get(); + long now = System.currentTimeMillis(); + logger.info("{} {} indexed ({} docs/second)", + currentCount, label, documentsPerSecond(currentCount - previousCount, now - previousTime), label); + this.previousCount = currentCount; + this.previousTime = now; + } + + private int documentsPerSecond(long nbDocs, long time) { + return (int) Math.round(nbDocs / (time / 1000.0)); + } +} diff --git a/server/sonar-data-test/src/test/java/org/sonar/server/benchmark/SourceIndexBenchmarkTest.java b/server/sonar-data-test/src/test/java/org/sonar/server/benchmark/SourceIndexBenchmarkTest.java new file mode 100644 index 00000000000..e3c8212561f --- /dev/null +++ b/server/sonar-data-test/src/test/java/org/sonar/server/benchmark/SourceIndexBenchmarkTest.java @@ -0,0 +1,150 @@ +/* + * SonarQube, open source software quality management tool. + * Copyright (C) 2008-2014 SonarSource + * mailto:contact AT sonarsource DOT com + * + * SonarQube is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * SonarQube is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package org.sonar.server.benchmark; + +import com.google.common.collect.Maps; +import org.apache.commons.io.FileUtils; +import org.apache.commons.lang.StringUtils; +import org.junit.ClassRule; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.sonar.server.source.index.SourceLineDoc; +import org.sonar.server.source.index.SourceLineIndex; +import org.sonar.server.source.index.SourceLineIndexer; +import org.sonar.server.source.index.SourceLineResultSetIterator; +import org.sonar.server.tester.ServerTester; + +import java.util.Iterator; +import java.util.List; +import java.util.Timer; +import java.util.concurrent.atomic.AtomicLong; + +import static org.fest.assertions.Assertions.assertThat; + +public class SourceIndexBenchmarkTest { + + private static final Logger LOGGER = LoggerFactory.getLogger("benchmarkSources"); + + final static long FILES = 10000L; + static final int LINES_PER_FILE = 200; + + @ClassRule + public static ServerTester tester = new ServerTester(); + + @Test + public void benchmark() throws Exception { + // index source lines + benchmarkIndexing(); + + // execute some queries + benchmarkQueries(); + } + + private void benchmarkIndexing() { + LOGGER.info("Indexing source lines"); + + SourceIterator files = new SourceIterator(FILES, LINES_PER_FILE); + ProgressTask progressTask = new ProgressTask(LOGGER, "files of " + LINES_PER_FILE + " lines", files.count()); + Timer timer = new Timer("SourceIndexer"); + timer.schedule(progressTask, ProgressTask.PERIOD_MS, ProgressTask.PERIOD_MS); + + long start = System.currentTimeMillis(); + tester.get(SourceLineIndexer.class).index(files); + long end = System.currentTimeMillis(); + + timer.cancel(); + long period = end - start; + long nbDocs = files.count.get() * LINES_PER_FILE; + LOGGER.info(String.format("%d files indexed in %d ms (%d docs/second)", nbDocs, period, 1000 * nbDocs / period)); + LOGGER.info(String.format("Index disk: " + FileUtils.byteCountToDisplaySize(FileUtils.sizeOfDirectory(tester.getEsServerHolder().getHomeDir())))); + + } + + private void benchmarkQueries() { + SourceLineIndex index = tester.get(SourceLineIndex.class); + for (int i = 1; i <= 100; i++) { + long start = System.currentTimeMillis(); + List result = index.getLines("FILE" + i, 20, 150); + long end = System.currentTimeMillis(); + assertThat(result).hasSize(131); + LOGGER.info("Request: {} docs in {} ms", result.size(), end - start); + } + } + + private static class SourceIterator implements Iterator { + private final long nbFiles; + private final int nbLinesPerFile; + private int currentProject = 0; + private AtomicLong count = new AtomicLong(0L); + + SourceIterator(long nbFiles, int nbLinesPerFile) { + this.nbFiles = nbFiles; + this.nbLinesPerFile = nbLinesPerFile; + } + + public AtomicLong count() { + return count; + } + + @Override + public boolean hasNext() { + return count.get() < nbFiles; + } + + @Override + public SourceLineResultSetIterator.SourceFile next() { + String fileUuid = "FILE" + count.get(); + SourceLineResultSetIterator.SourceFile file = new SourceLineResultSetIterator.SourceFile(fileUuid, System.currentTimeMillis()); + for (int indexLine = 1; indexLine <= nbLinesPerFile; indexLine++) { + SourceLineDoc line = new SourceLineDoc(Maps.newHashMap()); + line.setFileUuid(fileUuid); + line.setLine(indexLine); + line.setHighlighting(StringUtils.repeat("HIGHLIGHTING", 5)); + line.setItConditions(4); + line.setItCoveredConditions(2); + line.setItLineHits(2); + line.setOverallConditions(8); + line.setOverallCoveredConditions(2); + line.setOverallLineHits(2); + line.setUtConditions(8); + line.setUtCoveredConditions(2); + line.setUtLineHits(2); + line.setProjectUuid("PROJECT" + currentProject); + line.setScmAuthor("a_guy"); + line.setScmRevision("ABCDEFGHIJKL"); + line.setSource(StringUtils.repeat("SOURCE", 10)); + file.addLine(line); + } + count.incrementAndGet(); + if (count.get() % 500 == 0) { + currentProject++; + } + return file; + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/server/sonar-server/src/test/java/org/sonar/server/es/EsServerHolder.java b/server/sonar-server/src/test/java/org/sonar/server/es/EsServerHolder.java index 28810012ff5..713cb3ed6a6 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/es/EsServerHolder.java +++ b/server/sonar-server/src/test/java/org/sonar/server/es/EsServerHolder.java @@ -38,13 +38,15 @@ public class EsServerHolder { private static EsServerHolder HOLDER = null; private final String clusterName, nodeName; private final int port; + private final File homeDir; private final SearchServer server; - private EsServerHolder(SearchServer server, String clusterName, String nodeName, int port) { + private EsServerHolder(SearchServer server, String clusterName, String nodeName, int port, File homeDir) { this.server = server; this.clusterName = clusterName; this.nodeName = nodeName; this.port = port; + this.homeDir = homeDir; } public String getClusterName() { @@ -63,6 +65,10 @@ public class EsServerHolder { return server; } + public File getHomeDir() { + return homeDir; + } + private void reset() { TransportClient client = new TransportClient(ImmutableSettings.settingsBuilder() .put("node.name", nodeName) @@ -101,7 +107,7 @@ public class EsServerHolder { properties.setProperty(ProcessConstants.PATH_HOME, homeDir.getAbsolutePath()); SearchServer server = new SearchServer(new Props(properties)); server.start(); - HOLDER = new EsServerHolder(server, clusterName, nodeName, port); + HOLDER = new EsServerHolder(server, clusterName, nodeName, port, homeDir); } HOLDER.reset(); return HOLDER; diff --git a/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java b/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java index 8514dcfa3ce..e444f2be0fa 100644 --- a/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java +++ b/server/sonar-server/src/test/java/org/sonar/server/tester/ServerTester.java @@ -58,6 +58,7 @@ public class ServerTester extends ExternalResource { private static final String PROP_PREFIX = "mediumTests."; private Platform platform; + private EsServerHolder esServerHolder; private final File homeDir = TestUtils.newTempDir("tmp-sq-"); private final List components = Lists.newArrayList(WsTester.class); private final Properties initialProps = new Properties(); @@ -79,8 +80,7 @@ public class ServerTester extends ExternalResource { try { Properties properties = new Properties(); properties.putAll(initialProps); - EsServerHolder esServerHolder = EsServerHolder.get(); - + esServerHolder = EsServerHolder.get(); properties.setProperty(ProcessConstants.CLUSTER_NAME, esServerHolder.getClusterName()); properties.setProperty(ProcessConstants.CLUSTER_NODE_NAME, esServerHolder.getNodeName()); properties.setProperty(ProcessConstants.SEARCH_PORT, String.valueOf(esServerHolder.getPort())); @@ -126,6 +126,7 @@ public class ServerTester extends ExternalResource { } catch (Exception e) { LOG.error("Fail to stop web server", e); } + esServerHolder = null; FileUtils.deleteQuietly(homeDir); } @@ -193,6 +194,10 @@ public class ServerTester extends ExternalResource { return get(WsTester.class); } + public EsServerHolder getEsServerHolder() { + return esServerHolder; + } + private void checkStarted() { if (platform == null || !platform.isStarted()) { throw new IllegalStateException("Not started"); -- 2.39.5