Skip to content

Commit a72ebc1

Browse files
author
Vladimir Kotal
committed
make parallelism of suggester rebuild tunable
1 parent ab9e24f commit a72ebc1

File tree

8 files changed

+102
-22
lines changed

8 files changed

+102
-22
lines changed

apiary.apib

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -366,13 +366,17 @@ with a colon. If the type cannot be determined "N/A" is returned.
366366

367367
### rebuilds suggester data for all projects [PUT]
368368

369+
+ Response 204
370+
369371
## Suggester project data rebuild [/suggest/rebuild/{project}]
370372

371373
### rebuilds suggester data for given project [PUT]
372374

373375
+ Parameters
374376
+ project - project name
375377

378+
+ Response 204
379+
376380
## Index searchers refresh [/system/refresh]
377381

378382
### refreshes index searchers for specified project [PUT]

dev/checkstyle/suppressions.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
<suppress checks="ParameterNumber" files="CtagsReader\.java|Definitions\.java|PerlLexHelper\.java|
1515
|JFlexXrefUtils\.java|RubyLexHelper\.java|FileAnalyzerFactory\.java|SearchController\.java|
16-
|Context\.java|HistoryContext\.java|Indexer\.java" />
16+
|Context\.java|HistoryContext\.java|Indexer\.java|Suggester\.java" />
1717

1818
<suppress checks="MethodLength" files="Indexer\.java" />
1919
</suppressions>

