You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by rc...@apache.org on 2021/06/28 10:29:43 UTC

[james-project] 04/04: JAMES-3516 Add threadId to ComposedMessageIdWithMetaData

This is an automated email from the ASF dual-hosted git repository.

rcordier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit 8a827e753d71c6b0a55b72b4b80c77ad9af96913
Author: quanth <hq...@linagora.com>
AuthorDate: Wed Jun 23 14:22:29 2021 +0700

    JAMES-3516 Add threadId to ComposedMessageIdWithMetaData
---
 .../model/ComposedMessageIdWithMetaData.java       | 23 +++++++++--
 .../model/ComposedMessageIdWithMetaDataTest.java   | 23 +++++++++--
 .../cassandra/mail/CassandraMessageIdDAO.java      | 16 +++++++-
 .../cassandra/mail/CassandraMessageIdMapper.java   |  4 +-
 .../mail/CassandraMessageIdToImapUidDAO.java       | 16 +++++++-
 .../cassandra/mail/CassandraMessageMapper.java     |  2 +
 .../cassandra/table/MessageIdToImapUid.java        |  1 +
 .../mail/CassandraIndexTableHandlerTest.java       | 22 ++++++----
 .../cassandra/mail/CassandraMessageDAOTest.java    |  2 +
 .../cassandra/mail/CassandraMessageDAOV3Test.java  |  2 +
 .../cassandra/mail/CassandraMessageIdDAOTest.java  | 47 ++++++++++++++++++++++
 .../mail/CassandraMessageIdToImapUidDAOTest.java   | 33 +++++++++++++++
 .../task/RecomputeMailboxCountersServiceTest.java  |  5 ++-
 .../SolveMessageInconsistenciesServiceTest.java    |  5 +++
 .../model/openjpa/AbstractJPAMailboxMessage.java   |  1 +
 .../maildir/mail/model/MaildirMailboxMessage.java  |  1 +
 .../inmemory/mail/InMemoryMessageIdMapper.java     |  3 +-
 .../james/mailbox/store/mail/MessageMapper.java    |  3 +-
 .../mail/model/impl/SimpleMailboxMessage.java      |  1 +
 19 files changed, 188 insertions(+), 22 deletions(-)

diff --git a/mailbox/api/src/main/java/org/apache/james/mailbox/model/ComposedMessageIdWithMetaData.java b/mailbox/api/src/main/java/org/apache/james/mailbox/model/ComposedMessageIdWithMetaData.java
index ad05c0d..c30a202 100644
--- a/mailbox/api/src/main/java/org/apache/james/mailbox/model/ComposedMessageIdWithMetaData.java
+++ b/mailbox/api/src/main/java/org/apache/james/mailbox/model/ComposedMessageIdWithMetaData.java
@@ -38,6 +38,7 @@ public class ComposedMessageIdWithMetaData {
         private ComposedMessageId composedMessageId;
         private Flags flags;
         private ModSeq modSeq;
+        private ThreadId threadId;
 
         private Builder() {
         }
@@ -57,22 +58,30 @@ public class ComposedMessageIdWithMetaData {
             return this;
         }
 
+        public Builder threadId(ThreadId threadId) {
+            this.threadId = threadId;
+            return this;
+        }
+
         public ComposedMessageIdWithMetaData build() {
             Preconditions.checkNotNull(composedMessageId, "'composedMessageId' is mandatory");
             Preconditions.checkNotNull(flags, "'flags' is mandatory");
             Preconditions.checkNotNull(modSeq, "'modSeq' is mandatory");
-            return new ComposedMessageIdWithMetaData(composedMessageId, flags, modSeq);
+            Preconditions.checkNotNull(threadId, "'threadId' is mandatory");
+            return new ComposedMessageIdWithMetaData(composedMessageId, flags, modSeq, threadId);
         }
     }
 
     private final ComposedMessageId composedMessageId;
     private final Flags flags;
     private final ModSeq modSeq;
+    private final ThreadId threadId;
 
-    public ComposedMessageIdWithMetaData(ComposedMessageId composedMessageId, Flags flags, ModSeq modSeq) {
+    public ComposedMessageIdWithMetaData(ComposedMessageId composedMessageId, Flags flags, ModSeq modSeq, ThreadId threadId) {
         this.composedMessageId = composedMessageId;
         this.flags = flags;
         this.modSeq = modSeq;
+        this.threadId = threadId;
     }
 
     public ComposedMessageId getComposedMessageId() {
@@ -87,6 +96,10 @@ public class ComposedMessageIdWithMetaData {
         return modSeq;
     }
 
+    public ThreadId getThreadId() {
+        return threadId;
+    }
+
     public boolean isMatching(MessageId messageId) {
         return getComposedMessageId().getMessageId().equals(messageId);
     }
@@ -97,14 +110,15 @@ public class ComposedMessageIdWithMetaData {
             ComposedMessageIdWithMetaData other = (ComposedMessageIdWithMetaData) o;
             return Objects.equal(composedMessageId, other.composedMessageId)
                 && Objects.equal(flags, other.flags)
-                && Objects.equal(modSeq, other.modSeq);
+                && Objects.equal(modSeq, other.modSeq)
+                && Objects.equal(threadId, other.threadId);
         }
         return false;
     }
 
     @Override
     public final int hashCode() {
-        return Objects.hashCode(composedMessageId, flags, modSeq);
+        return Objects.hashCode(composedMessageId, flags, modSeq, threadId);
     }
 
     @Override
@@ -113,6 +127,7 @@ public class ComposedMessageIdWithMetaData {
             .add("composedMessageId", composedMessageId)
             .add("flags", flags)
             .add("modSeq", modSeq)
+            .add("threadId", threadId)
             .toString();
     }
 }
