You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by rc...@apache.org on 2020/04/10 02:02:10 UTC

[james-project] 02/15: JAMES-3136 Switch imapUidDAO into source of truth for CassandraMessageMapper

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 f44aeed5630bc38316424c1e4d218fb360b4129b
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Tue Apr 7 10:30:08 2020 +0700

    JAMES-3136 Switch imapUidDAO into source of truth for CassandraMessageMapper
    
    Write to it first, then denormalize to `messageIdTable`.
    
    Tests the various failures that can occur on the write path for
    CassandraMessageMapper.
---
 .../cassandra/mail/CassandraMessageMapper.java     |   6 +-
 .../cassandra/mail/CassandraMessageMapperTest.java | 133 +++++++++++++++++++++
 .../store/mail/model/MessageMapperTest.java        |  24 ++--
 3 files changed, 147 insertions(+), 16 deletions(-)

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 23dbc07..807b891 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
@@ -405,10 +405,8 @@ public class CassandraMessageMapper implements MessageMapper {
                 .flags(message.createFlags())
                 .modSeq(message.getModSeq())
                 .build();
-        return Flux.merge(
-                messageIdDAO.insert(composedMessageIdWithMetaData),
-                imapUidDAO.insert(composedMessageIdWithMetaData))
-            .then();
+        return imapUidDAO.insert(composedMessageIdWithMetaData)
+            .then(messageIdDAO.insert(composedMessageIdWithMetaData));
     }
 
 
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java
index acfacf3..fb647c0 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapperTest.java
@@ -19,11 +19,25 @@
 
 package org.apache.james.mailbox.cassandra.mail;
 
+import static org.apache.james.backends.cassandra.Scenario.Builder.fail;
+
+import java.util.Optional;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.CassandraClusterExtension;
+import org.apache.james.mailbox.cassandra.ids.CassandraId;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.model.MessageRange;
+import org.apache.james.mailbox.store.mail.MessageMapper;
 import org.apache.james.mailbox.store.mail.model.MapperProvider;
 import org.apache.james.mailbox.store.mail.model.MessageMapperTest;
