You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2023/04/27 10:55:16 UTC
[james-project] 02/03: [PERF] Optimize IMAP ESEARCH options
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 <bt...@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-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org