diff --git a/mailbox/api/src/test/java/org/apache/james/mailbox/model/ComposedMessageIdWithMetaDataTest.java b/mailbox/api/src/test/java/org/apache/james/mailbox/model/ComposedMessageIdWithMetaDataTest.java
index 77070ee..e5bcf21 100644
--- a/mailbox/api/src/test/java/org/apache/james/mailbox/model/ComposedMessageIdWithMetaDataTest.java
+++ b/mailbox/api/src/test/java/org/apache/james/mailbox/model/ComposedMessageIdWithMetaDataTest.java
@@ -36,15 +36,16 @@ class ComposedMessageIdWithMetaDataTest {
     private static final TestMessageId TEST_MESSAGE_ID = new TestMessageId("2");
     private static final MessageUid MESSAGE_UID = MessageUid.of(3);
     private static final ComposedMessageId COMPOSED_MESSAGE_ID = new ComposedMessageId(TEST_ID, TEST_MESSAGE_ID, MESSAGE_UID);
+    private static final ThreadId THREAD_ID = ThreadId.fromBaseMessageId(TEST_MESSAGE_ID);
 
     @Test
-    void buildShoudThrownWhenComposedMessageIdIsNull() {
+    void buildShouldThrownWhenComposedMessageIdIsNull() {
         assertThatThrownBy(() -> ComposedMessageIdWithMetaData.builder().build())
             .isInstanceOf(NullPointerException.class);
     }
 
     @Test
-    void buildShoudThrownWhenFlagsIsNull() {
+    void buildShouldThrownWhenFlagsIsNull() {
         assertThatThrownBy(() -> ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(COMPOSED_MESSAGE_ID)
                 .build())
@@ -52,7 +53,7 @@ class ComposedMessageIdWithMetaDataTest {
     }
 
     @Test
-    void buildShoudThrownWhenModSeqIsNull() {
+    void buildShouldThrownWhenModSeqIsNull() {
         assertThatThrownBy(() -> ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(COMPOSED_MESSAGE_ID)
                 .flags(new Flags())
@@ -61,7 +62,17 @@ class ComposedMessageIdWithMetaDataTest {
     }
 
     @Test
-    void buildShoudWork() {
+    void buildShouldThrownWhenThreadIdIsNull() {
+        assertThatThrownBy(() -> ComposedMessageIdWithMetaData.builder()
+                .composedMessageId(COMPOSED_MESSAGE_ID)
+                .flags(new Flags())
+                .modSeq(ModSeq.of(1))
+                .build())
+            .isInstanceOf(NullPointerException.class);
+    }
+
+    @Test
+    void buildShouldWork() {
         Flags flags = new Flags(Flag.RECENT);
         ModSeq modSeq = ModSeq.of(1);
 
@@ -69,11 +80,13 @@ class ComposedMessageIdWithMetaDataTest {
             .composedMessageId(COMPOSED_MESSAGE_ID)
             .flags(flags)
             .modSeq(modSeq)
+            .threadId(THREAD_ID)
             .build();
 
         assertThat(composedMessageIdWithMetaData.getComposedMessageId()).isEqualTo(COMPOSED_MESSAGE_ID);
         assertThat(composedMessageIdWithMetaData.getFlags()).isEqualTo(flags);
         assertThat(composedMessageIdWithMetaData.getModSeq()).isEqualTo(modSeq);
+        assertThat(composedMessageIdWithMetaData.getThreadId()).isEqualTo(THREAD_ID);
     }
 
     @Test
@@ -82,6 +95,7 @@ class ComposedMessageIdWithMetaDataTest {
                 .composedMessageId(new ComposedMessageId(TEST_ID, TEST_MESSAGE_ID, MESSAGE_UID))
                 .flags(new Flags(Flag.RECENT))
                 .modSeq(ModSeq.of(1))
+                .threadId(THREAD_ID)
                 .build();
 
         assertThat(composedMessageIdWithMetaData.isMatching(TEST_MESSAGE_ID)).isTrue();
@@ -93,6 +107,7 @@ class ComposedMessageIdWithMetaDataTest {
                 .composedMessageId(COMPOSED_MESSAGE_ID)
                 .flags(new Flags(Flag.RECENT))
                 .modSeq(ModSeq.of(1))
+                .threadId(THREAD_ID)
                 .build();
 
         assertThat(composedMessageIdWithMetaData.isMatching(new TestMessageId("3"))).isFalse();
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 5714e36..aff977a 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
@@ -45,9 +45,11 @@ import static org.apache.james.mailbox.cassandra.table.Flag.USER;
 import static org.apache.james.mailbox.cassandra.table.Flag.USER_FLAGS;
 import static org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.MOD_SEQ;
 import static org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.MOD_SEQ_LOWERCASE;
+import static org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.THREAD_ID_LOWERCASE;
 import static org.apache.james.util.ReactorUtils.publishIfPresent;
 
 import java.util.Optional;
+import java.util.UUID;
 
 import javax.inject.Inject;
 import javax.mail.Flags;
@@ -61,7 +63,9 @@ import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
 import org.apache.james.mailbox.cassandra.ids.CassandraMessageId.Factory;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.mailbox.model.MessageId;
 import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.util.streams.Limit;
 
 import com.datastax.driver.core.PreparedStatement;
@@ -349,13 +353,23 @@ public class CassandraMessageIdDAO {
                 .subscribe();
             return Optional.empty();
         }
+        MessageId messageId = messageIdFactory.of(row.getUUID(MESSAGE_ID_LOWERCASE));
         return Optional.of(ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(new ComposedMessageId(
                         CassandraId.of(row.getUUID(MAILBOX_ID_LOWERCASE)),
-                        messageIdFactory.of(row.getUUID(MESSAGE_ID_LOWERCASE)),
+                        messageId,
                         MessageUid.of(row.getLong(IMAP_UID))))
                 .flags(FlagsExtractor.getFlags(row))
                 .modSeq(ModSeq.of(row.getLong(MOD_SEQ_LOWERCASE)))
+                .threadId(getThreadIdFromRow(row, messageId))
                 .build());
     }
+
+    private ThreadId getThreadIdFromRow(Row row, MessageId messageId) {
+        UUID threadIdUUID = row.getUUID(THREAD_ID_LOWERCASE);
+        if (threadIdUUID == null) {
+            return ThreadId.fromBaseMessageId(messageId);
+        }
+        return ThreadId.fromBaseMessageId(messageIdFactory.of(threadIdUUID));
+    }
 }
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
index 84407d8..91bafd5 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
@@ -202,6 +202,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
             .composedMessageId(composedMessageId)
             .flags(mailboxMessage.createFlags())
             .modSeq(mailboxMessage.getModSeq())
+            .threadId(mailboxMessage.getThreadId())
             .build();
     }
 
@@ -313,7 +314,8 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
                 .map(modSeq -> new ComposedMessageIdWithMetaData(
                     oldComposedId.getComposedMessageId(),
                     newFlags,
-                    modSeq))
+                    modSeq,
+                    oldComposedId.getThreadId()))
             .flatMap(newComposedId -> updateFlags(oldComposedId, newComposedId));
         }
     }
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
index 98a58ef..4ff6d01 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAO.java
@@ -43,8 +43,10 @@ import static org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.MOD_SE
 import static org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.MOD_SEQ_LOWERCASE;
 import static org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.TABLE_NAME;
 import static org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.THREAD_ID;
+import static org.apache.james.mailbox.cassandra.table.MessageIdToImapUid.THREAD_ID_LOWERCASE;
 
 import java.util.Optional;
+import java.util.UUID;
 
 import javax.inject.Inject;
 import javax.mail.Flags;
@@ -61,6 +63,8 @@ import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
 import org.apache.james.mailbox.cassandra.ids.CassandraMessageId.Factory;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.ThreadId;
 
 import com.datastax.driver.core.BoundStatement;
 import com.datastax.driver.core.PreparedStatement;
@@ -245,16 +249,26 @@ public class CassandraMessageIdToImapUidDAO {
     }
 
     private ComposedMessageIdWithMetaData toComposedMessageIdWithMetadata(Row row) {
+        MessageId messageId = messageIdFactory.of(row.getUUID(MESSAGE_ID_LOWERCASE));
         return ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(new ComposedMessageId(
                     CassandraId.of(row.getUUID(MAILBOX_ID_LOWERCASE)),
-                    messageIdFactory.of(row.getUUID(MESSAGE_ID_LOWERCASE)),
+                    messageId,
                     MessageUid.of(row.getLong(IMAP_UID))))
                 .flags(FlagsExtractor.getFlags(row))
                 .modSeq(ModSeq.of(row.getLong(MOD_SEQ_LOWERCASE)))
+                .threadId(getThreadIdFromRow(row, messageId))
                 .build();
     }
 
+    private ThreadId getThreadIdFromRow(Row row, MessageId messageId) {
+        UUID threadIdUUID = row.getUUID(THREAD_ID_LOWERCASE);
+        if (threadIdUUID == null) {
+            return ThreadId.fromBaseMessageId(messageId);
+        }
+        return ThreadId.fromBaseMessageId(messageIdFactory.of(threadIdUUID));
+    }
+
     private Statement selectStatement(CassandraMessageId messageId, Optional<CassandraId> mailboxId) {
         return mailboxId
             .map(cassandraId -> select.bind()
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
index 7bf43d1..1d464fc 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
@@ -534,6 +534,7 @@ public class CassandraMessageMapper implements MessageMapper {
                 .composedMessageId(new ComposedMessageId(mailboxId, message.getMessageId(), message.getUid()))
                 .flags(message.createFlags())
                 .modSeq(message.getModSeq())
+                .threadId(message.getThreadId())
                 .build();
         return composedMessageIdWithMetaData;
     }
@@ -587,6 +588,7 @@ public class CassandraMessageMapper implements MessageMapper {
                 .composedMessageId(oldMetadata.getComposedMessageId())
                 .modSeq(newModSeq)
                 .flags(newFlags)
+                .threadId(oldMetadata.getThreadId())
                 .build();
         return imapUidDAO.updateMetadata(newMetadata, oldMetadata.getModSeq())
             .flatMap(success -> {
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/MessageIdToImapUid.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/MessageIdToImapUid.java
index 8294117..df1dd3c 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/MessageIdToImapUid.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/MessageIdToImapUid.java
@@ -33,6 +33,7 @@ public interface MessageIdToImapUid {
     String MOD_SEQ_LOWERCASE = MOD_SEQ.toLowerCase(Locale.US);
 
     String THREAD_ID = "threadId";
+    String THREAD_ID_LOWERCASE = THREAD_ID.toLowerCase(Locale.US);
 
     String[] FIELDS = { MESSAGE_ID, MAILBOX_ID, IMAP_UID, THREAD_ID, MOD_SEQ,
             Flag.ANSWERED, Flag.DELETED, Flag.DRAFT, Flag.FLAGGED, Flag.RECENT, Flag.SEEN, Flag.USER, Flag.USER_FLAGS };
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java
index beb08d5..5151916 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraIndexTableHandlerTest.java
@@ -45,6 +45,7 @@ import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxPath;
 import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.mailbox.model.UpdatedFlags;
 import org.apache.james.mailbox.store.MessageBuilder;
@@ -198,7 +199,8 @@ class CassandraIndexTableHandlerTest {
         testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
                 new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
                 new Flags(Flags.Flag.RECENT),
-                MODSEQ),
+                MODSEQ,
+                ThreadId.fromBaseMessageId(CASSANDRA_MESSAGE_ID)),
             MAILBOX_ID).block();
 
         Long actual = mailboxCounterDAO.countMessagesInMailbox(mailbox).block();
@@ -215,7 +217,8 @@ class CassandraIndexTableHandlerTest {
         testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
                 new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
                 new Flags(),
-                MODSEQ),
+                MODSEQ,
+                ThreadId.fromBaseMessageId(CASSANDRA_MESSAGE_ID)),
             MAILBOX_ID).block();
 
         Long actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).block();
@@ -232,7 +235,8 @@ class CassandraIndexTableHandlerTest {
         testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
                 new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
                 new Flags(Flags.Flag.SEEN),
-                MODSEQ),
+                MODSEQ,
+                ThreadId.fromBaseMessageId(CASSANDRA_MESSAGE_ID)),
             MAILBOX_ID).block();
 
         Long actual = mailboxCounterDAO.countUnseenMessagesInMailbox(mailbox).block();
@@ -249,7 +253,8 @@ class CassandraIndexTableHandlerTest {
         testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
                 new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
                 new Flags(Flags.Flag.RECENT),
-                MODSEQ),
+                MODSEQ,
+                ThreadId.fromBaseMessageId(CASSANDRA_MESSAGE_ID)),
             MAILBOX_ID).block();
 
         assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID)
