2 * SonarQube, open source software quality management tool.
3 * Copyright (C) 2008-2014 SonarSource
4 * mailto:contact AT sonarsource DOT com
6 * SonarQube is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 3 of the License, or (at your option) any later version.
11 * SonarQube is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 package org.sonar.server.benchmark;
23 import com.google.common.collect.Iterables;
24 import com.google.common.collect.Iterators;
25 import com.google.common.collect.Lists;
26 import com.google.common.collect.Maps;
27 import org.apache.commons.io.FileUtils;
28 import org.apache.commons.lang.math.RandomUtils;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33 import org.sonar.api.issue.Issue;
34 import org.sonar.api.rule.Severity;
35 import org.sonar.api.utils.internal.Uuids;
36 import org.sonar.server.es.EsClient;
37 import org.sonar.server.es.SearchOptions;
38 import org.sonar.server.es.SearchResult;
39 import org.sonar.server.issue.IssueQuery;
40 import org.sonar.server.issue.index.IssueAuthorizationDao;
41 import org.sonar.server.issue.index.IssueAuthorizationIndexer;
42 import org.sonar.server.issue.index.IssueDoc;
43 import org.sonar.server.issue.index.IssueIndex;
44 import org.sonar.server.issue.index.IssueIndexer;
45 import org.sonar.server.tester.ServerTester;
46 import org.sonar.server.user.MockUserSession;
48 import java.util.Arrays;
49 import java.util.Date;
50 import java.util.Iterator;
51 import java.util.List;
52 import java.util.Timer;
53 import java.util.concurrent.atomic.AtomicLong;
55 public class IssueIndexBenchmarkTest {
57 private static final Logger LOGGER = LoggerFactory.getLogger("benchmarkIssues");
59 final static int PROJECTS = 100;
60 final static int FILES_PER_PROJECT = 100;
61 final static int ISSUES_PER_FILE = 100;
64 public ServerTester tester = new ServerTester();
67 public Benchmark benchmark = new Benchmark();
70 public void benchmark() throws Exception {
71 // initialization - feed issues/issueAuthorization with projects and hardcoded users
72 indexAuthorizations();
75 benchmarkIssueIndexing();
77 // execute some queries
81 private void indexAuthorizations() {
82 LOGGER.info("Indexing issue authorizations");
83 IssueAuthorizationIndexer indexer = tester.get(IssueAuthorizationIndexer.class);
84 List<IssueAuthorizationDao.Dto> authorizations = Lists.newArrayList();
85 for (int i = 0; i < PROJECTS; i++) {
86 IssueAuthorizationDao.Dto authorization = new IssueAuthorizationDao.Dto("PROJECT" + i, System.currentTimeMillis());
87 authorization.addGroup("sonar-users");
88 authorization.addUser("admin");
89 authorizations.add(authorization);
91 long start = System.currentTimeMillis();
92 indexer.index(authorizations);
93 long period = System.currentTimeMillis() - start;
94 long throughputPerSecond = 1000L * PROJECTS / period;
95 LOGGER.info(String.format("%d authorizations indexed in %d ms (%d docs/second)", PROJECTS, period, throughputPerSecond));
97 // big range as absolute value is quite slow
98 benchmark.expectBetween("Time to index issue authorizations", period, 10L, 500L);
101 private void benchmarkIssueIndexing() {
102 LOGGER.info("Indexing issues");
103 IssueIterator issues = new IssueIterator(PROJECTS, FILES_PER_PROJECT, ISSUES_PER_FILE);
104 ProgressTask progressTask = new ProgressTask(LOGGER, "issues", issues.count());
105 Timer timer = new Timer("IssuesIndex");
106 timer.schedule(progressTask, ProgressTask.PERIOD_MS, ProgressTask.PERIOD_MS);
108 long start = System.currentTimeMillis();
109 tester.get(IssueIndexer.class).index(issues);
112 long period = System.currentTimeMillis() - start;
113 long throughputPerSecond = 1000 * issues.count.get() / period;
114 LOGGER.info(String.format("%d issues indexed in %d ms (%d docs/second)", issues.count.get(), period, throughputPerSecond));
115 benchmark.expectBetween("Throughput to index issues", throughputPerSecond, 3000, 3400);
117 // be sure that physical files do not evolve during estimation of size
118 tester.get(EsClient.class).prepareOptimize("issues").get();
119 long dirSize = FileUtils.sizeOfDirectory(tester.getEsServerHolder().getHomeDir());
120 LOGGER.info(String.format("ES dir: " + FileUtils.byteCountToDisplaySize(dirSize)));
121 benchmark.expectBetween("ES dir size (b)", dirSize, 200L * FileUtils.ONE_MB, 420L * FileUtils.ONE_MB);
124 private void benchmarkQueries() {
125 MockUserSession.set().setUserGroups("sonar-users");
126 benchmarkQuery("all issues", IssueQuery.builder().build());
127 benchmarkQuery("project issues", IssueQuery.builder().projectUuids(Arrays.asList("PROJECT33")).build());
128 benchmarkQuery("file issues", IssueQuery.builder().componentUuids(Arrays.asList("FILE333")).build());
129 benchmarkQuery("various", IssueQuery.builder()
130 .resolutions(Arrays.asList(Issue.RESOLUTION_FIXED))
137 private void benchmarkQuery(String label, IssueQuery query) {
138 IssueIndex index = tester.get(IssueIndex.class);
139 for (int i = 0; i < 10; i++) {
140 long start = System.currentTimeMillis();
141 SearchResult<IssueDoc> result = index.search(query, new SearchOptions());
142 long end = System.currentTimeMillis();
143 LOGGER.info("Request (" + label + "): {} docs in {} ms", result.getTotal(), end - start);
147 private static class IssueIterator implements Iterator<IssueDoc> {
148 private final int nbProjects, nbFilesPerProject, nbIssuesPerFile;
149 private int currentProject = 0, currentFile = 0;
150 private AtomicLong count = new AtomicLong(0L);
151 private final Iterator<String> users = cycleIterator("guy", 200);
152 private Iterator<String> rules = cycleIterator("squid:rule", 1000);
153 private final Iterator<String> severities = Iterables.cycle(Severity.ALL).iterator();
154 private final Iterator<String> statuses = Iterables.cycle(Issue.STATUSES).iterator();
155 private final Iterator<String> resolutions = Iterables.cycle(Issue.RESOLUTIONS).iterator();
157 IssueIterator(int nbProjects, int nbFilesPerProject, int nbIssuesPerFile) {
158 this.nbProjects = nbProjects;
159 this.nbFilesPerProject = nbFilesPerProject;
160 this.nbIssuesPerFile = nbIssuesPerFile;
163 public AtomicLong count() {
168 public boolean hasNext() {
169 return count.get() < nbProjects * nbFilesPerProject * nbIssuesPerFile;
173 public IssueDoc next() {
174 IssueDoc issue = new IssueDoc(Maps.<String, Object>newHashMap());
175 issue.setKey(Uuids.create());
176 issue.setFilePath("src/main/java/Foo" + currentFile);
177 issue.setComponentUuid("FILE" + currentFile);
178 issue.setProjectUuid("PROJECT" + currentProject);
179 issue.setActionPlanKey("PLAN" + currentProject);
180 issue.setAssignee(users.next());
181 issue.setAuthorLogin(users.next());
182 issue.setLine(RandomUtils.nextInt());
183 issue.setTechnicalUpdateDate(new Date());
184 issue.setFuncUpdateDate(new Date());
185 issue.setFuncCreationDate(new Date());
186 issue.setFuncCloseDate(null);
187 issue.setAttributes(null);
188 issue.setDebt(1000L);
189 issue.setEffortToFix(3.14);
190 issue.setLanguage("php");
191 issue.setReporter(users.next());
192 issue.setRuleKey(rules.next());
193 issue.setResolution(resolutions.next());
194 issue.setStatus(statuses.next());
195 issue.setSeverity(severities.next());
196 issue.setMessage(RandomUtils.nextLong() + "this is the message. Not too short.");
197 count.incrementAndGet();
198 if (count.get() % nbIssuesPerFile == 0) {
201 if (count.get() % (nbFilesPerProject * nbIssuesPerFile) == 0) {
209 public void remove() {
210 throw new UnsupportedOperationException();
214 private static Iterator<String> cycleIterator(String prefix, int size) {
215 List<String> values = Lists.newArrayList();
216 for (int i = 0; i < size; i++) {
217 values.add(String.format("%s%d", prefix, i));
219 return Iterators.cycle(values);