Skip to content

Commit d0c4c47

Browse files
authored
improve suggester throughput (#4539)
- eliminate the locking done at Suggester level - use single thread pool for init/rebuild - add tunable for the search pool parallelism fixes #4538 fixes #3347
1 parent 500e28c commit d0c4c47

File tree

6 files changed

+237
-170
lines changed

6 files changed

+237
-170
lines changed

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
/*
21-
* Copyright (c) 2018, 2021, Oracle and/or its affiliates. All rights reserved.
21+
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
2222
* Portions Copyright (c) 2019, Chris Fraire <cfraire@me.com>.
2323
*/
2424
package org.opengrok.indexer.configuration;
@@ -51,6 +51,7 @@ public class SuggesterConfig {
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
5353
public static final int REBUILD_THREAD_POOL_PERCENT_NCPUS_DEFAULT = 80;
54+
public static final int SEARCH_THREAD_POOL_PERCENT_NCPUS_DEFAULT = 90;
5455

5556
private static final Set<String> allowedProjectsDefault = null;
5657
private static final Set<String> allowedFieldsDefault = Set.of(
@@ -141,6 +142,11 @@ public class SuggesterConfig {
141142
*/
142143
private int rebuildThreadPoolSizeInNcpuPercent;
143144

145+
/**
146+
* Number of threads used for search pool expressed in percent of available CPUs in the system.
147+
*/
148+
private int searchThreadPoolSizeInNcpuPercent;
149+
144150
public SuggesterConfig() {
145151
setEnabled(ENABLED_DEFAULT);
146152
setMaxResults(MAX_RESULTS_DEFAULT);
@@ -157,6 +163,7 @@ public SuggesterConfig() {
157163
setRebuildCronConfig(REBUILD_CRON_CONFIG_DEFAULT);
158164
setBuildTerminationTime(BUILD_TERMINATION_TIME_DEFAULT);
159165
setRebuildThreadPoolSizeInNcpuPercent(REBUILD_THREAD_POOL_PERCENT_NCPUS_DEFAULT);
166+
setSearchThreadPoolSizeInNcpuPercent(SEARCH_THREAD_POOL_PERCENT_NCPUS_DEFAULT);
160167
}
161168

162169
public boolean isEnabled() {
@@ -302,6 +309,17 @@ public int getRebuildThreadPoolSizeInNcpuPercent() {
302309
return rebuildThreadPoolSizeInNcpuPercent;
303310
}
304311

312+
public void setSearchThreadPoolSizeInNcpuPercent(final int percent) {
313+
if (percent < 0 || percent > 100) {
314+
throw new IllegalArgumentException("Need percentage value");
315+
}
316+
this.searchThreadPoolSizeInNcpuPercent = percent;
317+
}
318+
319+
public int getSearchThreadPoolSizeInNcpuPercent() {
320+
return searchThreadPoolSizeInNcpuPercent;
321+
}
322+
305323
@Override
306324
public boolean equals(Object o) {
307325
if (this == o) {
@@ -355,6 +373,8 @@ static SuggesterConfig getForHelp() {
355373
res.setRebuildCronConfig("1 0 * * *");
356374
res.setBuildTerminationTime(1 + res.getBuildTerminationTime());
357375
res.setRebuildThreadPoolSizeInNcpuPercent(1 + res.getRebuildThreadPoolSizeInNcpuPercent());
376+
res.setSearchThreadPoolSizeInNcpuPercent(1 + res.getSearchThreadPoolSizeInNcpuPercent());
377+
358378
return res;
359379
}
360380

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

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
/*
21-
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
21+
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
2222
*/
2323
package org.opengrok.web.api.v1.suggester.provider.service.impl;
2424

@@ -30,6 +30,7 @@
3030
import org.apache.lucene.index.Term;
3131
import org.apache.lucene.search.Query;
3232
import org.apache.lucene.util.BytesRef;
33+
import org.jetbrains.annotations.VisibleForTesting;
3334
import org.opengrok.indexer.Metrics;
3435
import org.opengrok.indexer.configuration.OpenGrokThreadFactory;
3536
import org.opengrok.suggest.Suggester;
@@ -193,14 +194,14 @@ public void rebuild() {
193194

194195
/** {@inheritDoc} */
195196
@Override
196-
public void rebuild(final String project) {
197-
Project p = env.getProjects().get(project);
198-
if (p == null) {
197+
public void rebuild(final String projectName) {
198+
Project project = env.getProjects().get(projectName);
199+
if (project == null) {
199200
logger.log(Level.WARNING, "Cannot rebuild suggester because project for name {0} was not found",
200201
project);
201202
return;
202203
}
203-
if (!p.isIndexed()) {
204+
if (!project.isIndexed()) {
204205
logger.log(Level.WARNING, "Cannot rebuild project {0} because it is not indexed yet", project);
205206
return;
206207
}
@@ -210,7 +211,7 @@ public void rebuild(final String project) {
210211
logger.log(Level.FINE, "Cannot rebuild {0} because suggester is not initialized", project);
211212
return;
212213
}
213-
suggester.rebuild(Collections.singleton(getNamedIndexDir(p)));
214+
suggester.rebuild(Collections.singleton(getNamedIndexDir(project)));
214215
} finally {
215216
lock.readLock().unlock();
216217
}
@@ -299,28 +300,32 @@ public List<Entry<BytesRef, Integer>> getPopularityData(
299300
}
300301
}
301302

303+
private int getParallelismLevel(int ncpuPercent, String prefix) {
304+
int paralleismLevel = (int) (((float) ncpuPercent / 100) * Runtime.getRuntime().availableProcessors());
305+
if (paralleismLevel == 0) {
306+
paralleismLevel = 1;
307+
}
308+
logger.log(Level.FINER, "Suggester {0} parallelism level: {1}", new Object[]{prefix, paralleismLevel});
309+
310+
return paralleismLevel;
311+
}
312+
302313
private void initSuggester() {
303314
SuggesterConfig suggesterConfig = env.getSuggesterConfig();
304315
if (!suggesterConfig.isEnabled()) {
305316
logger.log(Level.INFO, "Suggester disabled");
306317
return;
307318
}
308319

309-
File suggesterDir = new File(env.getDataRootPath(), IndexDatabase.SUGGESTER_DIR);
310-
int rebuildParalleismLevel = (int) (((float) suggesterConfig.getRebuildThreadPoolSizeInNcpuPercent() / 100)
311-
* Runtime.getRuntime().availableProcessors());
312-
if (rebuildParalleismLevel == 0) {
313-
rebuildParalleismLevel = 1;
314-
}
315-
logger.log(Level.FINER, "Suggester rebuild parallelism level: {}", rebuildParalleismLevel);
316-
suggester = new Suggester(suggesterDir,
320+
suggester = new Suggester(new File(env.getDataRootPath(), IndexDatabase.SUGGESTER_DIR),
317321
suggesterConfig.getMaxResults(),
318322
Duration.ofSeconds(suggesterConfig.getBuildTerminationTime()),
319323
suggesterConfig.isAllowMostPopular(),
320324
env.isProjectsEnabled(),
321325
suggesterConfig.getAllowedFields(),
322326
suggesterConfig.getTimeThreshold(),
323-
rebuildParalleismLevel,
327+
getParallelismLevel(suggesterConfig.getRebuildThreadPoolSizeInNcpuPercent(), "rebuild"),
328+
getParallelismLevel(suggesterConfig.getSearchThreadPoolSizeInNcpuPercent(), "search"),
324329
Metrics.getRegistry(),
325330
env.isPrintProgress());
326331

@@ -405,10 +410,12 @@ private Duration getTimeToNextRebuild() {
405410
return d.get();
406411
}
407412

413+
@VisibleForTesting
408414
public void waitForRebuild(long timeout, TimeUnit unit) throws InterruptedException {
409415
suggester.waitForRebuild(timeout, unit);
410416
}
411417

418+
@VisibleForTesting
412419
public void waitForInit(long timeout, TimeUnit unit) throws InterruptedException {
413420
suggester.waitForInit(timeout, unit);
414421
}

opengrok-web/src/test/java/org/opengrok/web/api/v1/controller/SuggesterControllerProjectsDisabledTest.java

Lines changed: 3 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
*/
1919

2020
/*
21-
* Copyright (c) 2018, 2022, Oracle and/or its affiliates. All rights reserved.
21+
* Copyright (c) 2018, 2024, Oracle and/or its affiliates. All rights reserved.
2222
* Portions Copyright (c) 2019, 2020, Chris Fraire <cfraire@me.com>.
2323
*/
2424
package org.opengrok.web.api.v1.controller;
@@ -33,7 +33,6 @@
3333
import org.junit.jupiter.api.BeforeAll;
3434
import org.junit.jupiter.api.BeforeEach;
3535
import org.junit.jupiter.api.Test;
36-
import org.opengrok.suggest.Suggester;
3736
import org.opengrok.indexer.configuration.RuntimeEnvironment;
3837
import org.opengrok.indexer.configuration.SuggesterConfig;
3938
import org.opengrok.indexer.index.Indexer;
@@ -43,13 +42,10 @@
4342
import org.opengrok.web.api.v1.suggester.provider.service.impl.SuggesterServiceImpl;
4443

4544
import java.io.File;
46-
import java.lang.reflect.Field;
4745
import java.util.Collections;
48-
import java.util.Map;
4946
import java.util.concurrent.TimeUnit;
5047
import java.util.stream.Collectors;
5148

52-
import static org.awaitility.Awaitility.await;
5349
import static org.hamcrest.MatcherAssert.assertThat;
5450
import static org.hamcrest.Matchers.containsInAnyOrder;
5551

@@ -91,23 +87,12 @@ public static void tearDownClass() {
9187
}
9288

9389
@BeforeEach
94-
void before() {
95-
await().atMost(15, TimeUnit.SECONDS).until(() -> getSuggesterProjectDataSize() == 1);
90+
void before() throws Exception {
91+
SuggesterServiceImpl.getInstance().waitForInit(15, TimeUnit.SECONDS);
9692

9793
env.setSuggesterConfig(new SuggesterConfig());
9894
}
9995

100-
private static int getSuggesterProjectDataSize() throws Exception {
101-
Field f = SuggesterServiceImpl.class.getDeclaredField("suggester");
102-
f.setAccessible(true);
103-
Suggester suggester = (Suggester) f.get(SuggesterServiceImpl.getInstance());
104-
105-
Field f2 = Suggester.class.getDeclaredField("projectData");
106-
f2.setAccessible(true);
107-
108-
return ((Map) f2.get(suggester)).size();
109-
}
110-
11196
@Test
11297
void suggestionsSimpleTest() {
11398
SuggesterControllerTest.Result res = target(SuggesterController.PATH)

0 commit comments

Comments
 (0)