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 ad...@apache.org on 2017/02/07 08:06:57 UTC

[08/10] james-project git commit: JAMES-1874 Cassandra optimization : Add a table for recent messages

JAMES-1874 Cassandra optimization : Add a table for recent messages


Project: http://git-wip-us.apache.org/repos/asf/james-project/repo
Commit: http://git-wip-us.apache.org/repos/asf/james-project/commit/c8c12826
Tree: http://git-wip-us.apache.org/repos/asf/james-project/tree/c8c12826
Diff: http://git-wip-us.apache.org/repos/asf/james-project/diff/c8c12826

Branch: refs/heads/master
Commit: c8c12826015ac8706d75d7d423aea5f38cc146c4
Parents: 9c28aad
Author: Benoit Tellier <bt...@linagora.com>
Authored: Fri Feb 3 19:41:02 2017 +0700
Committer: Benoit Tellier <bt...@linagora.com>
Committed: Tue Feb 7 08:57:46 2017 +0700

----------------------------------------------------------------------
 .../CassandraMailboxSessionMapperFactory.java   |  15 ++-
 .../mail/CassandraMailboxCounterDAO.java        |   3 +
 .../mail/CassandraMailboxRecentsDAO.java        | 105 ++++++++++++++++++
 .../mail/CassandraMessageIdMapper.java          |  29 +++--
 .../cassandra/mail/CassandraMessageMapper.java  |  55 +++++++---
 .../modules/CassandraMailboxRecentsModule.java  |  68 ++++++++++++
 .../table/CassandraMailboxRecentsTable.java     |  26 +++++
 .../cassandra/CassandraMailboxManagerTest.java  |  11 +-
 .../CassandraSubscriptionManagerTest.java       |   8 +-
 .../cassandra/CassandraTestSystemFixture.java   |  10 +-
 .../CassandraMailboxManagerAttachmentTest.java  |   6 +-
 .../mail/CassandraMailboxRecentDAOTest.java     | 107 +++++++++++++++++++
 .../cassandra/mail/CassandraMapperProvider.java |   6 +-
 .../cassandra/host/CassandraHostSystem.java     |   8 +-
 .../modules/mailbox/CassandraMailboxModule.java |   1 +
 15 files changed, 428 insertions(+), 30 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
index e3ce7c7..b384297 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxSessionMapperFactory.java
@@ -27,6 +27,7 @@ import org.apache.james.mailbox.cassandra.mail.CassandraAnnotationMapper;
 import org.apache.james.mailbox.cassandra.mail.CassandraAttachmentMapper;
 import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMailboxMapper;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdMapper;
@@ -59,20 +60,24 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
     private final CassandraMessageDAO messageDAO;
     private final CassandraMessageIdDAO messageIdDAO;
     private final CassandraMessageIdToImapUidDAO imapUidDAO;
-    private CassandraMailboxCounterDAO mailboxCounterDAO;
+    private final CassandraMailboxCounterDAO mailboxCounterDAO;
+    private final CassandraMailboxRecentsDAO mailboxRecentsDAO;
     private int maxRetry;
 
+
     @Inject
     public CassandraMailboxSessionMapperFactory(UidProvider uidProvider, ModSeqProvider modSeqProvider, 
             Session session, CassandraTypesProvider typesProvider,
-            CassandraMessageDAO messageDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO) {
+            CassandraMessageDAO messageDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO,
+            CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentsDAO) {
         this.uidProvider = uidProvider;
         this.modSeqProvider = modSeqProvider;
         this.session = session;
         this.messageDAO = messageDAO;
         this.messageIdDAO = messageIdDAO;
         this.imapUidDAO = imapUidDAO;
-        this.mailboxCounterDAO = new CassandraMailboxCounterDAO(session);
+        this.mailboxCounterDAO = mailboxCounterDAO;
+        this.mailboxRecentsDAO = mailboxRecentsDAO;
         this.maxRetry = DEFAULT_MAX_RETRY;
         this.typesProvider = typesProvider;
     }
@@ -84,13 +89,13 @@ public class CassandraMailboxSessionMapperFactory extends MailboxSessionMapperFa
     @Override
     public CassandraMessageMapper createMessageMapper(MailboxSession mailboxSession) {
         return new CassandraMessageMapper(uidProvider, modSeqProvider, null, maxRetry, createAttachmentMapper(mailboxSession),
-                messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO);
+                messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO, mailboxRecentsDAO);
     }
 
     @Override
     public MessageIdMapper createMessageIdMapper(MailboxSession mailboxSession) throws MailboxException {
         return new CassandraMessageIdMapper(getMailboxMapper(mailboxSession), getAttachmentMapper(mailboxSession),
-                imapUidDAO, messageIdDAO, messageDAO, mailboxCounterDAO, modSeqProvider, mailboxSession);
+                imapUidDAO, messageIdDAO, messageDAO, mailboxCounterDAO, mailboxRecentsDAO, modSeqProvider, mailboxSession);
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
index 32a1fee..7f3c35a 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxCounterDAO.java
@@ -29,6 +29,8 @@ import static com.datastax.driver.core.querybuilder.QueryBuilder.update;
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 
+import javax.inject.Inject;
+
 import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
 import org.apache.james.mailbox.cassandra.CassandraId;
 import org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable;