+import org.assertj.core.api.SoftAssertions;
+import org.junit.jupiter.api.Nested;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
+import com.github.fge.lambdas.Throwing;
+
 class CassandraMessageMapperTest extends MessageMapperTest {
     @RegisterExtension
     static CassandraClusterExtension cassandraCluster = new CassandraClusterExtension(MailboxAggregateModule.MODULE);
@@ -32,4 +46,123 @@ class CassandraMessageMapperTest extends MessageMapperTest {
     protected MapperProvider createMapperProvider() {
         return new CassandraMapperProvider(cassandraCluster.getCassandraCluster());
     }
+
+    @Nested
+    class FailureTesting {
+        @Test
+        void retrieveMessagesShouldNotReturnMessagesWhenFailToPersistInMessageDAO(CassandraCluster cassandra) {
+            cassandra.getConf()
+                .registerScenario(fail()
+                    .forever()
+                    .whenQueryStartsWith("INSERT INTO messageV2 (messageId,internalDate,bodyStartOctet,fullContentOctets,bodyOctets,bodyContent,headerContent,properties,textualLineCount,attachments)"));
+
+            try {
+                messageMapper.add(benwaInboxMailbox, message1);
+            } catch (Exception e) {
+                // ignoring expected error
+            }
+
+            CassandraMessageIdDAO messageIdDAO = new CassandraMessageIdDAO(cassandra.getConf(), new CassandraMessageId.Factory());
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.all(), MessageMapper.FetchType.Metadata, 1))
+                    .toIterable()
+                    .isEmpty();
+                softly.assertThat(messageIdDAO.retrieveMessages((CassandraId) benwaInboxMailbox.getMailboxId(), MessageRange.all()).collectList().block())
+                    .isEmpty();
+            }));
+        }
+
+        @Test
+        void retrieveMessagesShouldNotReturnMessagesWhenFailsToPersistBlobParts(CassandraCluster cassandra) {
+            cassandra.getConf()
+                .registerScenario(fail()
+                    .forever()
+                    .whenQueryStartsWith("INSERT INTO blobParts (id,chunkNumber,data) VALUES (:id,:chunkNumber,:data);"));
+
+            try {
+                messageMapper.add(benwaInboxMailbox, message1);
+            } catch (Exception e) {
+                // ignoring expected error
+            }
+
+            CassandraMessageIdDAO messageIdDAO = new CassandraMessageIdDAO(cassandra.getConf(), new CassandraMessageId.Factory());
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.all(), MessageMapper.FetchType.Metadata, 1))
+                    .toIterable()
+                    .isEmpty();
+                softly.assertThat(messageIdDAO.retrieveMessages((CassandraId) benwaInboxMailbox.getMailboxId(), MessageRange.all()).collectList().block())
+                    .isEmpty();
+            }));
+        }
+
+        @Test
+        void retrieveMessagesShouldNotReturnMessagesWhenFailsToPersistBlobs(CassandraCluster cassandra) {
+            cassandra.getConf()
+                .registerScenario(fail()
+                    .forever()
+                    .whenQueryStartsWith("INSERT INTO blobs (id,position) VALUES (:id,:position);"));
+
+            try {
+                messageMapper.add(benwaInboxMailbox, message1);
+            } catch (Exception e) {
+                // ignoring expected error
+            }
+
+            CassandraMessageIdDAO messageIdDAO = new CassandraMessageIdDAO(cassandra.getConf(), new CassandraMessageId.Factory());
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.all(), MessageMapper.FetchType.Metadata, 1))
+                    .toIterable()
+                    .isEmpty();
+                softly.assertThat(messageIdDAO.retrieveMessages((CassandraId) benwaInboxMailbox.getMailboxId(), MessageRange.all()).collectList().block())
+                    .isEmpty();
+            }));
+        }
+
+        @Test
+        void retrieveMessagesShouldNotReturnMessagesWhenFailsToPersistInImapUidTable(CassandraCluster cassandra) {
+            cassandra.getConf()
+                .registerScenario(fail()
+                    .forever()
+                    .whenQueryStartsWith("INSERT INTO imapUidTable (messageId,mailboxId,uid,modSeq,flagAnswered,flagDeleted,flagDraft,flagFlagged,flagRecent,flagSeen,flagUser,userFlags)"));
+
+            try {
+                messageMapper.add(benwaInboxMailbox, message1);
+            } catch (Exception e) {
+                // ignoring expected error
+            }
+
+            CassandraMessageIdDAO messageIdDAO = new CassandraMessageIdDAO(cassandra.getConf(), new CassandraMessageId.Factory());
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.all(), MessageMapper.FetchType.Metadata, 1))
+                    .toIterable()
+                    .isEmpty();
+                softly.assertThat(messageIdDAO.retrieveMessages((CassandraId) benwaInboxMailbox.getMailboxId(), MessageRange.all()).collectList().block())
+                    .isEmpty();
+            }));
+        }
+
+        @Test
+        void addShouldPersistInTableOfTruthWhenMessageIdTableWritesFails(CassandraCluster cassandra) {
+            cassandra.getConf()
+                .registerScenario(fail()
+                    .forever()
+                    .whenQueryStartsWith("INSERT INTO messageIdTable (mailboxId,uid,modSeq,messageId,flagAnswered,flagDeleted,flagDraft,flagFlagged,flagRecent,flagSeen,flagUser,userFlags)"));
+
+            try {
+                messageMapper.add(benwaInboxMailbox, message1);
+            } catch (Exception e) {
+                // ignoring expected error
+            }
+
+            CassandraMessageIdToImapUidDAO imapUidDAO = new CassandraMessageIdToImapUidDAO(cassandra.getConf(), new CassandraMessageId.Factory());
+
+            SoftAssertions.assertSoftly(Throwing.consumer(softly -> {
+                softly.assertThat(messageMapper.findInMailbox(benwaInboxMailbox, MessageRange.all(), MessageMapper.FetchType.Metadata, 1))
+                    .toIterable()
+                    .isEmpty();
+                softly.assertThat(imapUidDAO.retrieve((CassandraMessageId) message1.getMessageId(), Optional.empty()).collectList().block())
+                    .hasSize(1);
+            }));
+        }
+    }
 }
diff --git a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
index 11cf811..ae67a36 100644
--- a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
+++ b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MessageMapperTest.java
@@ -77,18 +77,18 @@ public abstract class MessageMapperTest {
     private static final Username BENWA = Username.of("benwa");
 
     private MapperProvider mapperProvider;
-    private MessageMapper messageMapper;
+    protected MessageMapper messageMapper;
     private MailboxMapper mailboxMapper;
 
-    private Mailbox benwaInboxMailbox;
-    private Mailbox benwaWorkMailbox;
-    
-    private MailboxMessage message1;
-    private MailboxMessage message2;
-    private MailboxMessage message3;
-    private MailboxMessage message4;
-    private MailboxMessage message5;
-    private MailboxMessage message6;
+    protected Mailbox benwaInboxMailbox;
+    protected Mailbox benwaWorkMailbox;
+
+    protected MailboxMessage message1;
+    protected MailboxMessage message2;
+    protected MailboxMessage message3;
+    protected MailboxMessage message4;
+    protected MailboxMessage message5;
+    protected MailboxMessage message6;
 
     protected abstract MapperProvider createMapperProvider();
 
@@ -1203,8 +1203,8 @@ public abstract class MessageMapperTest {
     private Mailbox createMailbox(MailboxPath mailboxPath) throws MailboxException {
         return mailboxMapper.create(mailboxPath, UID_VALIDITY);
     }
-    
-    private void saveMessages() throws MailboxException {
+
+    protected void saveMessages() throws MailboxException {
         messageMapper.add(benwaInboxMailbox, message1);
         message1.setModSeq(messageMapper.getHighestModSeq(benwaInboxMailbox));
         messageMapper.add(benwaInboxMailbox, message2);


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