@@ -269,7 +274,8 @@ class CassandraIndexTableHandlerTest {
         testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
                 new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
                 new Flags(),
-                MODSEQ),
+                MODSEQ,
+                ThreadId.fromBaseMessageId(CASSANDRA_MESSAGE_ID)),
             MAILBOX_ID).block();
 
         assertThat(mailboxRecentsDAO.getRecentMessageUidsInMailbox(MAILBOX_ID)
@@ -287,7 +293,8 @@ class CassandraIndexTableHandlerTest {
         testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
                 new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
                 new Flags(Flags.Flag.DELETED),
-                MODSEQ),
+                MODSEQ,
+                ThreadId.fromBaseMessageId(CASSANDRA_MESSAGE_ID)),
             MAILBOX_ID).block();
 
         assertThat(
@@ -656,7 +663,8 @@ class CassandraIndexTableHandlerTest {
         testee.updateIndexOnDelete(new ComposedMessageIdWithMetaData(
             new ComposedMessageId(MAILBOX_ID, CASSANDRA_MESSAGE_ID, MESSAGE_UID),
             new Flags(),
-            MODSEQ), MAILBOX_ID).block();
+            MODSEQ,
+            ThreadId.fromBaseMessageId(CASSANDRA_MESSAGE_ID)), MAILBOX_ID).block();
 
         Boolean actual = firstUnseenDAO.retrieveFirstUnread(MAILBOX_ID).hasElement().block();
         assertThat(actual).isFalse();
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOTest.java
index 7003aae..b80822e 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOTest.java
@@ -47,6 +47,7 @@ import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
 import org.apache.james.mailbox.model.MessageAttachmentMetadata;
 import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
 import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