@@ -49,6 +51,7 @@ public class CassandraMailboxCounterDAO {
     private final PreparedStatement decrementUnseenCountStatement;
     private final PreparedStatement decrementMessageCountStatement;
 
+    @Inject
     public CassandraMailboxCounterDAO(Session session) {
         cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
         readStatement = createReadStatement(session);

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentsDAO.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentsDAO.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentsDAO.java
new file mode 100644
index 0000000..06df836
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentsDAO.java
@@ -0,0 +1,105 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.mail;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.bindMarker;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.delete;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.eq;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.insertInto;
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import javax.inject.Inject;
+
+import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
+import org.apache.james.backends.cassandra.utils.CassandraUtils;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.cassandra.CassandraId;
+import org.apache.james.mailbox.cassandra.table.CassandraMailboxRecentsTable;
+
+import com.datastax.driver.core.BoundStatement;
+import com.datastax.driver.core.PreparedStatement;
+import com.datastax.driver.core.Session;
+import com.github.steveash.guavate.Guavate;
+
+public class CassandraMailboxRecentsDAO {
+
+    private final CassandraAsyncExecutor cassandraAsyncExecutor;
+    private final PreparedStatement readStatement;
+    private final PreparedStatement deleteStatement;
+    private final PreparedStatement addStatement;
+
+    @Inject
+    public CassandraMailboxRecentsDAO(Session session) {
+        cassandraAsyncExecutor = new CassandraAsyncExecutor(session);
+        readStatement = createReadStatement(session);
+        deleteStatement = createDeleteStatement(session);
+        addStatement = createAddStatement(session);
+    }
+
+    private PreparedStatement createReadStatement(Session session) {
+        return session.prepare(
+            select(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID)
+                .from(CassandraMailboxRecentsTable.TABLE_NAME)
+                .where(eq(CassandraMailboxRecentsTable.MAILBOX_ID, bindMarker(CassandraMailboxRecentsTable.MAILBOX_ID))));
+    }
+
+    private PreparedStatement createDeleteStatement(Session session) {
+        return session.prepare(
+            delete()
+                .from(CassandraMailboxRecentsTable.TABLE_NAME)
+                .where(eq(CassandraMailboxRecentsTable.MAILBOX_ID, bindMarker(CassandraMailboxRecentsTable.MAILBOX_ID)))
+                .and(eq(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID, bindMarker(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID))));
+    }
+
+    private PreparedStatement createAddStatement(Session session) {
+        return session.prepare(
+            insertInto(CassandraMailboxRecentsTable.TABLE_NAME)
+                .value(CassandraMailboxRecentsTable.MAILBOX_ID, bindMarker(CassandraMailboxRecentsTable.MAILBOX_ID))
+                .value(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID, bindMarker(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID)));
+    }
+
+    public CompletableFuture<List<MessageUid>> getRecentMessageUidsInMailbox(CassandraId mailboxId) {
+        return cassandraAsyncExecutor.execute(bindWithMailbox(mailboxId, readStatement))
+            .thenApply(CassandraUtils::convertToStream)
+            .thenApply(stream -> stream.map(row -> row.getLong(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID)))
+            .thenApply(stream -> stream.map(MessageUid::of))
+            .thenApply(stream -> stream.collect(Guavate.toImmutableList()));
+    }
+
+    private BoundStatement bindWithMailbox(CassandraId mailboxId, PreparedStatement statement) {
+        return statement.bind()
+            .setUUID(CassandraMailboxRecentsTable.MAILBOX_ID, mailboxId.asUuid());
+    }
+
+    public CompletableFuture<Void> removeFromRecent(CassandraId mailboxId, MessageUid messageUid) {
+        return cassandraAsyncExecutor.executeVoid(deleteStatement.bind()
+            .setUUID(CassandraMailboxRecentsTable.MAILBOX_ID, mailboxId.asUuid())
+            .setLong(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID, messageUid.asLong()));
+    }
+
+    public CompletableFuture<Void> addToRecent(CassandraId mailboxId, MessageUid messageUid) {
+        return cassandraAsyncExecutor.executeVoid(addStatement.bind()
+            .setUUID(CassandraMailboxRecentsTable.MAILBOX_ID, mailboxId.asUuid())
+            .setLong(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID, messageUid.asLong()));
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageIdMapper.java
----------------------------------------------------------------------
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 b4e0474..41d8749 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
@@ -69,18 +69,20 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
     private final CassandraMessageIdDAO messageIdDAO;
     private final CassandraMessageDAO messageDAO;
     private final CassandraMailboxCounterDAO mailboxCounterDAO;
+    private final CassandraMailboxRecentsDAO mailboxRecentsDAO;
     private final ModSeqProvider modSeqProvider;
     private final MailboxSession mailboxSession;
 
     public CassandraMessageIdMapper(MailboxMapper mailboxMapper, AttachmentMapper attachmentMapper,
                                     CassandraMessageIdToImapUidDAO imapUidDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageDAO messageDAO,
-                                    CassandraMailboxCounterDAO cassandraMailboxCounterDAO, ModSeqProvider modSeqProvider, MailboxSession mailboxSession) {
+                                    CassandraMailboxCounterDAO cassandraMailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentsDAO, ModSeqProvider modSeqProvider, MailboxSession mailboxSession) {
         this.mailboxMapper = mailboxMapper;
         this.attachmentMapper = attachmentMapper;
         this.imapUidDAO = imapUidDAO;
         this.messageIdDAO = messageIdDAO;
         this.messageDAO = messageDAO;
         this.mailboxCounterDAO = cassandraMailboxCounterDAO;
+        this.mailboxRecentsDAO = mailboxRecentsDAO;
         this.modSeqProvider = modSeqProvider;
         this.mailboxSession = mailboxSession;
     }
@@ -149,6 +151,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
                 imapUidDAO.insert(composedMessageIdWithMetaData),
                 messageIdDAO.insert(composedMessageIdWithMetaData)))
             .thenCompose(voidValue -> CompletableFuture.allOf(
+                mailboxRecentsDAO.addToRecent(mailboxId, mailboxMessage.getUid()),
                 mailboxCounterDAO.incrementCount(mailboxId),
                 incrementUnseenOnSave(mailboxId, mailboxMessage.createFlags())))
             .join();
@@ -196,6 +199,7 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
             messageIdDAO.delete(mailboxId, metaData.getComposedMessageId().getUid()))
             .thenCompose(voidValue -> CompletableFuture.allOf(
                 mailboxCounterDAO.decrementCount(mailboxId),
+                mailboxRecentsDAO.removeFromRecent(mailboxId, metaData.getComposedMessageId().getUid()),
                 decrementUnseenOnDelete(mailboxId, metaData.getFlags())));
     }
 
