]> source.dussan.org Git - sonarqube.git/blob
640cdcf6abdb983a63c14a18b8fdfcaaf1eccb1a
[sonarqube.git] /
1 /*
2  * SonarQube, open source software quality management tool.
3  * Copyright (C) 2008-2014 SonarSource
4  * mailto:contact AT sonarsource DOT com
5  *
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.
10  *
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.
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
21 package org.sonar.server.benchmark;
22
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.issue.IssueQuery;
37 import org.sonar.server.issue.index.IssueAuthorizationDao;
38 import org.sonar.server.issue.index.IssueAuthorizationIndexer;
39 import org.sonar.server.issue.index.IssueDoc;
40 import org.sonar.server.issue.index.IssueIndex;
41 import org.sonar.server.issue.index.IssueIndexer;
42 import org.sonar.server.search.QueryContext;
43 import org.sonar.server.search.Result;
44 import org.sonar.server.tester.ServerTester;
45 import org.sonar.server.user.MockUserSession;
46
47 import java.util.Arrays;
48 import java.util.Date;
49 import java.util.Iterator;
50 import java.util.List;
51 import java.util.Timer;
52 import java.util.concurrent.atomic.AtomicLong;
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   @Rule
63   public ServerTester tester = new ServerTester();
64
65   @Rule
66   public Benchmark benchmark = new Benchmark();
67
68   @Test
69   public void benchmark() throws Exception {
70     // initialization - feed issues/issueAuthorization with projects and hardcoded users
71     indexAuthorizations();
72
73     // index issues
74     benchmarkIssueIndexing();
75
76     // execute some queries
77     benchmarkQueries();
78   }
79
80   private void indexAuthorizations() {
81     LOGGER.info("Indexing issue authorizations");
82     IssueAuthorizationIndexer indexer = tester.get(IssueAuthorizationIndexer.class);
83     List<IssueAuthorizationDao.Dto> authorizations = Lists.newArrayList();
84     for (int i = 0; i < PROJECTS; i++) {
85       IssueAuthorizationDao.Dto authorization = new IssueAuthorizationDao.Dto("PROJECT" + i, System.currentTimeMillis());
86       authorization.addGroup("sonar-users");
87       authorization.addUser("admin");
88       authorizations.add(authorization);
89     }
90     long start = System.currentTimeMillis();
91     indexer.index(authorizations);
92     long period = System.currentTimeMillis() - start;
93     LOGGER.info(String.format("%d authorizations indexed in %d ms (%d docs/second)", PROJECTS, period, 1000 * PROJECTS / period));
94
95     // big range as absolute value is quite slow
96     benchmark.expectBetween("Time to index issue authorizations", period, 200L, 500L);
97   }
98
99   private void benchmarkIssueIndexing() {
100     LOGGER.info("Indexing issues");
101     IssueIterator issues = new IssueIterator(PROJECTS, FILES_PER_PROJECT, ISSUES_PER_FILE);
102     ProgressTask progressTask = new ProgressTask(LOGGER, "issues", issues.count());
103     Timer timer = new Timer("IssuesIndex");
104     timer.schedule(progressTask, ProgressTask.PERIOD_MS, ProgressTask.PERIOD_MS);
105
106     long start = System.currentTimeMillis();
107     tester.get(IssueIndexer.class).index(issues);
108
109     timer.cancel();
110     long period = System.currentTimeMillis() - start;
111     LOGGER.info(String.format("%d issues indexed in %d ms (%d docs/second)", issues.count.get(), period, 1000 * issues.count.get() / period));
112     benchmark.expectBetween("Time to index issues", period, 350000L, 430000L);
113
114     long dirSize = FileUtils.sizeOfDirectory(tester.getEsServerHolder().getHomeDir());
115     LOGGER.info(String.format("ES dir: " + FileUtils.byteCountToDisplaySize(dirSize)));
116     benchmark.expectBetween("ES dir size (b)", dirSize, 385L * FileUtils.ONE_MB, 400L * FileUtils.ONE_MB);
117   }
118
119   private void benchmarkQueries() {
120     MockUserSession.set().setUserGroups("sonar-users");
121     benchmarkQuery("all issues", IssueQuery.builder().build());
122     benchmarkQuery("project issues", IssueQuery.builder().projectUuids(Arrays.asList("PROJECT33")).build());
123     benchmarkQuery("file issues", IssueQuery.builder().componentUuids(Arrays.asList("FILE333")).build());
124     benchmarkQuery("various", IssueQuery.builder()
125       .resolutions(Arrays.asList(Issue.RESOLUTION_FIXED))
126       .assigned(true)
127       .build());
128     // TODO test facets
129   }
130
131   private void benchmarkQuery(String label, IssueQuery query) {
132     IssueIndex index = tester.get(IssueIndex.class);
133     for (int i = 0; i < 10; i++) {
134       long start = System.currentTimeMillis();
135       Result<Issue> result = index.search(query, new QueryContext());
136       long end = System.currentTimeMillis();
137       LOGGER.info("Request (" + label + "): {} docs in {} ms", result.getTotal(), end - start);
138     }
139   }
140
141   private static class IssueIterator implements Iterator<IssueDoc> {
142     private final int nbProjects, nbFilesPerProject, nbIssuesPerFile;
143     private int currentProject = 0, currentFile = 0;
144     private AtomicLong count = new AtomicLong(0L);
145     private final Iterator<String> users = cycleIterator("guy", 200);
146     private Iterator<String> rules = cycleIterator("squid:rule", 1000);
147     private final Iterator<String> severities = Iterables.cycle(Severity.ALL).iterator();
148     private final Iterator<String> statuses = Iterables.cycle(Issue.STATUSES).iterator();
149     private final Iterator<String> resolutions = Iterables.cycle(Issue.RESOLUTIONS).iterator();
150
151     IssueIterator(int nbProjects, int nbFilesPerProject, int nbIssuesPerFile) {
152       this.nbProjects = nbProjects;
153       this.nbFilesPerProject = nbFilesPerProject;
154       this.nbIssuesPerFile = nbIssuesPerFile;
155     }
156
157     public AtomicLong count() {
158       return count;
159     }
160
161     @Override
162     public boolean hasNext() {
163       return count.get() < nbProjects * nbFilesPerProject * nbIssuesPerFile;
164     }
165
166     @Override
167     public IssueDoc next() {
168       IssueDoc issue = new IssueDoc(Maps.<String, Object>newHashMap());
169       issue.setKey(Uuids.create());
170       issue.setFilePath("src/main/java/Foo" + currentFile);
171       issue.setComponentUuid("FILE" + currentFile);
172       issue.setProjectUuid("PROJECT" + currentProject);
173       issue.setActionPlanKey("PLAN" + currentProject);
174       issue.setAssignee(users.next());
175       issue.setAuthorLogin(users.next());
176       issue.setLine(RandomUtils.nextInt());
177       issue.setTechnicalCreationDate(new Date());
178       issue.setTechnicalUpdateDate(new Date());
179       issue.setFuncUpdateDate(new Date());
180       issue.setFuncCreationDate(new Date());
181       issue.setFuncCloseDate(null);
182       issue.setAttributes(null);
183       issue.setDebt(1000L);
184       issue.setEffortToFix(3.14);
185       issue.setLanguage("php");
186       issue.setReporter(users.next());
187       issue.setRuleKey(rules.next());
188       issue.setResolution(resolutions.next());
189       issue.setStatus(statuses.next());
190       issue.setSeverity(severities.next());
191       issue.setMessage(RandomUtils.nextLong() + "this is the message. Not too short.");
192       count.incrementAndGet();
193       if (count.get() % nbIssuesPerFile == 0) {
194         currentFile++;
195       }
196       if (count.get() % (nbFilesPerProject * nbIssuesPerFile) == 0) {
197         currentProject++;
198       }
199
200       return issue;
201     }
202
203     @Override
204     public void remove() {
205       throw new UnsupportedOperationException();
206     }
207   }
208
209   private static Iterator<String> cycleIterator(String prefix, int size) {
210     List<String> values = Lists.newArrayList();
211     for (int i = 0; i < size; i++) {
212       values.add(String.format("%s%d", prefix, i));
213     }
214     return Iterators.cycle(values);
215   }
216 }