@@ -102,6 +103,7 @@ class CassandraMessageDAOTest {
                 .composedMessageId(new ComposedMessageId(MAILBOX_ID, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
     }
 
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOV3Test.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOV3Test.java
index 7ef00ea..21c9f5a 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOV3Test.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageDAOV3Test.java
@@ -47,6 +47,7 @@ import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
 import org.apache.james.mailbox.model.MessageAttachmentMetadata;
 import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.model.impl.PropertyBuilder;
 import org.apache.james.mailbox.store.mail.model.impl.SimpleMailboxMessage;
@@ -100,6 +101,7 @@ class CassandraMessageDAOV3Test {
                 .composedMessageId(new ComposedMessageId(MAILBOX_ID, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
     }
 
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAOTest.java
index 2ae675e..26d5580 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAOTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdDAOTest.java
@@ -37,6 +37,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
 import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.util.streams.Limit;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -76,6 +77,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -94,6 +96,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -103,6 +106,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags(org.apache.james.mailbox.cassandra.table.Flag.ANSWERED))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -122,11 +126,13 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build()),
             testee.insert(ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId2, messageUid2))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId2))
                 .build()))
         .blockLast();
 
@@ -148,6 +154,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.insert(composedMessageIdWithMetaData).block();
 
@@ -166,6 +173,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -173,6 +181,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId).block();
 
