This is an automated email from the ASF dual-hosted git repository. btellier pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/james-project.git
commit f812548421384d44f0d2f0a580c249a2db957b30 Author: Benoit Tellier <btell...@linagora.com> AuthorDate: Wed Apr 26 17:34:31 2023 +0700 [PERF] Optimize IMAP ESEARCH options - Compute UID ranges only if needed - Compute the idSet directly from the LongList to work in-place and drop complexity --- .../james/imap/processor/SearchProcessor.java | 69 ++++++++++++++++------ 1 file changed, 50 insertions(+), 19 deletions(-) diff --git a/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java b/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java index 6d506900c3..499047dd94 100644 --- a/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java +++ b/protocols/imap/src/main/java/org/apache/james/imap/processor/SearchProcessor.java @@ -22,6 +22,7 @@ package org.apache.james.imap.processor; import static org.apache.james.mailbox.MessageManager.MailboxMetaData.RecentMode.IGNORE; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; @@ -74,6 +75,8 @@ import com.github.fge.lambdas.Throwing; import com.google.common.collect.ImmutableList; import it.unimi.dsi.fastutil.longs.LongArrayList; +import it.unimi.dsi.fastutil.longs.LongComparators; +import it.unimi.dsi.fastutil.longs.LongConsumer; import it.unimi.dsi.fastutil.longs.LongList; import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; @@ -164,22 +167,8 @@ public class SearchProcessor extends AbstractMailboxProcessor<SearchRequest> imp private ImapResponseMessage handleResultOptions(SearchRequest request, ImapSession session, Collection<MessageUid> uids, ModSeq highestModSeq, LongList ids) { List<SearchResultOption> resultOptions = request.getSearchOperation().getResultOptions(); - List<Long> idList = new ArrayList<>(ids.size()); - for (long id : ids) { - idList.add(id); - } - List<IdRange> idsAsRanges = new ArrayList<>(); - for (Long id: idList) { - idsAsRanges.add(new IdRange(id)); - } - IdRange[] idRanges = IdRange.mergeRanges(idsAsRanges).toArray(IdRange[]::new); - - List<UidRange> uidsAsRanges = new ArrayList<>(); - for (MessageUid uid: uids) { - uidsAsRanges.add(new UidRange(uid)); - } - UidRange[] uidRanges = UidRange.mergeRanges(uidsAsRanges).toArray(UidRange[]::new); + IdRange[] idRanges = asRanges(ids); boolean esearch = false; for (SearchResultOption resultOption : resultOptions) { @@ -194,12 +183,11 @@ public class SearchProcessor extends AbstractMailboxProcessor<SearchRequest> imp long max = -1; long count = ids.size(); - if (ids.size() > 0) { + if (!ids.isEmpty()) { min = ids.getLong(0); max = ids.getLong(ids.size() - 1); } - // Save the sequence-set for later usage. This is part of SEARCHRES if (resultOptions.contains(SearchResultOption.SAVE)) { if (resultOptions.contains(SearchResultOption.ALL) || resultOptions.contains(SearchResultOption.COUNT)) { @@ -207,17 +195,18 @@ public class SearchProcessor extends AbstractMailboxProcessor<SearchRequest> imp SearchResUtil.saveSequenceSet(session, idRanges); } else { List<IdRange> savedRanges = new ArrayList<>(); - if (resultOptions.contains(SearchResultOption.MIN)) { + if (resultOptions.contains(SearchResultOption.MIN) && min > 0) { // Store the MIN savedRanges.add(new IdRange(min)); } - if (resultOptions.contains(SearchResultOption.MAX)) { + if (resultOptions.contains(SearchResultOption.MAX) && max > 0) { // Store the MAX savedRanges.add(new IdRange(max)); } SearchResUtil.saveSequenceSet(session, savedRanges.toArray(IdRange[]::new)); } } + UidRange[] uidRanges = uidRangeIfNeeded(request, resultOptions, uids); return new ESearchResponse(min, max, count, idRanges, uidRanges, highestModSeq, request.getTag(), request.isUseUids(), resultOptions); } else { // Just save the returned sequence-set as this is not SEARCHRES + ESEARCH @@ -226,6 +215,48 @@ public class SearchProcessor extends AbstractMailboxProcessor<SearchRequest> imp } } + private UidRange[] uidRangeIfNeeded(SearchRequest request, List<SearchResultOption> resultOptions, Collection<MessageUid> uids) { + if (resultOptions.contains(SearchResultOption.ALL) && request.isUseUids()) { + List<UidRange> uidsAsRanges = new ArrayList<>(); + for (MessageUid uid: uids) { + uidsAsRanges.add(new UidRange(uid)); + } + return UidRange.mergeRanges(uidsAsRanges).toArray(UidRange[]::new); + } + return null; + } + + /** + * Optimization of IdRange.mergeRanges(idsAsRanges) for list of long + */ + private IdRange[] asRanges(LongList ids) { + ids.sort(LongComparators.NATURAL_COMPARATOR); + List<IdRange> idsAsRanges = new ArrayList<>(); + long lowBound = -1; + long highBound = -1; + for (int i = 0; i < ids.size(); i++) { + long id = ids.getLong(i); + // Initialize + if (lowBound == -1) { + lowBound = id; + highBound = id; + continue; + } + if (id == highBound + 1) { + highBound = id; + continue; + } + idsAsRanges.add(new IdRange(lowBound, highBound)); + lowBound = id; + highBound = id; + } + if (lowBound != -1 && highBound != -1) { + idsAsRanges.add(new IdRange(lowBound, highBound)); + } + IdRange[] result = new IdRange[idsAsRanges.size()]; + return idsAsRanges.toArray(result); + } + private LongList asResults(ImapSession session, boolean useUids, Collection<MessageUid> uids) { LongList result = new LongArrayList(uids.size()); // Avoid using streams here as the overhead for large search responses is massive. --------------------------------------------------------------------- To unsubscribe, e-mail: notifications-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org