@@ -243,20 +247,31 @@ public class CassandraMessageIdMapper implements MessageIdMapper {
     private CompletableFuture<Pair<MailboxId, UpdatedFlags>> updateCounts(Pair<MailboxId, UpdatedFlags> pair) {
         CassandraId cassandraId = (CassandraId) pair.getLeft();
         return CompletableFuture.allOf(
-            incrementCountIfNeeded(pair.getRight().getOldFlags(), pair.getRight().getNewFlags(), cassandraId),
-            decrementCountIfNeeded(pair.getRight().getOldFlags(), pair.getRight().getNewFlags(), cassandraId))
+            manageRecent(pair.getRight(), cassandraId),
+            incrementCountIfNeeded(pair.getRight(), cassandraId),
+            decrementCountIfNeeded(pair.getRight(), cassandraId))
             .thenApply(voidValue -> pair);
     }
 
-    private CompletableFuture<Void> incrementCountIfNeeded(Flags oldFlags, Flags newFlags, CassandraId cassandraId) {
-        if (oldFlags.contains(Flags.Flag.SEEN) && !newFlags.contains(Flags.Flag.SEEN)) {
+    private CompletableFuture<Void> manageRecent(UpdatedFlags updatedFlags, CassandraId cassandraId) {
+        if (updatedFlags.isSet(Flags.Flag.RECENT)) {
+            return mailboxRecentsDAO.addToRecent(cassandraId, updatedFlags.getUid());
+        }
+        if (updatedFlags.isUnset(Flags.Flag.RECENT)){
+            return mailboxRecentsDAO.removeFromRecent(cassandraId, updatedFlags.getUid());
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
+    private CompletableFuture<Void> incrementCountIfNeeded(UpdatedFlags updatedFlags, CassandraId cassandraId) {
+        if (updatedFlags.isUnset(Flags.Flag.SEEN)) {
             return mailboxCounterDAO.incrementUnseen(cassandraId);
         }
         return CompletableFuture.completedFuture(null);
     }
 
-    private CompletableFuture<Void> decrementCountIfNeeded(Flags oldFlags, Flags newFlags, CassandraId cassandraId) {
-        if (!oldFlags.contains(Flags.Flag.SEEN) && newFlags.contains(Flags.Flag.SEEN)) {
+    private CompletableFuture<Void> decrementCountIfNeeded(UpdatedFlags updatedFlags,  CassandraId cassandraId) {
+        if (updatedFlags.isSet(Flags.Flag.SEEN)) {
             return mailboxCounterDAO.decrementUnseen(cassandraId);
         }
         return CompletableFuture.completedFuture(null);

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMessageMapper.java
----------------------------------------------------------------------
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 79763ee..38d27b8 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
@@ -75,10 +75,12 @@ public class CassandraMessageMapper implements MessageMapper {
     private final CassandraMessageIdDAO messageIdDAO;
     private final CassandraMessageIdToImapUidDAO imapUidDAO;
     private final CassandraMailboxCounterDAO mailboxCounterDAO;
+    private final CassandraMailboxRecentsDAO mailboxRecentDAO;
 
     public CassandraMessageMapper(UidProvider uidProvider, ModSeqProvider modSeqProvider,
                                   MailboxSession mailboxSession, int maxRetries, AttachmentMapper attachmentMapper,
-                                  CassandraMessageDAO messageDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO, CassandraMailboxCounterDAO mailboxCounterDAO) {
+                                  CassandraMessageDAO messageDAO, CassandraMessageIdDAO messageIdDAO, CassandraMessageIdToImapUidDAO imapUidDAO,
+                                  CassandraMailboxCounterDAO mailboxCounterDAO, CassandraMailboxRecentsDAO mailboxRecentDAO) {
         this.uidProvider = uidProvider;
         this.modSeqProvider = modSeqProvider;
         this.mailboxSession = mailboxSession;
@@ -88,6 +90,7 @@ public class CassandraMessageMapper implements MessageMapper {
         this.messageIdDAO = messageIdDAO;
         this.imapUidDAO = imapUidDAO;
         this.mailboxCounterDAO = mailboxCounterDAO;
+        this.mailboxRecentDAO = mailboxRecentDAO;
     }
 
     @Override
@@ -123,6 +126,7 @@ public class CassandraMessageMapper implements MessageMapper {
             imapUidDAO.delete(messageId, mailboxId),
             messageIdDAO.delete(mailboxId, uid)
         ).thenCompose(voidValue -> CompletableFuture.allOf(
+            removeRecentOnDelete(mailboxId, composedMessageIdWithMetaData.getFlags(), composedMessageId.getUid()),
             mailboxCounterDAO.decrementCount(mailboxId),
             decrementUnseenOnDelete(mailboxId, composedMessageIdWithMetaData.getFlags())));
     }
@@ -134,6 +138,14 @@ public class CassandraMessageMapper implements MessageMapper {
         return mailboxCounterDAO.decrementUnseen(mailboxId);
     }
 
+    private CompletableFuture<Void> removeRecentOnDelete(CassandraId mailboxId, Flags flags, MessageUid uid) {
+        if (flags.contains(Flag.RECENT)) {
+            return mailboxRecentDAO.removeFromRecent(mailboxId, uid);
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
+
     private CompletableFuture<Optional<ComposedMessageIdWithMetaData>> retrieveMessageId(CassandraId mailboxId, MailboxMessage message) {
         return messageIdDAO.retrieve(mailboxId, message.getUid());
     }
@@ -167,13 +179,8 @@ public class CassandraMessageMapper implements MessageMapper {
     @Override
     public List<MessageUid> findRecentMessageUidsInMailbox(Mailbox mailbox) throws MailboxException {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
-        return retrieveMessages(retrieveMessageIds(mailboxId, MessageRange.all()), FetchType.Metadata, Optional.empty())
-                .filter(MailboxMessage::isRecent)
-                .flatMap(message -> imapUidDAO.retrieve((CassandraMessageId) message.getMessageId(), Optional.ofNullable(mailboxId)).join())
-                .map(ComposedMessageIdWithMetaData::getComposedMessageId)
-                .map(ComposedMessageId::getUid)
-                .sorted()
-                .collect(Collectors.toList());
+        return mailboxRecentDAO.getRecentMessageUidsInMailbox(mailboxId)
+                .join();
     }
 
     @Override
@@ -225,6 +232,7 @@ public class CassandraMessageMapper implements MessageMapper {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
         save(mailbox, message)
             .thenCompose(voidValue -> CompletableFuture.allOf(
+                addRecentOnSave(mailboxId, message),
                 incrementUnseenOnSave(mailboxId, message.createFlags()),
                 mailboxCounterDAO.incrementCount(mailboxId)))
             .join();
@@ -238,12 +246,19 @@ public class CassandraMessageMapper implements MessageMapper {
         return mailboxCounterDAO.incrementUnseen(mailboxId);
     }
 
+    private CompletableFuture<Void> addRecentOnSave(CassandraId mailboxId, MailboxMessage message) {
+        if (message.createFlags().contains(Flag.RECENT)) {
+            return mailboxRecentDAO.addToRecent(mailboxId, message.getUid());
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
     @Override
     public Iterator<UpdatedFlags> updateFlags(Mailbox mailbox, FlagsUpdateCalculator flagUpdateCalculator, MessageRange set) throws MailboxException {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
         return retrieveMessages(retrieveMessageIds(mailboxId, set), FetchType.Metadata, Optional.empty())
                 .flatMap(message -> updateFlagsOnMessage(mailbox, flagUpdateCalculator, message))
-                .map((UpdatedFlags updatedFlags) -> manageUnseenMessageCounts(mailbox, updatedFlags.getOldFlags(), updatedFlags.getNewFlags())
+                .map((UpdatedFlags updatedFlags) -> manageCounters(mailbox, updatedFlags)
                     .thenApply(voidValue -> updatedFlags))
                 .map(CompletableFuture::join)
                 .collect(Collectors.toList()) // This collect is here as we need to consume all the stream before returning result
@@ -282,17 +297,33 @@ public class CassandraMessageMapper implements MessageMapper {
                 imapUidDAO.insert(composedMessageIdWithMetaData));
     }
 
-    private CompletableFuture<Void> manageUnseenMessageCounts(Mailbox mailbox, Flags oldFlags, Flags newFlags) {
+    private CompletableFuture<Void> manageCounters(Mailbox mailbox, UpdatedFlags updatedFlags) {
+        return CompletableFuture.allOf(manageUnseenMessageCounts(mailbox, updatedFlags),
+                manageRecents(mailbox, updatedFlags));
+    }
+
+    private CompletableFuture<Void> manageUnseenMessageCounts(Mailbox mailbox, UpdatedFlags updatedFlags) {
         CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
-        if (oldFlags.contains(Flag.SEEN) && !newFlags.contains(Flag.SEEN)) {
+        if (updatedFlags.isUnset(Flag.SEEN)) {
             return mailboxCounterDAO.incrementUnseen(mailboxId);
         }
-        if (!oldFlags.contains(Flag.SEEN) && newFlags.contains(Flag.SEEN)) {
+        if (updatedFlags.isSet(Flag.SEEN)) {
             return mailboxCounterDAO.decrementUnseen(mailboxId);
         }
         return CompletableFuture.completedFuture(null);
     }
 
+    private CompletableFuture<Void> manageRecents(Mailbox mailbox, UpdatedFlags updatedFlags) {
+        CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
+        if (updatedFlags.isUnset(Flag.RECENT)) {
+            return mailboxRecentDAO.removeFromRecent(mailboxId, updatedFlags.getUid());
+        }
+        if (updatedFlags.isSet(Flag.RECENT)) {
+            return mailboxRecentDAO.addToRecent(mailboxId, updatedFlags.getUid());
+        }
+        return CompletableFuture.completedFuture(null);
+    }
+
     private Stream<UpdatedFlags> updateFlagsOnMessage(Mailbox mailbox, FlagsUpdateCalculator flagUpdateCalculator, MailboxMessage message) {
         return tryMessageFlagsUpdate(flagUpdateCalculator, mailbox, message)
             .map(Stream::of)

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxRecentsModule.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxRecentsModule.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxRecentsModule.java
new file mode 100644
index 0000000..6cccbd8
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/modules/CassandraMailboxRecentsModule.java
@@ -0,0 +1,68 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.modules;
+
+import static com.datastax.driver.core.DataType.bigint;
+import static com.datastax.driver.core.DataType.list;
+import static com.datastax.driver.core.DataType.timeuuid;
+
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.james.backends.cassandra.components.CassandraIndex;
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.components.CassandraTable;
+import org.apache.james.backends.cassandra.components.CassandraType;
+import org.apache.james.mailbox.cassandra.table.CassandraMailboxRecentsTable;
+
+import com.datastax.driver.core.schemabuilder.SchemaBuilder;
+
+public class CassandraMailboxRecentsModule implements CassandraModule {
+
+    private final List<CassandraTable> tables;
+    private final List<CassandraIndex> index;
+    private final List<CassandraType> types;
+
+    public CassandraMailboxRecentsModule() {
+        tables = Collections.singletonList(
+            new CassandraTable(CassandraMailboxRecentsTable.TABLE_NAME,
+                SchemaBuilder.createTable(CassandraMailboxRecentsTable.TABLE_NAME)
+                    .ifNotExists()
+                    .addPartitionKey(CassandraMailboxRecentsTable.MAILBOX_ID, timeuuid())
+                    .addClusteringColumn(CassandraMailboxRecentsTable.RECENT_MESSAGE_UID, bigint())));
+        index = Collections.emptyList();
+        types = Collections.emptyList();
+    }
+
+    @Override
+    public List<CassandraTable> moduleTables() {
+        return tables;
+    }
+
+    @Override
+    public List<CassandraIndex> moduleIndex() {
+        return index;
+    }
+
+    @Override
+    public List<CassandraType> moduleTypes() {
+        return types;
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxRecentsTable.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxRecentsTable.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxRecentsTable.java
new file mode 100644
index 0000000..d729ae4
--- /dev/null
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/table/CassandraMailboxRecentsTable.java
@@ -0,0 +1,26 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.table;
+
+public interface CassandraMailboxRecentsTable {
+    String TABLE_NAME = "mailboxRecents";
+    String MAILBOX_ID = "mailboxId";
+    String RECENT_MESSAGE_UID = "recent_mesage_uid";
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
index 633bfbc..caadf4b 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraMailboxManagerTest.java
@@ -24,6 +24,8 @@ import org.apache.james.mailbox.acl.GroupMembershipResolver;
 import org.apache.james.mailbox.acl.MailboxACLResolver;
 import org.apache.james.mailbox.acl.SimpleGroupMembershipResolver;
 import org.apache.james.mailbox.acl.UnionMailboxACLResolver;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
@@ -34,6 +36,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraSubscriptionModule;
@@ -60,6 +63,7 @@ public class CassandraMailboxManagerTest {
         new CassandraMailboxModule(),
         new CassandraMessageModule(),
         new CassandraMailboxCounterModule(),
+        new CassandraMailboxRecentsModule(),
         new CassandraUidModule(),
         new CassandraModSeqModule(),
         new CassandraSubscriptionModule(),
@@ -77,13 +81,18 @@ public class CassandraMailboxManagerTest {
             CassandraMessageIdDAO messageIdDAO = new CassandraMessageIdDAO(CASSANDRA.getConf(), messageIdFactory);
             CassandraMessageIdToImapUidDAO imapUidDAO = new CassandraMessageIdToImapUidDAO(CASSANDRA.getConf(), messageIdFactory);
             CassandraMessageDAO messageDAO = new CassandraMessageDAO(CASSANDRA.getConf(), CASSANDRA.getTypesProvider(), messageIdFactory);
+            CassandraMailboxCounterDAO mailboxCounterDAO = new CassandraMailboxCounterDAO(CASSANDRA.getConf());
+            CassandraMailboxRecentsDAO mailboxRecentsDAO = new CassandraMailboxRecentsDAO(CASSANDRA.getConf());
+
             CassandraMailboxSessionMapperFactory mapperFactory = new CassandraMailboxSessionMapperFactory(uidProvider,
                 modSeqProvider,
                 CASSANDRA.getConf(),
                 CASSANDRA.getTypesProvider(),
                 messageDAO,
                 messageIdDAO,
-                imapUidDAO);
+                imapUidDAO,
+                mailboxCounterDAO,
+                mailboxRecentsDAO);
 
             MailboxACLResolver aclResolver = new UnionMailboxACLResolver();
             GroupMembershipResolver groupMembershipResolver = new SimpleGroupMembershipResolver();

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
index f253f53..5d5e6c1 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraSubscriptionManagerTest.java
@@ -23,6 +23,8 @@ import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.init.CassandraModuleComposite;
 import org.apache.james.mailbox.AbstractSubscriptionManagerTest;
 import org.apache.james.mailbox.SubscriptionManager;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
@@ -45,6 +47,8 @@ public class CassandraSubscriptionManagerTest extends AbstractSubscriptionManage
         CassandraMessageIdToImapUidDAO imapUidDAO = null;
         CassandraMessageDAO messageDAO = null;
         CassandraMessageIdDAO messageIdDAO = null;
+        CassandraMailboxCounterDAO mailboxCounterDAO = null;
+        CassandraMailboxRecentsDAO mailboxRecentsDAO = null;
         return new CassandraSubscriptionManager(
             new CassandraMailboxSessionMapperFactory(
                 new CassandraUidProvider(cassandra.getConf()),
@@ -53,7 +57,9 @@ public class CassandraSubscriptionManagerTest extends AbstractSubscriptionManage
                 cassandra.getTypesProvider(),
                 messageDAO,
                 messageIdDAO,
-                imapUidDAO
+                imapUidDAO,
+                mailboxCounterDAO,
+                mailboxRecentsDAO
             )
         );
     }

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
index fdf3abe..0999c8e 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/CassandraTestSystemFixture.java
@@ -23,6 +23,8 @@ import static org.mockito.Mockito.mock;
 
 import org.apache.james.backends.cassandra.CassandraCluster;
 import org.apache.james.backends.cassandra.init.CassandraModuleComposite;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
@@ -33,6 +35,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraUidModule;
@@ -50,6 +53,7 @@ public class CassandraTestSystemFixture {
         new CassandraMailboxModule(),
         new CassandraMessageModule(),
         new CassandraMailboxCounterModule(),
+        new CassandraMailboxRecentsModule(),
         new CassandraUidModule(),
         new CassandraModSeqModule(),
         new CassandraAttachmentModule(),
@@ -64,13 +68,17 @@ public class CassandraTestSystemFixture {
         CassandraMessageIdDAO messageIdDAO = new CassandraMessageIdDAO(CASSANDRA.getConf(), messageIdFactory);
         CassandraMessageIdToImapUidDAO imapUidDAO = new CassandraMessageIdToImapUidDAO(CASSANDRA.getConf(), messageIdFactory);
         CassandraMessageDAO messageDAO = new CassandraMessageDAO(CASSANDRA.getConf(), CASSANDRA.getTypesProvider(), messageIdFactory);
+        CassandraMailboxCounterDAO mailboxCounterDAO = new CassandraMailboxCounterDAO(CASSANDRA.getConf());
+        CassandraMailboxRecentsDAO mailboxRecentsDAO = new CassandraMailboxRecentsDAO(CASSANDRA.getConf());
         return new CassandraMailboxSessionMapperFactory(uidProvider,
             modSeqProvider,
             CASSANDRA.getConf(),
             CASSANDRA.getTypesProvider(),
             messageDAO,
             messageIdDAO,
-            imapUidDAO);
+            imapUidDAO,
+            mailboxCounterDAO,
+            mailboxRecentsDAO);
     }
 
     public static CassandraMailboxManager createMailboxManager(CassandraMailboxSessionMapperFactory mapperFactory) throws Exception{

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java
index b923c1f..93228aa 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxManagerAttachmentTest.java
@@ -32,6 +32,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraAclModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraUidModule;
@@ -47,6 +48,7 @@ public class CassandraMailboxManagerAttachmentTest extends AbstractMailboxManage
             new CassandraMailboxModule(),
             new CassandraMessageModule(),
             new CassandraMailboxCounterModule(),
+            new CassandraMailboxRecentsModule(),
             new CassandraModSeqModule(),
             new CassandraUidModule(),
             new CassandraAttachmentModule()));
@@ -64,7 +66,9 @@ public class CassandraMailboxManagerAttachmentTest extends AbstractMailboxManage
                 cassandra.getTypesProvider(),
                 new CassandraMessageDAO(cassandra.getConf(), cassandra.getTypesProvider(), messageIdFactory),
                 new CassandraMessageIdDAO(cassandra.getConf(), messageIdFactory),
-                new CassandraMessageIdToImapUidDAO(cassandra.getConf(), messageIdFactory));
+                new CassandraMessageIdToImapUidDAO(cassandra.getConf(), messageIdFactory),
+                new CassandraMailboxCounterDAO(cassandra.getConf()),
+                new CassandraMailboxRecentsDAO(cassandra.getConf()));
         Authenticator noAuthenticator = null;
         mailboxManager = new CassandraMailboxManager(mailboxSessionMapperFactory, noAuthenticator, new NoMailboxPathLocker(), new MessageParser(), messageIdFactory); 
         mailboxManager.init();

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentDAOTest.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentDAOTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentDAOTest.java
new file mode 100644
index 0000000..81bb313
--- /dev/null
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxRecentDAOTest.java
@@ -0,0 +1,107 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.mailbox.cassandra.mail;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+import org.apache.james.backends.cassandra.CassandraCluster;
+import org.apache.james.mailbox.MessageUid;
+import org.apache.james.mailbox.cassandra.CassandraId;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CassandraMailboxRecentDAOTest {
+    public static final MessageUid UID1 = MessageUid.of(36L);
+    public static final MessageUid UID2 = MessageUid.of(37L);
+    public static final CassandraId CASSANDRA_ID = CassandraId.timeBased();
+
+    private CassandraCluster cassandra;
+    private CassandraMailboxRecentsDAO testee;
+
+    @Before
+    public void setUp() {
+        cassandra = CassandraCluster.create(new CassandraMailboxRecentsModule());
+        cassandra.ensureAllTables();
+
+        testee = new CassandraMailboxRecentsDAO(cassandra.getConf());
+    }
+
+    @After
+    public void tearDown() {
+        cassandra.clearAllTables();
+    }
+
+    @Test
+    public void getRecentMessageUidsInMailboxShouldBeEmptyByDefault() throws Exception {
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).isEmpty();
+    }
+
+    @Test
+    public void addToRecentShouldAddUidWhenEmpty() throws Exception {
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).containsOnly(UID1);
+    }
+
+    @Test
+    public void removeFromRecentShouldRemoveUidWhenOnlyOneUid() throws Exception {
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+
+        testee.removeFromRecent(CASSANDRA_ID, UID1).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).isEmpty();
+    }
+
+    @Test
+    public void removeFromRecentShouldNotFailIfNotExisting() throws Exception {
+        testee.removeFromRecent(CASSANDRA_ID, UID1).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).isEmpty();
+    }
+
+    @Test
+    public void addToRecentShouldAddUidWhenNotEmpty() throws Exception {
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+
+        testee.addToRecent(CASSANDRA_ID, UID2).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).containsOnly(UID1, UID2);
+    }
+
+    @Test
+    public void removeFromRecentShouldOnlyRemoveUidWhenNotEmpty() throws Exception {
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+        testee.addToRecent(CASSANDRA_ID, UID2).join();
+
+        testee.removeFromRecent(CASSANDRA_ID, UID2).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).containsOnly(UID1);
+    }
+
+    @Test
+    public void addToRecentShouldBeIdempotent() throws Exception {
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+        testee.addToRecent(CASSANDRA_ID, UID1).join();
+
+        assertThat(testee.getRecentMessageUidsInMailbox(CASSANDRA_ID).join()).containsOnly(UID1);
+    }
+}

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java
----------------------------------------------------------------------
diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java
index 2516944..ba39d49 100644
--- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java
+++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/CassandraMapperProvider.java
@@ -33,6 +33,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraUidModule;
@@ -59,6 +60,7 @@ public class CassandraMapperProvider implements MapperProvider {
         new CassandraMailboxModule(),
         new CassandraMessageModule(),
         new CassandraMailboxCounterModule(),
+        new CassandraMailboxRecentsModule(),
         new CassandraModSeqModule(),
         new CassandraUidModule(),
         new CassandraAttachmentModule(),
@@ -100,7 +102,9 @@ public class CassandraMapperProvider implements MapperProvider {
             cassandra.getTypesProvider(),
             new CassandraMessageDAO(cassandra.getConf(), cassandra.getTypesProvider(), MESSAGE_ID_FACTORY),
             new CassandraMessageIdDAO(cassandra.getConf(), MESSAGE_ID_FACTORY),
-            new CassandraMessageIdToImapUidDAO(cassandra.getConf(), MESSAGE_ID_FACTORY));
+            new CassandraMessageIdToImapUidDAO(cassandra.getConf(), MESSAGE_ID_FACTORY),
+            new CassandraMailboxCounterDAO(cassandra.getConf()),
+            new CassandraMailboxRecentsDAO(cassandra.getConf()));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java
----------------------------------------------------------------------
diff --git a/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java b/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java
index 16ef829..3b8b46c 100644
--- a/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java
+++ b/mpt/impl/imap-mailbox/cassandra/src/test/java/org/apache/james/mpt/imapmailbox/cassandra/host/CassandraHostSystem.java
@@ -29,6 +29,8 @@ import org.apache.james.mailbox.SubscriptionManager;
 import org.apache.james.mailbox.cassandra.CassandraMailboxManager;
 import org.apache.james.mailbox.cassandra.CassandraMailboxSessionMapperFactory;
 import org.apache.james.mailbox.cassandra.CassandraMessageId;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxCounterDAO;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxRecentsDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdDAO;
 import org.apache.james.mailbox.cassandra.mail.CassandraMessageIdToImapUidDAO;
@@ -39,6 +41,7 @@ import org.apache.james.mailbox.cassandra.modules.CassandraAnnotationModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraAttachmentModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule;
+import org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraMessageModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraModSeqModule;
 import org.apache.james.mailbox.cassandra.modules.CassandraQuotaModule;
@@ -78,6 +81,7 @@ public class CassandraHostSystem extends JamesImapHostSystem {
             new CassandraMailboxModule(),
             new CassandraMessageModule(),
             new CassandraMailboxCounterModule(),
+            new CassandraMailboxRecentsModule(),
             new CassandraUidModule(),
             new CassandraModSeqModule(),
             new CassandraSubscriptionModule(),
@@ -94,9 +98,11 @@ public class CassandraHostSystem extends JamesImapHostSystem {
         CassandraMessageDAO messageDAO = new CassandraMessageDAO(session, typesProvider, messageIdFactory);
         CassandraMessageIdDAO messageIdDAO = new CassandraMessageIdDAO(session, messageIdFactory);
         CassandraMessageIdToImapUidDAO imapUidDAO = new CassandraMessageIdToImapUidDAO(session, messageIdFactory);
+        CassandraMailboxCounterDAO mailboxCounterDAO = new CassandraMailboxCounterDAO(session);
+        CassandraMailboxRecentsDAO mailboxRecentsDAO = new CassandraMailboxRecentsDAO(session);
 
         CassandraMailboxSessionMapperFactory mapperFactory = new CassandraMailboxSessionMapperFactory(uidProvider, modSeqProvider, 
-                session, typesProvider, messageDAO, messageIdDAO, imapUidDAO);
+                session, typesProvider, messageDAO, messageIdDAO, imapUidDAO, mailboxCounterDAO, mailboxRecentsDAO);
         
         mailboxManager = new CassandraMailboxManager(mapperFactory, userManager, new JVMMailboxPathLocker(), new MessageParser(), messageIdFactory); 
         QuotaRootResolver quotaRootResolver = new DefaultQuotaRootResolver(mapperFactory);

http://git-wip-us.apache.org/repos/asf/james-project/blob/c8c12826/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
----------------------------------------------------------------------
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
index 7ad3a28..df0693f 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraMailboxModule.java
@@ -98,6 +98,7 @@ public class CassandraMailboxModule extends AbstractModule {
         Multibinder<CassandraModule> cassandraDataDefinitions = Multibinder.newSetBinder(binder(), CassandraModule.class);
         cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraAclModule.class);
         cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule.class);
+        cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraMailboxRecentsModule.class);
         cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraMailboxModule.class);
         cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraMessageModule.class);
         cassandraDataDefinitions.addBinding().to(org.apache.james.mailbox.cassandra.modules.CassandraSubscriptionModule.class);


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