@@ -191,6 +200,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -198,6 +208,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.ANSWERED))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId).block();
 
@@ -216,6 +227,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -223,6 +235,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.DELETED))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId).block();
 
@@ -241,6 +254,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -248,6 +262,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.DRAFT))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId).block();
 
@@ -266,6 +281,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -273,6 +289,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.FLAGGED))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId).block();
 
@@ -291,6 +308,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -298,6 +316,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.RECENT))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId).block();
 
@@ -316,6 +335,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -323,6 +343,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.SEEN))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId).block();
 
@@ -341,6 +362,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -348,6 +370,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.USER))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId).block();
 
@@ -366,6 +389,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -375,6 +399,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(flags)
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId).block();
 
@@ -391,6 +416,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.insert(composedMessageIdWithMetaData).block();
 
@@ -411,11 +437,13 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         ComposedMessageIdWithMetaData composedMessageIdWithMetaData2 = ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId2, messageUid2))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         Flux.merge(testee.insert(composedMessageIdWithMetaData),
                 testee.insert(composedMessageIdWithMetaData2))
@@ -437,11 +465,13 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         ComposedMessageIdWithMetaData composedMessageIdWithMetaData2 = ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId2, messageUid2))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId2))
                 .build();
         Flux.merge(testee.insert(composedMessageIdWithMetaData),
                 testee.insert(composedMessageIdWithMetaData2))
@@ -465,17 +495,20 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId2, messageUid2))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId2))
                 .build();
         ComposedMessageIdWithMetaData composedMessageIdWithMetaData2 = ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId3, messageUid3))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId3))
                 .build();
         Flux.merge(testee.insert(
                 ComposedMessageIdWithMetaData.builder()
                     .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                     .flags(new Flags())
                     .modSeq(ModSeq.of(1))
+                    .threadId(ThreadId.fromBaseMessageId(messageId))
                     .build()),
                 testee.insert(composedMessageIdWithMetaData),
                 testee.insert(composedMessageIdWithMetaData2))
@@ -499,17 +532,20 @@ class CassandraMessageIdDAOTest {
             .composedMessageId(new ComposedMessageId(mailboxId, messageId2, messageUid2))
             .flags(new Flags())
             .modSeq(ModSeq.of(1))
+            .threadId(ThreadId.fromBaseMessageId(messageId2))
             .build();
         ComposedMessageIdWithMetaData composedMessageIdWithMetaData2 = ComposedMessageIdWithMetaData.builder()
             .composedMessageId(new ComposedMessageId(mailboxId, messageId3, messageUid3))
             .flags(new Flags())
             .modSeq(ModSeq.of(1))
+            .threadId(ThreadId.fromBaseMessageId(messageId3))
             .build();
         Flux.merge(testee.insert(
             ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build()),
             testee.insert(composedMessageIdWithMetaData),
             testee.insert(composedMessageIdWithMetaData2))
@@ -535,6 +571,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -542,12 +579,14 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId2, messageUid2))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId2))
                 .build();
 
         ComposedMessageIdWithMetaData composedMessageIdWithMetaData2 = ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId3, messageUid3))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId3))
                 .build();
         Flux.merge(testee.insert(composedMessageIdWithMetaData),
                 testee.insert(composedMessageIdWithMetaData2),
@@ -555,6 +594,7 @@ class CassandraMessageIdDAOTest {
                     .composedMessageId(new ComposedMessageId(mailboxId, messageId4, messageUid4))
                     .flags(new Flags())
                     .modSeq(ModSeq.of(1))
+                    .threadId(ThreadId.fromBaseMessageId(messageId4))
                     .build()))
         .blockLast();
 
