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


The following commit(s) were added to refs/heads/master by this push:
     new 4f95fb4ecf [PERF] Precompute ResultSet indexes on large IMAP 
processings (#2974)
4f95fb4ecf is described below

commit 4f95fb4ecf915ecaa9fbfe42957da657d4a763fd
Author: Benoit TELLIER <[email protected]>
AuthorDate: Fri Mar 13 15:22:41 2026 +0100

    [PERF] Precompute ResultSet indexes on large IMAP processings (#2974)
    
    Outlook and other IMAP clients relies heavily on
    
    C: UID FETCH 1:* (FLAGS)
    
    For their resync and we spend ~50% of the compute of driver deserialization
    recomputing the same integer indexes over, and over again on the DB driver 
threads.
    
    This patch proposes precomputation on the first row of those indexes for 
reuse, saving
    easily this precious time.
---
 .../cassandra/mail/CassandraMessageIdDAO.java      | 18 +++--
 .../mailbox/cassandra/mail/FlagsExtractor.java     | 86 ++++++++++++++++++++++
 2 files changed, 99 insertions(+), 5 deletions(-)

diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
index f0ddb2eb2d..68c854b96c 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAO.java
@@ -465,20 +465,24 @@ public class CassandraMessageIdDAO {
     }
 
     public Flux<ComposedMessageIdWithMetaData> 
listMessagesMetadata(CassandraId mailboxId, MessageRange range) {
+        MemoizedSupplier<FlagsExtractor.Optimized> memoizedReader = new 
MemoizedSupplier<>();
+
         return cassandraAsyncExecutor.executeRows(selectMetadataRange.bind()
                 .set(MAILBOX_ID, mailboxId.asUuid(), TypeCodecs.TIMEUUID)
                 .setLong(IMAP_UID_GTE, range.getUidFrom().asLong())
                 .setLong(IMAP_UID_LTE, range.getUidTo().asLong())
                 .setExecutionProfile(readProfile))
             .map(row -> {
-                CassandraMessageId messageId = 
CassandraMessageId.Factory.of(row.get(MESSAGE_ID, TypeCodecs.TIMEUUID));
+                FlagsExtractor.Optimized reader = memoizedReader.get(() -> 
FlagsExtractor.Optimized.of(row));
+                CassandraMessageId messageId = 
CassandraMessageId.Factory.of(reader.getMessageId(row));
+
                 return ComposedMessageIdWithMetaData.builder()
-                    
.modSeq(ModSeq.of(TypeCodecs.BIGINT.decodePrimitive(row.getBytesUnsafe(MOD_SEQ),
 protocolVersion)))
-                    .threadId(getThreadIdFromRow(row, messageId))
-                    .flags(FlagsExtractor.getFlags(row))
+                    .modSeq(ModSeq.of(reader.getModSeq(row)))
+                    .threadId(asThreadId(messageId, reader.getThreadId(row)))
+                    .flags(reader.getFlags(row))
                     .composedMessageId(new ComposedMessageId(mailboxId,
                         messageId,
-                        
MessageUid.of(TypeCodecs.BIGINT.decodePrimitive(row.getBytesUnsafe(IMAP_UID), 
protocolVersion))))
+                        MessageUid.of(reader.getImapUid(row))))
                     .build();
             });
     }