opengrok-indexer/src/main/java/org/opengrok/indexer/configuration/SuggesterConfig.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public class SuggesterConfig {
5050
public static final String REBUILD_CRON_CONFIG_DEFAULT = "0 0 * * *"; // every day at midnight
5151
public static final int BUILD_TERMINATION_TIME_DEFAULT = 1800; // half an hour should be enough
5252
public static final int TIME_THRESHOLD_DEFAULT = 2000; // 2 sec
53+
public static final int REBUILD_THREAD_POOL_PERCENT_NCPUS_DEFAULT = 80;
5354

5455
public static final Set<String> allowedProjectsDefault = null;
5556
public static final Set<String> allowedFieldsDefault = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
@@ -135,6 +136,11 @@ public class SuggesterConfig {
135136
*/
136137
private int timeThreshold;
137138

139+
/**
140+
* Number of threads used for rebuild pool expressed in percent of available CPUs in the system.
141+
*/
142+
private int rebuildThreadPoolSizeInNcpuPercent;
143+
138144
public SuggesterConfig() {
139145
setEnabled(ENABLED_DEFAULT);
140146
setMaxResults(MAX_RESULTS_DEFAULT);
@@ -151,6 +157,7 @@ public SuggesterConfig() {
151157
// do not use setter because indexer invocation with --man will fail
152158
rebuildCronConfig = REBUILD_CRON_CONFIG_DEFAULT;
153159
setBuildTerminationTime(BUILD_TERMINATION_TIME_DEFAULT);
160+
setRebuildThreadPoolSizeInNcpuPercent(REBUILD_THREAD_POOL_PERCENT_NCPUS_DEFAULT);
154161
}
155162

156163
public boolean isEnabled() {
@@ -285,6 +292,17 @@ public void setTimeThreshold(final int timeThreshold) {
285292
this.timeThreshold = timeThreshold;
286293
}
287294

295+
public void setRebuildThreadPoolSizeInNcpuPercent(final int percent) {
296+
if (percent < 0 || percent > 100) {
297+
throw new IllegalArgumentException("Need percentage value");
298+
}
299+
this.rebuildThreadPoolSizeInNcpuPercent = percent;
300+
}
301+
302+
public int getRebuildThreadPoolSizeInNcpuPercent() {
303+
return rebuildThreadPoolSizeInNcpuPercent;
304+
}
305+
288306
@Override
289307
public boolean equals(Object o) {
290308
if (this == o) {
@@ -306,14 +324,15 @@ public boolean equals(Object o) {
306324
buildTerminationTime == that.buildTerminationTime &&
307325
Objects.equals(allowedProjects, that.allowedProjects) &&
308326
Objects.equals(allowedFields, that.allowedFields) &&
309-
Objects.equals(rebuildCronConfig, that.rebuildCronConfig);
327+
Objects.equals(rebuildCronConfig, that.rebuildCronConfig) &&
328+
rebuildThreadPoolSizeInNcpuPercent == that.rebuildThreadPoolSizeInNcpuPercent;
310329
}
311330

312331
@Override
313332
public int hashCode() {
314333
return Objects.hash(enabled, maxResults, minChars, allowedProjects, maxProjects, allowedFields,
315334
allowComplexQueries, allowMostPopular, showScores, showProjects, showTime, rebuildCronConfig,
316-
buildTerminationTime);
335+
buildTerminationTime, rebuildThreadPoolSizeInNcpuPercent);
317336
}
318337

319338
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*
2+
* CDDL HEADER START
3+
*
4+
* The contents of this file are subject to the terms of the
5+
* Common Development and Distribution License (the "License").
6+
* You may not use this file except in compliance with the License.
7+
*
8+
* See LICENSE.txt included in this distribution for the specific
9+
* language governing permissions and limitations under the License.
10+
*
11+
* When distributing Covered Code, include this CDDL HEADER in each
12+
* file and include the License file at LICENSE.txt.
13+
* If applicable, add the following below this CDDL HEADER, with the
14+
* fields enclosed by brackets "[]" replaced with your own identifying
15+
* information: Portions Copyright [yyyy] [name of copyright owner]
16+
*
17+
* CDDL HEADER END
18+
*/
19+
20+
/*
21+
* Copyright (c) 2018 Oracle and/or its affiliates. All rights reserved.
22+
*/
23+
24+
package org.opengrok.indexer.configuration;
25+
26+
import org.junit.Test;
27+
28+
import static org.junit.Assert.assertEquals;
29+
30+
public class SuggesterConfigTest {
31+
32+
@Test(expected = IllegalArgumentException.class)
33+
public void testRebuildParallelismNegative() {
34+
SuggesterConfig sconfig = new SuggesterConfig();
35+
sconfig.setRebuildThreadPoolSizeInNcpuPercent(-1);
36+
}
37+
38+
@Test
39+
public void testRebuildParallelismPositive() {
40+
SuggesterConfig sconfig = new SuggesterConfig();
41+
sconfig.setRebuildThreadPoolSizeInNcpuPercent(70);
42+
assertEquals(70, sconfig.getRebuildThreadPoolSizeInNcpuPercent());
43+
}
44+
}

opengrok-web/src/main/java/org/opengrok/web/api/v1/controller/SuggesterController.java

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -168,15 +168,13 @@ public SuggesterConfig getConfig() {
168168
@PUT
169169
@Path("/rebuild")
170170
public void rebuild() {
171-
// TODO run this async in a thread so that the request get a response instantly
172-
suggester.rebuild();
171+
new Thread(() -> suggester.rebuild()).start();
173172
}
174173

175174
@PUT
176175
@Path("/rebuild/{project}")
177176
public void rebuild(@PathParam("project") final String project) {
178-
// TODO run this async in a thread so that the request get a response instantly
179-
suggester.rebuild(project);
177+
new Thread(() -> suggester.rebuild(project)).start();
180178
}
181179

182180
/**

opengrok-web/src/main/java/org/opengrok/web/api/v1/suggester/provider/service/impl/SuggesterServiceImpl.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,13 @@
3535
import org.opengrok.suggest.Suggester.NamedIndexReader;
3636
import org.opengrok.suggest.Suggester.Suggestions;
3737
import org.opengrok.suggest.query.SuggesterQuery;
38-
import org.opengrok.indexer.configuration.Configuration;
3938
import org.opengrok.indexer.configuration.Project;
4039
import org.opengrok.indexer.configuration.RuntimeEnvironment;
4140
import org.opengrok.indexer.configuration.SuggesterConfig;
4241
import org.opengrok.indexer.configuration.SuperIndexSearcher;
4342
import org.opengrok.indexer.index.IndexDatabase;
4443
import org.opengrok.indexer.logger.LoggerFactory;
4544
import org.opengrok.web.api.v1.suggester.provider.service.SuggesterService;
46-
import sun.reflect.generics.reflectiveObjects.NotImplementedException;
4745

4846
import java.io.File;
4947
import java.io.IOException;
@@ -291,13 +289,19 @@ private void initSuggester() {
291289
}
292290

293291
File suggesterDir = new File(env.getDataRootPath(), IndexDatabase.SUGGESTER_DIR);
292+
int rebuildParalleismLevel = (int)(((float)suggesterConfig.getRebuildThreadPoolSizeInNcpuPercent() / 100) * Runtime.getRuntime().availableProcessors());
293+
if (rebuildParalleismLevel == 0) {
294+
rebuildParalleismLevel = 1;
295+
}
296+
logger.log(Level.FINER, "Suggester rebuild parallelism level: " + rebuildParalleismLevel);
294297
suggester = new Suggester(suggesterDir,
295298
suggesterConfig.getMaxResults(),
296299
Duration.ofSeconds(suggesterConfig.getBuildTerminationTime()),
297300
suggesterConfig.isAllowMostPopular(),
298301
env.isProjectsEnabled(),
299302
suggesterConfig.getAllowedFields(),
300-
suggesterConfig.getTimeThreshold());
303+
suggesterConfig.getTimeThreshold(),
304+
rebuildParalleismLevel);
301305

302306
new Thread(() -> {
303307
suggester.init(getAllProjectIndexDirs());

suggester/src/main/java/org/opengrok/suggest/Suggester.java

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,16 @@ public final class Suggester implements Closeable {
8484

8585
private final int timeThreshold;
8686

87+
private final int rebuildParallelismLevel;
88+
8789
// do NOT use fork join thread pool (work stealing thread pool) because it does not send interrupts upon cancellation
8890
private final ExecutorService executorService = Executors.newFixedThreadPool(
89-
Runtime.getRuntime().availableProcessors());
91+
Runtime.getRuntime().availableProcessors(),
92+
runnable -> {
93+
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
94+
thread.setName("suggester-lookup-" + thread.getId());
95+
return thread;
96+
});
9097

9198
/**
9299
* @param suggesterDir directory under which the suggester data should be created
@@ -105,7 +112,8 @@ public Suggester(
105112
final boolean allowMostPopular,
106113
final boolean projectsEnabled,
107114
final Set<String> allowedFields,
108-
final int timeThreshold
115+
final int timeThreshold,
116+
final int rebuildParallelismLevel
109117
) {
110118
if (suggesterDir == null) {
111119
throw new IllegalArgumentException("Suggester needs to have directory specified");
@@ -123,25 +131,26 @@ public Suggester(
123131
this.projectsEnabled = projectsEnabled;
124132
this.allowedFields = new HashSet<>(allowedFields);
125133
this.timeThreshold = timeThreshold;
134+
this.rebuildParallelismLevel = rebuildParallelismLevel;
126135
}
127136

128137
/**
129138
* Initializes suggester data for specified indexes. The data is initialized asynchronously.
130-
* @param luceneIndexes paths to lucene indexes and name with which the index should be associated
139+
* @param luceneIndexes paths to Lucene indexes and name with which the index should be associated
131140
*/
132141
public void init(final Collection<NamedIndexDir> luceneIndexes) {
133142
if (luceneIndexes == null || luceneIndexes.isEmpty()) {
134143
logger.log(Level.INFO, "No index directories found, exiting...");
135144
return;
136145
}
137146
if (!projectsEnabled && luceneIndexes.size() > 1) {
138-
throw new IllegalArgumentException("Projects are not enabled and multiple lucene indexes were passed");
147+
throw new IllegalArgumentException("Projects are not enabled and multiple Lucene indexes were passed");
139148
}
140149

141150
synchronized (lock) {
142151
logger.log(Level.INFO, "Initializing suggester");
143152

144-
ExecutorService executor = Executors.newWorkStealingPool();
153+
ExecutorService executor = Executors.newWorkStealingPool(rebuildParallelismLevel);
145154

146155
for (NamedIndexDir indexDir : luceneIndexes) {
147156
submitInitIfIndexExists(executor, indexDir);
@@ -222,9 +231,9 @@ public void rebuild(final Collection<NamedIndexDir> indexDirs) {
222231
}
223232

224233
synchronized (lock) {
225-
logger.log(Level.INFO, "Rebuilding following suggesters: {0}", indexDirs);
234+
logger.log(Level.INFO, "Rebuilding the following suggesters: {0}", indexDirs);
226235

227-
ExecutorService executor = Executors.newWorkStealingPool();
236+
ExecutorService executor = Executors.newWorkStealingPool(rebuildParallelismLevel);
228237

229238
for (NamedIndexDir indexDir : indexDirs) {
230239
SuggesterProjectData data = this.projectData.get(indexDir.name);

suggester/src/test/java/org/opengrok/suggest/SuggesterTest.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,14 +98,14 @@ private Suggester.NamedIndexReader getNamedIndexReader() throws IOException {
9898

9999
@Test(expected = IllegalArgumentException.class)
100100
public void testNullSuggesterDir() {
101-
new Suggester(null, 10, Duration.ofMinutes(5), false, true, null, Integer.MAX_VALUE);
101+
new Suggester(null, 10, Duration.ofMinutes(5), false, true, null, Integer.MAX_VALUE, 1);
102102
}
103103

104104
@Test(expected = IllegalArgumentException.class)
105105
public void testNullDuration() throws IOException {
106106
Path tempFile = Files.createTempFile("opengrok", "test");
107107
try {
108-
new Suggester(tempFile.toFile(), 10, null, false, true, null, Integer.MAX_VALUE);
108+
new Suggester(tempFile.toFile(), 10, null, false, true, null, Integer.MAX_VALUE, 1);
109109
} finally {
110110
tempFile.toFile().delete();
111111
}
@@ -115,7 +115,7 @@ public void testNullDuration() throws IOException {
115115
public void testNegativeDuration() throws IOException {
116116
Path tempFile = Files.createTempFile("opengrok", "test");
117117
try {
118-
new Suggester(tempFile.toFile(), 10, Duration.ofMinutes(-4), false, true, null, Integer.MAX_VALUE);
118+
new Suggester(tempFile.toFile(), 10, Duration.ofMinutes(-4), false, true, null, Integer.MAX_VALUE, 1);
119119
} finally {
120120
tempFile.toFile().delete();
121121
}
@@ -132,7 +132,7 @@ private SuggesterTestData initSuggester() throws IOException {
132132
Path tempSuggesterDir = Files.createTempDirectory("opengrok");
133133

134134
Suggester s = new Suggester(tempSuggesterDir.toFile(), 10, Duration.ofMinutes(1), true,
135-
true, Collections.singleton("test"), Integer.MAX_VALUE);
135+
true, Collections.singleton("test"), Integer.MAX_VALUE, Runtime.getRuntime().availableProcessors());
136136

137137
s.init(Collections.singleton(new Suggester.NamedIndexDir("test", tempIndexDir)));
138138

@@ -142,6 +142,7 @@ private SuggesterTestData initSuggester() throws IOException {
142142
testData.s = s;
143143
testData.indexDir = tempIndexDir;
144144
testData.suggesterDir = tempSuggesterDir;
145+
145146
return testData;
146147
}
147148

@@ -204,7 +205,8 @@ public void testIndexChangedWhileOffline() throws IOException {
204205
addText(t.getIndexDirectory(), "a1 a2");
205206

206207
t.s = new Suggester(t.suggesterDir.toFile(), 10, Duration.ofMinutes(1), false,
207-
true, Collections.singleton("test"), Integer.MAX_VALUE);
208+
true, Collections.singleton("test"), Integer.MAX_VALUE,
209+
Runtime.getRuntime().availableProcessors());
208210

209211
t.s.init(Collections.singleton(t.getNamedIndexDir()));
210212

0 commit comments

Comments
 (0)