]> source.dussan.org Git - sonarqube.git/blob
616a256f0ed0e8679c8520c49571e9e1244bd25b
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2016 SonarSource SA
4  * mailto:contact AT sonarsource DOT com
5  *
6  * This program 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.
10  *
11  * This program 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.
15  *
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.
19  */
20 package org.sonar.server.benchmark;
21
22 import com.google.common.collect.Iterables;
23 import com.google.common.collect.Iterators;
24 import com.google.common.collect.Lists;
25 import com.google.common.collect.Maps;
26 import java.util.Arrays;
27 import java.util.Date;
28 import java.util.Iterator;
29 import java.util.List;
30 import java.util.Timer;
31 import java.util.concurrent.atomic.AtomicLong;
32 import org.apache.commons.io.FileUtils;
33 import org.apache.commons.lang.math.RandomUtils;
34 import org.junit.ClassRule;
35 import org.junit.Rule;
36 import org.junit.Test;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39 import org.sonar.api.issue.Issue;
40 import org.sonar.api.rule.Severity;
41 import org.sonar.core.util.Uuids;
42 import org.sonar.server.es.EsClient;
43 import org.sonar.server.es.SearchOptions;
44 import org.sonar.server.es.SearchResult;
45 import org.sonar.server.issue.IssueQuery;
46 import org.sonar.server.issue.index.IssueAuthorizationDao;
47 import org.sonar.server.issue.index.IssueAuthorizationIndexer;
48 import org.sonar.server.issue.index.IssueDoc;
49 import org.sonar.server.issue.index.IssueIndex;
50 import org.sonar.server.issue.index.IssueIndexer;
51 import org.sonar.server.tester.ServerTester;
52 import org.sonar.server.tester.UserSessionRule;
53
54 public class IssueIndexBenchmarkTest {
55
56   private static final Logger LOGGER = LoggerFactory.getLogger("benchmarkIssues");
57
58   final static int PROJECTS = 100;
59   final static int FILES_PER_PROJECT = 100;
60   final static int ISSUES_PER_FILE = 100;
61
62   @ClassRule
63   public static ServerTester tester = new ServerTester().withEsIndexes();
64   @Rule
65   public UserSessionRule userSessionRule = UserSessionRule.forServerTester(tester);
66
67   @Rule
68   public Benchmark benchmark = new Benchmark();
69
70   @Test
71   public void benchmark() {
72     // initialization - feed issues/issueAuthorization with projects and hardcoded users
73     indexAuthorizations();
74
75     // index issues
76     benchmarkIssueIndexing();
77
78     // execute some queries
79     benchmarkQueries();
80   }
81
82   private void indexAuthorizations() {
83     LOGGER.info("Indexing issue authorizations");
84     IssueAuthorizationIndexer indexer = tester.get(IssueAuthorizationIndexer.class);
85     List<IssueAuthorizationDao.Dto> authorizations = Lists.newArrayList();
86     for (int i = 0; i < PROJECTS; i++) {
87       IssueAuthorizationDao.Dto authorization = new IssueAuthorizationDao.Dto("PROJECT" + i, System.currentTimeMillis());
88       authorization.addGroup("sonar-users");
89       authorization.addUser("admin");
90       authorizations.add(authorization);
91     }
92     long start = System.currentTimeMillis();
93     indexer.index(authorizations);
94     long period = System.currentTimeMillis() - start;
95     long throughputPerSecond = 1000L * PROJECTS / period;
96     LOGGER.info(String.format("%d authorizations indexed in %d ms (%d docs/second)", PROJECTS, period, throughputPerSecond));
97
98     // big range as absolute value is quite slow
99     benchmark.expectBetween("Time to index issue authorizations", period, 10L, 500L);
100   }
101
102   private void benchmarkIssueIndexing() {
103     LOGGER.info("Indexing issues");
104     IssueIterator issues = new IssueIterator(PROJECTS, FILES_PER_PROJECT, ISSUES_PER_FILE);
105     ProgressTask progressTask = new ProgressTask(LOGGER, "issues", issues.count());
106     Timer timer = new Timer("IssuesIndex");
107     timer.schedule(progressTask, ProgressTask.PERIOD_MS, ProgressTask.PERIOD_MS);
108
109     long start = System.currentTimeMillis();
110     tester.get(IssueIndexer.class).index(issues);
111
112     timer.cancel();
113     long period = System.currentTimeMillis() - start;
114     long throughputPerSecond = 1000 * issues.count.get() / period;
115     LOGGER.info(String.format("%d issues indexed in %d ms (%d docs/second)", issues.count.get(), period, throughputPerSecond));
116     benchmark.expectAround("Throughput to index issues", throughputPerSecond, 6500, Benchmark.DEFAULT_ERROR_MARGIN_PERCENTS);
117
118     // be sure that physical files do not evolve during estimation of size
119     tester.get(EsClient.class).prepareOptimize("issues").get();
120     long dirSize = FileUtils.sizeOfDirectory(tester.getEsServerHolder().getHomeDir());
121     LOGGER.info(String.format("ES dir: " + FileUtils.byteCountToDisplaySize(dirSize)));
122     benchmark.expectBetween("ES dir size (b)", dirSize, 200L * FileUtils.ONE_MB, 420L * FileUtils.ONE_MB);
123   }
124
125   private void benchmarkQueries() {
126     userSessionRule.setUserGroups("sonar-users");
127     benchmarkQuery("all issues", IssueQuery.builder(userSessionRule).build());
128     benchmarkQuery("project issues", IssueQuery.builder(userSessionRule).projectUuids(Arrays.asList("PROJECT33")).build());
129     benchmarkQuery("file issues", IssueQuery.builder(userSessionRule).componentUuids(Arrays.asList("FILE333")).build());
130     benchmarkQuery("various", IssueQuery.builder(userSessionRule)
131       .resolutions(Arrays.asList(Issue.RESOLUTION_FIXED))
132       .assigned(true)
133       .build());
134     // TODO test facets
135     // TODO assertions
136   }
137
138   private void benchmarkQuery(String label, IssueQuery query) {
139     IssueIndex index = tester.get(IssueIndex.class);
140     for (int i = 0; i < 10; i++) {
141       long start = System.currentTimeMillis();
142       SearchResult<IssueDoc> result = index.search(query, new SearchOptions());
143       long end = System.currentTimeMillis();
144       LOGGER.info("Request (" + label + "): {} docs in {} ms", result.getTotal(), end - start);
145     }
146   }
147
148   private static class IssueIterator implements Iterator<IssueDoc> {
149     private final int nbProjects;
150     private final int nbFilesPerProject;
151     private final int nbIssuesPerFile;
152     private int currentProject = 0;
153     private int currentFile = 0;
154     private AtomicLong count = new AtomicLong(0L);
155     private final Iterator<String> users = cycleIterator("guy", 200);
156     private Iterator<String> rules = cycleIterator("squid:rule", 1000);
157     private final Iterator<String> severities = Iterables.cycle(Severity.ALL).iterator();
158     private final Iterator<String> statuses = Iterables.cycle(Issue.STATUSES).iterator();
159     private final Iterator<String> resolutions = Iterables.cycle(Issue.RESOLUTIONS).iterator();
160
161     IssueIterator(int nbProjects, int nbFilesPerProject, int nbIssuesPerFile) {
162       this.nbProjects = nbProjects;
163       this.nbFilesPerProject = nbFilesPerProject;
164       this.nbIssuesPerFile = nbIssuesPerFile;
165     }
166
167     public AtomicLong count() {
168       return count;
169     }
170
171     @Override
172     public boolean hasNext() {
173       return count.get() < nbProjects * nbFilesPerProject * nbIssuesPerFile;
174     }
175
176     @Override
177     public IssueDoc next() {
178       IssueDoc issue = new IssueDoc(Maps.<String, Object>newHashMap());
179       issue.setKey(Uuids.create());
180       issue.setFilePath("src/main/java/Foo" + currentFile);
181       issue.setComponentUuid("FILE" + currentFile);
182       issue.setProjectUuid("PROJECT" + currentProject);
183       issue.setAssignee(users.next());
184       issue.setAuthorLogin(users.next());
185       issue.setLine(RandomUtils.nextInt());
186       issue.setTechnicalUpdateDate(new Date());
187       issue.setFuncUpdateDate(new Date());
188       issue.setFuncCreationDate(new Date());
189       issue.setFuncCloseDate(null);
190       issue.setAttributes(null);
191       issue.setEffort(1000L);
192       issue.setGap(3.14);
193       issue.setLanguage("php");
194       issue.setReporter(users.next());
195       issue.setRuleKey(rules.next());
196       issue.setResolution(resolutions.next());
197       issue.setStatus(statuses.next());
198       issue.setSeverity(severities.next());
199       issue.setMessage(RandomUtils.nextLong() + "this is the message. Not too short.");
200       count.incrementAndGet();
201       if (count.get() % nbIssuesPerFile == 0) {
202         currentFile++;
203       }
204       if (count.get() % (nbFilesPerProject * nbIssuesPerFile) == 0) {
205         currentProject++;
206       }
207
208       return issue;
209     }
210
211     @Override
212     public void remove() {
213       throw new UnsupportedOperationException();
214     }
215   }
216
217   private static Iterator<String> cycleIterator(String prefix, int size) {
218     List<String> values = Lists.newArrayList();
219     for (int i = 0; i < size; i++) {
220       values.add(String.format("%s%d", prefix, i));
221     }
222     return Iterators.cycle(values);
223   }
224 }