<module>sonar-server</module>
<module>sonar-web</module>
<module>sonar-ws-client</module>
- <module>sonar-data-test</module>
+ <module>sonar-server-benchmarks</module>
</modules>
</project>
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8"?>
-<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
- <modelVersion>4.0.0</modelVersion>
- <parent>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>server</artifactId>
- <version>5.0-SNAPSHOT</version>
- <relativePath>..</relativePath>
- </parent>
- <artifactId>sonar-data-test</artifactId>
- <name>SonarQube :: Server :: Benchmark</name>
-
- <properties>
- <testInclusions/>
- <enableBenchmarkAssertions>true</enableBenchmarkAssertions>
- </properties>
-
- <dependencies>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-server</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-server</artifactId>
- <type>test-jar</type>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-search</artifactId>
- <version>${project.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>com.google.guava</groupId>
- <artifactId>guava</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.codehaus.sonar</groupId>
- <artifactId>sonar-testing-harness</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <skipTests>${skipServerTests}</skipTests>
- <argLine>-Xmx1G -Xms256m -server -Djava.awt.headless=true -DenableBenchmarkAssertions=${enableBenchmarkAssertions}</argLine>
- <includes>
- <include>${testInclusions}</include>
- </includes>
- </configuration>
- </plugin>
- </plugins>
- </build>
-
- <!--
- mvn install -DrunBenchmarks=true -DenableBenchmarkAssertions=true
-
- -->
- <profiles>
- <profile>
- <id>runBenchmarks</id>
- <activation>
- <property>
- <name>runBenchmarks</name>
- </property>
- </activation>
- <properties>
- <testInclusions>org/sonar/server/benchmark/**/*.java</testInclusions>
- </properties>
- </profile>
- </profiles>
-</project>
+++ /dev/null
-/*
- * 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;
-
-public class BenchmarkAssertions {
-
- private static final boolean ENABLED = "true".equals(System.getProperty("enableBenchmarkAssertions"));
-
- static {
- if (ENABLED) {
- System.out.println("Assertions are calibrated for a dedicated benchmark environment. " +
- "They can be disabled by setting the property -DenableBenchmarkAssertions=false.");
- }
- }
-
-}
+++ /dev/null
-/*
- * 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.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;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.sonar.api.issue.Issue;
-import org.sonar.api.rule.Severity;
-import org.sonar.api.utils.internal.Uuids;
-import org.sonar.server.issue.IssueQuery;
-import org.sonar.server.issue.index.IssueAuthorizationDao;
-import org.sonar.server.issue.index.IssueAuthorizationIndexer;
-import org.sonar.server.issue.index.IssueDoc;
-import org.sonar.server.issue.index.IssueIndex;
-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.concurrent.atomic.AtomicLong;
-
-public class IssueIndexBenchmarkTest {
-
- private static final Logger LOGGER = LoggerFactory.getLogger("benchmarkIssues");
-
- final static int PROJECTS = 100;
- final static int FILES_PER_PROJECT = 100;
- final static int ISSUES_PER_FILE = 100;
-
- @ClassRule
- public static ServerTester tester = new ServerTester();
-
- @Test
- public void benchmark() throws Exception {
- // initialization - feed issues/issueAuthorization with projects and hardcoded users
- indexAuthorizations();
-
- // index issues
- benchmarkIssueIndexing();
-
- // execute some queries
- benchmarkQueries();
- }
-
- private void indexAuthorizations() {
- LOGGER.info("Indexing issue authorizations");
- IssueAuthorizationIndexer indexer = tester.get(IssueAuthorizationIndexer.class);
- List<IssueAuthorizationDao.Dto> authorizations = Lists.newArrayList();
- for (int i = 0; i < PROJECTS; i++) {
- IssueAuthorizationDao.Dto authorization = new IssueAuthorizationDao.Dto("PROJECT" + i, System.currentTimeMillis());
- authorization.addGroup("sonar-users");
- authorization.addUser("admin");
- authorizations.add(authorization);
- }
- long start = System.currentTimeMillis();
- indexer.index(authorizations);
- long period = System.currentTimeMillis() - start;
- LOGGER.info(String.format("%d authorizations indexed in %d ms (%d docs/second)", PROJECTS, period, 1000 * PROJECTS / period));
- }
-
- private void benchmarkIssueIndexing() {
- LOGGER.info("Indexing issues");
- IssueIterator issues = new IssueIterator(PROJECTS, FILES_PER_PROJECT, ISSUES_PER_FILE);
- ProgressTask progressTask = new ProgressTask(LOGGER, "issues", issues.count());
- Timer timer = new Timer("IssuesIndex");
- timer.schedule(progressTask, ProgressTask.PERIOD_MS, ProgressTask.PERIOD_MS);
-
- long start = System.currentTimeMillis();
- tester.get(IssueIndexer.class).index(issues);
-
- timer.cancel();
- long period = System.currentTimeMillis() - start;
- 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());
- benchmarkQuery("various", IssueQuery.builder()
- .resolutions(Arrays.asList(Issue.RESOLUTION_FIXED))
- .assigned(true)
- .build());
- // TODO test facets
- }
-
- private void benchmarkQuery(String label, IssueQuery query) {
- IssueIndex index = tester.get(IssueIndex.class);
- for (int i = 0; i < 10; i++) {
- long start = System.currentTimeMillis();
- Result<Issue> result = index.search(query, new QueryContext());
- long end = System.currentTimeMillis();
- LOGGER.info("Request (" + label + "): {} docs in {} ms", result.getTotal(), end - start);
- }
- }
-
- private static class IssueIterator implements Iterator<IssueDoc> {
- private final int nbProjects, nbFilesPerProject, nbIssuesPerFile;
- private int currentProject = 0, currentFile = 0;
- private AtomicLong count = new AtomicLong(0L);
- private final Iterator<String> users = cycleIterator("guy", 200);
- private Iterator<String> rules = cycleIterator("squid:rule", 1000);
- private final Iterator<String> severities = Iterables.cycle(Severity.ALL).iterator();
- private final Iterator<String> statuses = Iterables.cycle(Issue.STATUSES).iterator();
- private final Iterator<String> resolutions = Iterables.cycle(Issue.RESOLUTIONS).iterator();
-
- IssueIterator(int nbProjects, int nbFilesPerProject, int nbIssuesPerFile) {
- this.nbProjects = nbProjects;
- this.nbFilesPerProject = nbFilesPerProject;
- this.nbIssuesPerFile = nbIssuesPerFile;
- }
-
- public AtomicLong count() {
- return count;
- }
-
- @Override
- public boolean hasNext() {
- return count.get() < nbProjects * nbFilesPerProject * nbIssuesPerFile;
- }
-
- @Override
- public IssueDoc next() {
- IssueDoc issue = new IssueDoc(Maps.<String, Object>newHashMap());
- issue.setKey(Uuids.create());
- issue.setFilePath("src/main/java/Foo" + currentFile);
- issue.setComponentUuid("FILE" + currentFile);
- issue.setProjectUuid("PROJECT" + currentProject);
- issue.setActionPlanKey("PLAN" + currentProject);
- issue.setAssignee(users.next());
- issue.setAuthorLogin(users.next());
- issue.setLine(RandomUtils.nextInt());
- issue.setTechnicalCreationDate(new Date());
- issue.setTechnicalUpdateDate(new Date());
- issue.setFuncUpdateDate(new Date());
- issue.setFuncCreationDate(new Date());
- issue.setFuncCloseDate(null);
- issue.setAttributes(null);
- issue.setDebt(1000L);
- issue.setEffortToFix(3.14);
- issue.setLanguage("php");
- issue.setReporter(users.next());
- issue.setRuleKey(rules.next());
- issue.setResolution(resolutions.next());
- issue.setStatus(statuses.next());
- issue.setSeverity(severities.next());
- issue.setMessage(RandomUtils.nextLong() + "this is the message. Not too short.");
- count.incrementAndGet();
- if (count.get() % nbIssuesPerFile == 0) {
- currentFile++;
- }
- if (count.get() % (nbFilesPerProject * nbIssuesPerFile) == 0) {
- currentProject++;
- }
-
- return issue;
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- }
-
- private static Iterator<String> cycleIterator(String prefix, int size) {
- List<String> values = Lists.newArrayList();
- for (int i = 0; i < size; i++) {
- values.add(String.format("%s%d", prefix, i));
- }
- return Iterators.cycle(values);
- }
-}
+++ /dev/null
-/*
- * 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));
- }
-}
+++ /dev/null
-/*
- * 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<SourceLineDoc> 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<SourceLineResultSetIterator.SourceFile> {
- 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.<String, Object>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();
- }
- }
-
-}
+++ /dev/null
-<?xml version="1.0" encoding="UTF-8" ?>
-
-<configuration>
-
- <appender name="STDOUT"
- class="ch.qos.logback.core.ConsoleAppender">
- <encoder>
- <pattern>
- %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
- </pattern>
- </encoder>
- </appender>
-
- <logger name="org.hibernate">
- <level value="WARN"/>
- </logger>
-
- <logger name="org.dbunit">
- <level value="WARN"/>
- </logger>
-
- <!-- set to level DEBUG to log SQL requests executed by MyBatis -->
- <logger name="java.sql">
- <level value="WARN"/>
- </logger>
-
- <root>
- <level value="INFO"/>
- <appender-ref ref="STDOUT"/>
- </root>
-
-</configuration>
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>server</artifactId>
+ <version>5.0-SNAPSHOT</version>
+ <relativePath>..</relativePath>
+ </parent>
+ <artifactId>sonar-server-benchmarks</artifactId>
+ <name>SonarQube :: Server :: Benchmark</name>
+
+ <properties>
+ <testInclusions/>
+ <enableBenchmarkAssertions>true</enableBenchmarkAssertions>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-server</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-server</artifactId>
+ <type>test-jar</type>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-search</artifactId>
+ <version>${project.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.sonar</groupId>
+ <artifactId>sonar-testing-harness</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <skipTests>${skipServerTests}</skipTests>
+ <argLine>-Xmx1G -Xms256m -server -Djava.awt.headless=true -DenableBenchmarkAssertions=${enableBenchmarkAssertions}</argLine>
+ <includes>
+ <include>${testInclusions}</include>
+ </includes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <!--
+ mvn install -DrunBenchmarks=true -DenableBenchmarkAssertions=true
+
+ -->
+ <profiles>
+ <profile>
+ <id>runBenchmarks</id>
+ <activation>
+ <property>
+ <name>runBenchmarks</name>
+ </property>
+ </activation>
+ <properties>
+ <testInclusions>org/sonar/server/benchmark/**/*.java</testInclusions>
+ </properties>
+ </profile>
+ </profiles>
+</project>
--- /dev/null
+/*
+ * 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;
+
+public class BenchmarkAssertions {
+
+ private static final boolean ENABLED = "true".equals(System.getProperty("enableBenchmarkAssertions"));
+
+ static {
+ if (ENABLED) {
+ System.out.println("Assertions are calibrated for a dedicated benchmark environment. " +
+ "They can be disabled by setting the property -DenableBenchmarkAssertions=false.");
+ }
+ }
+
+}
--- /dev/null
+/*
+ * 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.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;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.sonar.api.issue.Issue;
+import org.sonar.api.rule.Severity;
+import org.sonar.api.utils.internal.Uuids;
+import org.sonar.server.issue.IssueQuery;
+import org.sonar.server.issue.index.IssueAuthorizationDao;
+import org.sonar.server.issue.index.IssueAuthorizationIndexer;
+import org.sonar.server.issue.index.IssueDoc;
+import org.sonar.server.issue.index.IssueIndex;
+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.concurrent.atomic.AtomicLong;
+
+public class IssueIndexBenchmarkTest {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger("benchmarkIssues");
+
+ final static int PROJECTS = 100;
+ final static int FILES_PER_PROJECT = 100;
+ final static int ISSUES_PER_FILE = 100;
+
+ @ClassRule
+ public static ServerTester tester = new ServerTester();
+
+ @Test
+ public void benchmark() throws Exception {
+ // initialization - feed issues/issueAuthorization with projects and hardcoded users
+ indexAuthorizations();
+
+ // index issues
+ benchmarkIssueIndexing();
+
+ // execute some queries
+ benchmarkQueries();
+ }
+
+ private void indexAuthorizations() {
+ LOGGER.info("Indexing issue authorizations");
+ IssueAuthorizationIndexer indexer = tester.get(IssueAuthorizationIndexer.class);
+ List<IssueAuthorizationDao.Dto> authorizations = Lists.newArrayList();
+ for (int i = 0; i < PROJECTS; i++) {
+ IssueAuthorizationDao.Dto authorization = new IssueAuthorizationDao.Dto("PROJECT" + i, System.currentTimeMillis());
+ authorization.addGroup("sonar-users");
+ authorization.addUser("admin");
+ authorizations.add(authorization);
+ }
+ long start = System.currentTimeMillis();
+ indexer.index(authorizations);
+ long period = System.currentTimeMillis() - start;
+ LOGGER.info(String.format("%d authorizations indexed in %d ms (%d docs/second)", PROJECTS, period, 1000 * PROJECTS / period));
+ }
+
+ private void benchmarkIssueIndexing() {
+ LOGGER.info("Indexing issues");
+ IssueIterator issues = new IssueIterator(PROJECTS, FILES_PER_PROJECT, ISSUES_PER_FILE);
+ ProgressTask progressTask = new ProgressTask(LOGGER, "issues", issues.count());
+ Timer timer = new Timer("IssuesIndex");
+ timer.schedule(progressTask, ProgressTask.PERIOD_MS, ProgressTask.PERIOD_MS);
+
+ long start = System.currentTimeMillis();
+ tester.get(IssueIndexer.class).index(issues);
+
+ timer.cancel();
+ long period = System.currentTimeMillis() - start;
+ 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());
+ benchmarkQuery("various", IssueQuery.builder()
+ .resolutions(Arrays.asList(Issue.RESOLUTION_FIXED))
+ .assigned(true)
+ .build());
+ // TODO test facets
+ }
+
+ private void benchmarkQuery(String label, IssueQuery query) {
+ IssueIndex index = tester.get(IssueIndex.class);
+ for (int i = 0; i < 10; i++) {
+ long start = System.currentTimeMillis();
+ Result<Issue> result = index.search(query, new QueryContext());
+ long end = System.currentTimeMillis();
+ LOGGER.info("Request (" + label + "): {} docs in {} ms", result.getTotal(), end - start);
+ }
+ }
+
+ private static class IssueIterator implements Iterator<IssueDoc> {
+ private final int nbProjects, nbFilesPerProject, nbIssuesPerFile;
+ private int currentProject = 0, currentFile = 0;
+ private AtomicLong count = new AtomicLong(0L);
+ private final Iterator<String> users = cycleIterator("guy", 200);
+ private Iterator<String> rules = cycleIterator("squid:rule", 1000);
+ private final Iterator<String> severities = Iterables.cycle(Severity.ALL).iterator();
+ private final Iterator<String> statuses = Iterables.cycle(Issue.STATUSES).iterator();
+ private final Iterator<String> resolutions = Iterables.cycle(Issue.RESOLUTIONS).iterator();
+
+ IssueIterator(int nbProjects, int nbFilesPerProject, int nbIssuesPerFile) {
+ this.nbProjects = nbProjects;
+ this.nbFilesPerProject = nbFilesPerProject;
+ this.nbIssuesPerFile = nbIssuesPerFile;
+ }
+
+ public AtomicLong count() {
+ return count;
+ }
+
+ @Override
+ public boolean hasNext() {
+ return count.get() < nbProjects * nbFilesPerProject * nbIssuesPerFile;
+ }
+
+ @Override
+ public IssueDoc next() {
+ IssueDoc issue = new IssueDoc(Maps.<String, Object>newHashMap());
+ issue.setKey(Uuids.create());
+ issue.setFilePath("src/main/java/Foo" + currentFile);
+ issue.setComponentUuid("FILE" + currentFile);
+ issue.setProjectUuid("PROJECT" + currentProject);
+ issue.setActionPlanKey("PLAN" + currentProject);
+ issue.setAssignee(users.next());
+ issue.setAuthorLogin(users.next());
+ issue.setLine(RandomUtils.nextInt());
+ issue.setTechnicalCreationDate(new Date());
+ issue.setTechnicalUpdateDate(new Date());
+ issue.setFuncUpdateDate(new Date());
+ issue.setFuncCreationDate(new Date());
+ issue.setFuncCloseDate(null);
+ issue.setAttributes(null);
+ issue.setDebt(1000L);
+ issue.setEffortToFix(3.14);
+ issue.setLanguage("php");
+ issue.setReporter(users.next());
+ issue.setRuleKey(rules.next());
+ issue.setResolution(resolutions.next());
+ issue.setStatus(statuses.next());
+ issue.setSeverity(severities.next());
+ issue.setMessage(RandomUtils.nextLong() + "this is the message. Not too short.");
+ count.incrementAndGet();
+ if (count.get() % nbIssuesPerFile == 0) {
+ currentFile++;
+ }
+ if (count.get() % (nbFilesPerProject * nbIssuesPerFile) == 0) {
+ currentProject++;
+ }
+
+ return issue;
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private static Iterator<String> cycleIterator(String prefix, int size) {
+ List<String> values = Lists.newArrayList();
+ for (int i = 0; i < size; i++) {
+ values.add(String.format("%s%d", prefix, i));
+ }
+ return Iterators.cycle(values);
+ }
+}
--- /dev/null
+/*
+ * 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));
+ }
+}
--- /dev/null
+/*
+ * 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<SourceLineDoc> 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<SourceLineResultSetIterator.SourceFile> {
+ 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.<String, Object>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();
+ }
+ }
+
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8" ?>
+
+<configuration>
+
+ <appender name="STDOUT"
+ class="ch.qos.logback.core.ConsoleAppender">
+ <encoder>
+ <pattern>
+ %d{HH:mm:ss.SSS} %-5level %logger{36} - %msg%n
+ </pattern>
+ </encoder>
+ </appender>
+
+ <logger name="org.hibernate">
+ <level value="WARN"/>
+ </logger>
+
+ <logger name="org.dbunit">
+ <level value="WARN"/>
+ </logger>
+
+ <!-- set to level DEBUG to log SQL requests executed by MyBatis -->
+ <logger name="java.sql">
+ <level value="WARN"/>
+ </logger>
+
+ <root>
+ <level value="INFO"/>
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>