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 <[email protected]> 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: [email protected] For additional commands, e-mail: [email protected]
