Details
-
Bug
-
Resolution: Fixed
-
Medium
-
4.0, 4.1, 4.2
-
4
-
Description
Here is the QueryVisitor.visit(TerminalClause) visit method:
public QueryFactoryResult visit(final TerminalClause terminalClause) { if (rootClause) { return new QueryFactoryResult(createQuery(terminalClause)); } final Collection<ClauseQueryFactory> clauseQueryFactory = this.queryRegistry.getClauseQueryFactory(queryCreationContext, terminalClause); if (clauseQueryFactory.isEmpty()) { // This time we really mean it! When we are asked to run a query for things that no longer exist we will // run the query the best we can, adding false to the tree for bits that we can not resolve. return QueryFactoryResult.createFalseResult(); } try { if (clauseQueryFactory.size() == 1) { ClauseQueryFactory factory = clauseQueryFactory.iterator().next(); return factory.getQuery(queryCreationContext, terminalClause); } else { BooleanQuery query = new BooleanQuery(); for (ClauseQueryFactory factory : clauseQueryFactory) { query.add(makeQuery(factory.getQuery(queryCreationContext, terminalClause)), BooleanClause.Occur.SHOULD); } return new QueryFactoryResult(query); } } catch (BooleanQuery.TooManyClauses tooManyClauses) { throw new JqlTooComplex(terminalClause); } // This could happen for Factories that use their own QueryVisitors, e.g. SavedFilterCQF catch (JqlTooComplex jqlTooComplex) { throw new JqlTooComplex(terminalClause); } }
This looks pretty simple. Unfortunately the collection returned from this.queryRegistry.getClauseQueryFactory(queryCreationContext, terminalClause) is actually generated via Collections2.filter like this:
public Collection<ClauseHandler> getClauseHandler(final User user, final String jqlClauseName) { return filter(getClauseHandler(jqlClauseName), new PermissionToUse(user)); }
So when we call .isEmpty we find the first element in the list where PermissionToUse returns true. We then call .size() and as such end up iterating across the collection evaluating PermissionToUse to work out the size of the collection (this includes iterating over the first element again). We then can iterate over the collection again evaluating PermissionToUse during the for loop (i.e. the collection returned from filter does not remember its results). This means we can end up evaluating the PermissionToUse (2x + 1) as many times as we need to. This can cause significant overhead:
Don't do this.
Attachments
Issue Links
- is related to
-
JRASERVER-23139 The Default Search Handler does way too many permissions checks which can slow down JQL significantly.
- Closed