diff --git a/src/main/java/org/olat/search/SearchModule.java b/src/main/java/org/olat/search/SearchModule.java index 02fe8febf05422309299fc8b8e0207bee2f28f63..741b43303e477a4a7dabf7b73514a254534d3ba6 100644 --- a/src/main/java/org/olat/search/SearchModule.java +++ b/src/main/java/org/olat/search/SearchModule.java @@ -97,6 +97,8 @@ public class SearchModule extends AbstractSpringModule { private int maxHits = 1000; private int maxResults = 100; + @Value("${search.timeout:15}") + private int searchTimeout; @Value("${search.folder.pool.size:3}") private int folderPoolSize; @Value("${restart.window.start}") @@ -267,6 +269,10 @@ public class SearchModule extends AbstractSpringModule { public long getIndexInterval() { return indexInterval; } + + public int getSearchTimeout() { + return searchTimeout; + } /** * @return Number of maximal hits before filtering of results for a certain search-query. diff --git a/src/main/java/org/olat/search/service/SearchCallable.java b/src/main/java/org/olat/search/service/SearchCallable.java index a3bab180741234707b138c05c5fc4610d9fb80a7..0cafbf58728971f84510f7ea568e7628084230bc 100644 --- a/src/main/java/org/olat/search/service/SearchCallable.java +++ b/src/main/java/org/olat/search/service/SearchCallable.java @@ -76,12 +76,16 @@ class SearchCallable implements Callable<SearchResults> { log.warn("Index does not exist, can't search for queryString: "+queryString); throw new ServiceNotAvailableException("Index does not exist"); } - + if(debug) log.debug("queryString=" + queryString); searcher = searchService.getIndexSearcher(); BooleanQuery query = searchService.createQuery(queryString, condQueries); if(debug) log.debug("query=" + query); + if(Thread.interrupted()) { + throw new InterruptedException(); + } + long startTime = System.currentTimeMillis(); int n = SearchServiceFactory.getService().getSearchModuleConfig().getMaxHits(); diff --git a/src/main/java/org/olat/search/service/SearchServiceImpl.java b/src/main/java/org/olat/search/service/SearchServiceImpl.java index 42f17e1aa211bfec16053e06d63604e2eafa67ea..76f6b0b8bd34af7b416be076ac2529c820194593 100644 --- a/src/main/java/org/olat/search/service/SearchServiceImpl.java +++ b/src/main/java/org/olat/search/service/SearchServiceImpl.java @@ -238,6 +238,7 @@ public class SearchServiceImpl implements SearchService, GenericEventListener { searchSpellChecker.setSpellDictionaryPath(searchModuleConfig.getSpellCheckDictionaryPath()); searchSpellChecker.setSpellCheckEnabled(searchModuleConfig.getSpellCheckEnabled()); searchSpellChecker.setSearchExecutor(searchExecutor); + searchSpellChecker.setSearchModule(searchModuleConfig); indexer = new Index(searchModuleConfig, this, searchSpellChecker, mainIndexer, lifeIndexer, coordinatorManager); @@ -301,14 +302,19 @@ public class SearchServiceImpl implements SearchService, GenericEventListener { public SearchResults doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles, int firstResult, int maxResults, boolean doHighlighting) throws ServiceNotAvailableException, ParseException { - + + Future<SearchResults> futureResults = null; try { SearchCallable run = new SearchCallable(queryString, condQueries, identity, roles, firstResult, maxResults, doHighlighting, this); - Future<SearchResults> futureResults = searchExecutor.submit(run); - SearchResults results = futureResults.get(30, TimeUnit.SECONDS); + futureResults = searchExecutor.submit(run); + SearchResults results = futureResults.get(searchModuleConfig.getSearchTimeout(), TimeUnit.SECONDS); queryCount++; return results; - } catch (InterruptedException | TimeoutException e) { + } catch (InterruptedException e) { + log.error("", e); + return null; + } catch (TimeoutException e) { + cancelSearch(futureResults); log.error("", e); return null; } catch (ExecutionException e) { @@ -327,15 +333,21 @@ public class SearchServiceImpl implements SearchService, GenericEventListener { public List<Long> doSearch(String queryString, List<String> condQueries, Identity identity, Roles roles, int firstResult, int maxResults, SortKey... orderBy) throws ServiceNotAvailableException, ParseException, QueryException { + + Future<List<Long>> futureResults = null; try { SearchOrderByCallable run = new SearchOrderByCallable(queryString, condQueries, orderBy, firstResult, maxResults, this); - Future<List<Long>> futureResults = searchExecutor.submit(run); - List<Long> results = futureResults.get(30, TimeUnit.SECONDS); + futureResults = searchExecutor.submit(run); + List<Long> results = futureResults.get(searchModuleConfig.getSearchTimeout(), TimeUnit.SECONDS); queryCount++; if(results == null) { results = new ArrayList<Long>(1); } return results; + } catch (TimeoutException e) { + cancelSearch(futureResults); + log.error("", e); + return null; } catch (Exception e) { log.error("", e); return new ArrayList<Long>(1); @@ -345,16 +357,31 @@ public class SearchServiceImpl implements SearchService, GenericEventListener { @Override public Document doSearch(String queryString) throws ServiceNotAvailableException, ParseException, QueryException { + Future<Document> futureResults = null; try { GetDocumentByCallable run = new GetDocumentByCallable(queryString, this); - Future<Document> futureResults = searchExecutor.submit(run); - return futureResults.get(); + futureResults = searchExecutor.submit(run); + return futureResults.get(searchModuleConfig.getSearchTimeout(), TimeUnit.SECONDS); + } catch (TimeoutException e) { + cancelSearch(futureResults); + log.error("", e); + return null; } catch (Exception e) { log.error("", e); return null; } } + private void cancelSearch(Future<?> search) { + if(search != null) { + try { + search.cancel(true); + } catch (Exception e) { + log.error("Error canceling a search", e); + } + } + } + protected BooleanQuery createQuery(String queryString, List<String> condQueries) throws ParseException { BooleanQuery query = new BooleanQuery(); diff --git a/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java b/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java index ade28f3ce892f6292be03f10db3e67268b01f64e..d322b14daac14d9674b568eaa625a8f034b2abb8 100644 --- a/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java +++ b/src/main/java/org/olat/search/service/spell/SearchSpellChecker.java @@ -31,6 +31,8 @@ import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; @@ -46,6 +48,7 @@ import org.apache.lucene.store.FSDirectory; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.util.FileUtils; +import org.olat.search.SearchModule; import org.olat.search.SearchService; import org.olat.search.model.OlatDocument; @@ -67,6 +70,7 @@ public class SearchSpellChecker { private SpellChecker spellChecker; private boolean isSpellCheckEnabled = true; private ExecutorService searchExecutor; + private SearchModule searchModule; public SearchSpellChecker() { // @@ -76,22 +80,41 @@ public class SearchSpellChecker { this.searchExecutor = searchExecutor; } + public void setSearchModule(SearchModule searchModule) { + this.searchModule = searchModule; + } + /** * Check for valid similar search terms * @param query * @return Returns list of String with similar search-words. * Returns null when spell-checker is disabled or has an exception. */ - public Set<String> check(String query) { - try { - CheckCallable run = new CheckCallable(query, this); - Future<Set<String>> futureResults = searchExecutor.submit(run); - return futureResults.get(); + public Set<String> check(String query) { + Future<Set<String>> futureResults = null; + try { + CheckCallable run = new CheckCallable(query, this); + futureResults = searchExecutor.submit(run); + return futureResults.get(searchModule.getSearchTimeout(), TimeUnit.SECONDS); + } catch (TimeoutException e) { + cancelSearch(futureResults); + log.error("", e); + return null; } catch (Exception e) { log.warn("Can not spell check",e); return new HashSet<String>(); } - } + } + + private void cancelSearch(Future<?> search) { + if(search != null) { + try { + search.cancel(false); + } catch (Exception e) { + log.error("Error canceling a search", e); + } + } + } protected SpellChecker getSpellChecker() { if(spellChecker==null) { //lazy initialization