@@ -609,6 +613,10 @@ public class CassandraMessageIdDAO {
 
     private ThreadId getThreadIdFromRow(Row row, MessageId messageId) {
         UUID threadIdUUID = row.get(THREAD_ID, TypeCodecs.TIMEUUID);
+        return asThreadId(messageId, threadIdUUID);
+    }
+
+    private static ThreadId asThreadId(MessageId messageId, UUID threadIdUUID) 
{
         if (threadIdUUID == null) {
             return ThreadId.fromBaseMessageId(messageId);
         }
diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/FlagsExtractor.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/FlagsExtractor.java
index b812ada7ab..dc883a0e3e 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/FlagsExtractor.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/FlagsExtractor.java
@@ -21,15 +21,21 @@ package org.apache.james.mailbox.cassandra.mail;
 import static com.datastax.oss.driver.api.core.type.DataTypes.TEXT;
 import static com.datastax.oss.driver.api.core.type.DataTypes.setOf;
 
+import java.time.Instant;
 import java.util.Set;
+import java.util.UUID;
 
 import jakarta.mail.Flags;
 
+import org.apache.james.mailbox.cassandra.table.CassandraMessageIdTable;
+import org.apache.james.mailbox.cassandra.table.CassandraMessageIds;
+import org.apache.james.mailbox.cassandra.table.CassandraMessageV3Table;
 import org.apache.james.mailbox.cassandra.table.Flag;
 import org.apache.james.mailbox.store.StoreMessageManager;
 
 import com.datastax.oss.driver.api.core.CqlIdentifier;
 import com.datastax.oss.driver.api.core.ProtocolVersion;
+import com.datastax.oss.driver.api.core.cql.ColumnDefinitions;
 import com.datastax.oss.driver.api.core.cql.Row;
 import com.datastax.oss.driver.api.core.type.codec.TypeCodec;
 import com.datastax.oss.driver.api.core.type.codec.TypeCodecs;
@@ -38,6 +44,86 @@ import 
com.datastax.oss.driver.api.core.type.codec.registry.CodecRegistry;
 public class FlagsExtractor {
     public static final TypeCodec<Set<String>> SET_OF_STRINGS_CODEC = 
CodecRegistry.DEFAULT.codecFor(setOf(TEXT));
 
+    /**
+     * Immutable row reader whose column indices are pre-computed once from the
+     * {@link ColumnDefinitions} of the first row in a ResultSet.  All 
subsequent
+     * rows of the same ResultSet reuse those indices, eliminating the per-row
+     * {@code ColumnDefinitions.firstIndexOf} scan which takes 50% of the 
Cassandra
+     * CPU post treatment time where in use.
+     */
+    public static class Optimized {
+        private final int[] flagIndices;
+        private final int userFlagsIndex;
+        private final int messageIdIndex;
+        private final int mailboxIdIndex;
+        private final int imapUidIndex;
+        private final int modSeqIndex;
+        private final int threadIdIndex;
+        private final int internalDateIndex;
+
+        private final ProtocolVersion protocolVersion;
+
+        public static Optimized of(Row row) {
+            return new Optimized(row.getColumnDefinitions(), 
row.protocolVersion());
+        }
+
+        private Optimized(ColumnDefinitions defs, ProtocolVersion 
protocolVersion) {
+            this.protocolVersion = protocolVersion;
+
+            this.flagIndices = new int[Flag.ALL_LOWERCASE.length];
+            for (int i = 0; i < Flag.ALL_LOWERCASE.length; i++) {
+                this.flagIndices[i] = defs.firstIndexOf(Flag.ALL_LOWERCASE[i]);
+            }
+            this.userFlagsIndex = defs.firstIndexOf(Flag.USER_FLAGS);
+
+            this.messageIdIndex = 
defs.firstIndexOf(CassandraMessageIds.MESSAGE_ID);
+            this.mailboxIdIndex = 
defs.firstIndexOf(CassandraMessageIds.MAILBOX_ID);
+            this.imapUidIndex = 
defs.firstIndexOf(CassandraMessageIds.IMAP_UID);
+            this.modSeqIndex = 
defs.firstIndexOf(CassandraMessageIdTable.MOD_SEQ);
+            this.threadIdIndex = 
defs.firstIndexOf(CassandraMessageIdTable.THREAD_ID);
+            this.internalDateIndex = 
defs.firstIndexOf(CassandraMessageV3Table.INTERNAL_DATE);
+        }
+
+        public Flags getFlags(Row row) {
+            Flags flags = new Flags();
+            for (int i = 0; i < Flag.ALL_LOWERCASE.length; i++) {
+                CqlIdentifier cqlId = Flag.ALL_LOWERCASE[i];
+                if (!StoreMessageManager.HANDLE_RECENT && 
cqlId.equals(Flag.RECENT)) {
+                    continue;
+                }
+                if 
(TypeCodecs.BOOLEAN.decodePrimitive(row.getBytesUnsafe(flagIndices[i]), 
protocolVersion)) {
+                    flags.add(Flag.JAVAX_MAIL_FLAG.get(cqlId));
+                }
+            }
+            row.get(userFlagsIndex, SET_OF_STRINGS_CODEC).forEach(flags::add);
+            return flags;
+        }
+
+        public UUID getMessageId(Row row) {
+            return 
TypeCodecs.TIMEUUID.decode(row.getBytesUnsafe(messageIdIndex), protocolVersion);
+        }
+
+        public UUID getMailboxId(Row row) {
+            return 
TypeCodecs.TIMEUUID.decode(row.getBytesUnsafe(mailboxIdIndex), protocolVersion);
+        }
+
+        public long getImapUid(Row row) {
+            return 
TypeCodecs.BIGINT.decodePrimitive(row.getBytesUnsafe(imapUidIndex), 
protocolVersion);
+        }
+
+        public long getModSeq(Row row) {
+            return 
TypeCodecs.BIGINT.decodePrimitive(row.getBytesUnsafe(modSeqIndex), 
protocolVersion);
+        }
+
+        public UUID getThreadId(Row row) {
+            return 
TypeCodecs.TIMEUUID.decode(row.getBytesUnsafe(threadIdIndex), protocolVersion);
+        }
+
+        public Instant getInternalDate(Row row) {
+            return 
TypeCodecs.TIMESTAMP.decode(row.getBytesUnsafe(internalDateIndex), 
protocolVersion);
+        }
+    }
+
     public static Flags getFlags(Row row) {
         return getFlags(row, row.protocolVersion());
     }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to