@@ -578,6 +618,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
             .build())
             .block();
 
@@ -585,12 +626,14 @@ class CassandraMessageIdDAOTest {
             .composedMessageId(new ComposedMessageId(mailboxId, messageId2, messageUid2))
             .flags(new Flags())
             .modSeq(ModSeq.of(1))
+            .threadId(ThreadId.fromBaseMessageId(messageId2))
             .build();
 
         ComposedMessageIdWithMetaData composedMessageIdWithMetaData2 = ComposedMessageIdWithMetaData.builder()
             .composedMessageId(new ComposedMessageId(mailboxId, messageId3, messageUid3))
             .flags(new Flags())
             .modSeq(ModSeq.of(1))
+            .threadId(ThreadId.fromBaseMessageId(messageId3))
             .build();
         Flux.merge(testee.insert(composedMessageIdWithMetaData),
             testee.insert(composedMessageIdWithMetaData2),
@@ -598,6 +641,7 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId4, messageUid4))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId4))
                 .build()))
             .blockLast();
 
@@ -619,18 +663,21 @@ class CassandraMessageIdDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId2, messageUid2))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId2))
                 .build();
         Flux.merge(testee.insert(
                 ComposedMessageIdWithMetaData.builder()
                     .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                     .flags(new Flags())
                     .modSeq(ModSeq.of(1))
+                    .threadId(ThreadId.fromBaseMessageId(messageId))
                     .build()),
                 testee.insert(composedMessageIdWithMetaData),
                 testee.insert(ComposedMessageIdWithMetaData.builder()
                     .composedMessageId(new ComposedMessageId(mailboxId, messageId3, messageUid3))
                     .flags(new Flags())
                     .modSeq(ModSeq.of(1))
+                    .threadId(ThreadId.fromBaseMessageId(messageId3))
                     .build()))
         .blockLast();
 
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAOTest.java
index 6abb4b6..0a376b7 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAOTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdToImapUidDAOTest.java
@@ -38,6 +38,7 @@ import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.mailbox.model.ThreadId;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
@@ -83,6 +84,7 @@ class CassandraMessageIdToImapUidDAOTest {
                     .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                     .flags(new Flags())
                     .modSeq(ModSeq.of(1))
+                    .threadId(ThreadId.fromBaseMessageId(messageId))
                     .build())
                 .block();
 
@@ -104,11 +106,13 @@ class CassandraMessageIdToImapUidDAOTest {
                     .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                     .flags(new Flags())
                     .modSeq(ModSeq.of(1))
+                    .threadId(ThreadId.fromBaseMessageId(messageId))
                     .build()),
             testee.insert(ComposedMessageIdWithMetaData.builder()
                     .composedMessageId(new ComposedMessageId(mailboxId2, messageId, messageUid2))
                     .flags(new Flags())
                     .modSeq(ModSeq.of(1))
+                    .threadId(ThreadId.fromBaseMessageId(messageId))
                     .build()))
         .blockLast();
 
@@ -118,6 +122,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId2, messageId, messageUid2))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         List<ComposedMessageIdWithMetaData> messages = testee.retrieve(messageId, Optional.empty()).collectList().block();
         assertThat(messages).containsOnly(expectedComposedMessageId);
@@ -133,6 +138,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -140,6 +146,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         List<ComposedMessageIdWithMetaData> messages = testee.retrieve(messageId, Optional.of(mailboxId)).collectList().block();
         assertThat(messages).containsOnly(expectedComposedMessageId);
@@ -155,6 +162,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.insert(composedMessageIdWithFlags).block();
 
@@ -173,6 +181,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.insert(composedMessageIdWithFlags).block();
 
@@ -192,6 +201,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -199,6 +209,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId, ModSeq.of(1)).block();
 
@@ -217,6 +228,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -224,6 +236,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.ANSWERED))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId, ModSeq.of(1)).block();
 
@@ -242,6 +255,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -249,6 +263,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.DELETED))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId, ModSeq.of(1)).block();
 
@@ -267,6 +282,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -274,6 +290,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.DRAFT))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId, ModSeq.of(1)).block();
 
@@ -292,6 +309,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -299,6 +317,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.FLAGGED))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId, ModSeq.of(1)).block();
 
@@ -317,6 +336,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -324,6 +344,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.RECENT))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId, ModSeq.of(1)).block();
 
@@ -342,6 +363,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -349,6 +371,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.SEEN))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         assertThat(testee.updateMetadata(expectedComposedMessageId, ModSeq.of(1)).block())
             .isTrue();
@@ -368,6 +391,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -375,6 +399,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags(Flag.USER))
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId, ModSeq.of(1)).block();
 
@@ -393,6 +418,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -402,6 +428,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(composedMessageId)
                 .flags(flags)
                 .modSeq(ModSeq.of(2))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         testee.updateMetadata(expectedComposedMessageId, ModSeq.of(1)).block();
 
@@ -418,6 +445,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build())
             .block();
 
@@ -425,6 +453,7 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         List<ComposedMessageIdWithMetaData> messages = testee.retrieve(messageId, Optional.of(mailboxId)).collectList().block();
 
@@ -443,11 +472,13 @@ class CassandraMessageIdToImapUidDAOTest {
                     .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                     .flags(new Flags())
                     .modSeq(ModSeq.of(1))
+                    .threadId(ThreadId.fromBaseMessageId(messageId))
                     .build()),
                 testee.insert(ComposedMessageIdWithMetaData.builder()
                     .composedMessageId(new ComposedMessageId(mailboxId2, messageId, messageUid2))
                     .flags(new Flags())
                     .modSeq(ModSeq.of(1))
+                    .threadId(ThreadId.fromBaseMessageId(messageId))
                     .build()))
         .blockLast();
 
@@ -455,11 +486,13 @@ class CassandraMessageIdToImapUidDAOTest {
                 .composedMessageId(new ComposedMessageId(mailboxId, messageId, messageUid))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         ComposedMessageIdWithMetaData expectedComposedMessageId2 = ComposedMessageIdWithMetaData.builder()
                 .composedMessageId(new ComposedMessageId(mailboxId2, messageId, messageUid2))
                 .flags(new Flags())
                 .modSeq(ModSeq.of(1))
+                .threadId(ThreadId.fromBaseMessageId(messageId))
                 .build();
         List<ComposedMessageIdWithMetaData> messages = testee.retrieve(messageId, Optional.empty()).collectList().block();
 
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java
index 0813247..2cabbc5 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/RecomputeMailboxCountersServiceTest.java
@@ -50,6 +50,7 @@ import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxCounters;
 import org.apache.james.mailbox.model.MailboxPath;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.mailbox.model.UidValidity;
 import org.apache.james.task.Task.Result;
 import org.junit.jupiter.api.BeforeEach;
@@ -67,8 +68,8 @@ class RecomputeMailboxCountersServiceTest {
     private static final CassandraMessageId.Factory MESSAGE_ID_FACTORY = new CassandraMessageId.Factory();
     private static final CassandraMessageId MESSAGE_ID_1 = MESSAGE_ID_FACTORY.fromString("40ff9e30-6022-11ea-9a94-d300cbf968c0");
     private static CassandraId CASSANDRA_ID_1 = CassandraId.of(UUID.fromString("16d681e0-6023-11ea-a7f2-0f94ad804b0d"));
-    private static final ComposedMessageIdWithMetaData METADATA_UNSEEN = new ComposedMessageIdWithMetaData(new ComposedMessageId(CASSANDRA_ID_1, MESSAGE_ID_1, MessageUid.of(45)), new Flags(), ModSeq.of(45));
-    private static final ComposedMessageIdWithMetaData METADATA_SEEN = new ComposedMessageIdWithMetaData(new ComposedMessageId(CASSANDRA_ID_1, MESSAGE_ID_1, MessageUid.of(45)), new Flags(Flags.Flag.SEEN), ModSeq.of(45));
+    private static final ComposedMessageIdWithMetaData METADATA_UNSEEN = new ComposedMessageIdWithMetaData(new ComposedMessageId(CASSANDRA_ID_1, MESSAGE_ID_1, MessageUid.of(45)), new Flags(), ModSeq.of(45), ThreadId.fromBaseMessageId(MESSAGE_ID_1));
+    private static final ComposedMessageIdWithMetaData METADATA_SEEN = new ComposedMessageIdWithMetaData(new ComposedMessageId(CASSANDRA_ID_1, MESSAGE_ID_1, MessageUid.of(45)), new Flags(Flags.Flag.SEEN), ModSeq.of(45), ThreadId.fromBaseMessageId(MESSAGE_ID_1));
     private static final Mailbox MAILBOX = new Mailbox(MAILBOX_PATH, UID_VALIDITY_1, CASSANDRA_ID_1);
 
     @RegisterExtension
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMessageInconsistenciesServiceTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMessageInconsistenciesServiceTest.java
index d7c7c46..18d66c5 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMessageInconsistenciesServiceTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/task/SolveMessageInconsistenciesServiceTest.java
@@ -45,6 +45,7 @@ import org.apache.james.mailbox.cassandra.mail.task.SolveMessageInconsistenciesS
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.model.ComposedMessageId;
 import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
+import org.apache.james.mailbox.model.ThreadId;
 import org.apache.james.task.Task;
 import org.assertj.core.api.SoftAssertions;
 import org.junit.jupiter.api.BeforeEach;
@@ -70,24 +71,28 @@ public class SolveMessageInconsistenciesServiceTest {
         .composedMessageId(new ComposedMessageId(MAILBOX_ID, MESSAGE_ID_1, MESSAGE_UID_1))
         .modSeq(MOD_SEQ_1)
         .flags(new Flags())
+        .threadId(ThreadId.fromBaseMessageId(MESSAGE_ID_1))
         .build();
 
     private static final ComposedMessageIdWithMetaData MESSAGE_1_WITH_SEEN_FLAG = ComposedMessageIdWithMetaData.builder()
         .composedMessageId(new ComposedMessageId(MAILBOX_ID, MESSAGE_ID_1, MESSAGE_UID_1))
         .modSeq(MOD_SEQ_1)
         .flags(new Flags(Flags.Flag.SEEN))
+        .threadId(ThreadId.fromBaseMessageId(MESSAGE_ID_1))
         .build();
 
     private static final ComposedMessageIdWithMetaData MESSAGE_1_WITH_MOD_SEQ_2 = ComposedMessageIdWithMetaData.builder()
         .composedMessageId(new ComposedMessageId(MAILBOX_ID, MESSAGE_ID_1, MESSAGE_UID_1))
         .modSeq(MOD_SEQ_2)
         .flags(new Flags(Flags.Flag.SEEN))
+        .threadId(ThreadId.fromBaseMessageId(MESSAGE_ID_1))
         .build();
 
     private static final ComposedMessageIdWithMetaData MESSAGE_2 = ComposedMessageIdWithMetaData.builder()
         .composedMessageId(new ComposedMessageId(MAILBOX_ID, MESSAGE_ID_2, MESSAGE_UID_2))
         .modSeq(MOD_SEQ_2)
         .flags(new Flags())
+        .threadId(ThreadId.fromBaseMessageId(MESSAGE_ID_2))
         .build();
 
     @RegisterExtension
diff --git a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java
index 6c833e2..cd9bf2f 100644
--- a/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java
+++ b/mailbox/jpa/src/main/java/org/apache/james/mailbox/jpa/mail/model/openjpa/AbstractJPAMailboxMessage.java
@@ -331,6 +331,7 @@ public abstract class AbstractJPAMailboxMessage implements MailboxMessage {
             .modSeq(getModSeq())
             .flags(createFlags())
             .composedMessageId(new ComposedMessageId(mailbox.getMailboxId(), getMessageId(), MessageUid.of(uid)))
+            .threadId(getThreadId())
             .build();
     }
 
diff --git a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMailboxMessage.java b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMailboxMessage.java
index 70ffdaa..f3f84e9 100644
--- a/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMailboxMessage.java
+++ b/mailbox/maildir/src/main/java/org/apache/james/mailbox/maildir/mail/model/MaildirMailboxMessage.java
@@ -78,6 +78,7 @@ public class MaildirMailboxMessage extends DelegatingMailboxMessage {
             .modSeq(modSeq)
             .flags(createFlags())
             .composedMessageId(new ComposedMessageId(mailbox.getMailboxId(), getMessageId(), uid))
+            .threadId(getThreadId())
             .build();
     }
 
diff --git a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageIdMapper.java b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageIdMapper.java
index 569b1ae..6e13e06 100644
--- a/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageIdMapper.java
+++ b/mailbox/memory/src/main/java/org/apache/james/mailbox/inmemory/mail/InMemoryMessageIdMapper.java
@@ -81,7 +81,8 @@ public class InMemoryMessageIdMapper implements MessageIdMapper {
                     message.getMessageId(),
                     message.getUid()),
                 message.createFlags(),
-                message.getModSeq()));
+                message.getModSeq(),
+                message.getThreadId()));
     }
 
     @Override
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java
index afce327..c844a0a 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/MessageMapper.java
@@ -80,7 +80,8 @@ public interface MessageMapper extends Mapper {
                     message.getMessageId(),
                     message.getUid()),
                 message.createFlags(),
-                message.getModSeq()));
+                message.getModSeq(),
+                message.getThreadId()));
     }
 
     default Flux<MailboxMessage> findInMailboxReactive(Mailbox mailbox, MessageRange set, FetchType type, int limit) {
diff --git a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailboxMessage.java b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailboxMessage.java
index ef2aaba..5b5843a 100644
--- a/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailboxMessage.java
+++ b/mailbox/store/src/main/java/org/apache/james/mailbox/store/mail/model/impl/SimpleMailboxMessage.java
@@ -222,6 +222,7 @@ public class SimpleMailboxMessage extends DelegatingMailboxMessage {
             .modSeq(modSeq)
             .flags(createFlags())
             .composedMessageId(new ComposedMessageId(mailboxId, getMessageId(), uid))
+            .threadId(getThreadId())
             .build();
     }
 

---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org