Skip to content

Commit

Permalink
JAMES-1874 No need of two reads for COUNT and UNSEEN
Browse files Browse the repository at this point in the history
  • Loading branch information
chibenwa committed Feb 20, 2017
1 parent 7732108 commit 052a344
Show file tree
Hide file tree
Showing 16 changed files with 264 additions and 12 deletions.
Expand Up @@ -31,6 +31,7 @@
import org.apache.james.mailbox.exception.UnsupportedCriteriaException;
import org.apache.james.mailbox.model.ComposedMessageId;
import org.apache.james.mailbox.model.MailboxACL;
import org.apache.james.mailbox.model.MailboxCounters;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.model.MessageRange;
Expand All @@ -39,6 +40,8 @@
import org.apache.james.mailbox.model.MessageResultIterator;
import org.apache.james.mailbox.model.SearchQuery;

import com.google.common.base.Objects;

/**
* Interface which represent a Mailbox
*
Expand All @@ -60,7 +63,7 @@ enum FlagsUpdateMode {
/**
* Return the count of unseen messages in the mailbox
*/
long getUnseenMessageCount(MailboxSession mailboxSession) throws MailboxException;
MailboxCounters getMailboxCounters(MailboxSession mailboxSession) throws MailboxException;

/**
* Return if the Mailbox is writable
Expand Down
@@ -0,0 +1,84 @@
/****************************************************************
* 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.model;

import com.google.common.base.Objects;
import com.google.common.base.Optional;
import com.google.common.base.Preconditions;

public class MailboxCounters {

public static class Builder {
private Optional<Long> count = Optional.absent();
private Optional<Long> unseen = Optional.absent();

public Builder count(long count) {
this.count = Optional.of(count);
return this;
}

public Builder unseen(long unseen) {
this.unseen = Optional.of(unseen);
return this;
}

public MailboxCounters build() {
Preconditions.checkState(count.isPresent(), "count is compulsory");
Preconditions.checkState(unseen.isPresent(), "unseen is compulsory");
return new MailboxCounters(count.get(), unseen.get());
}
}

public static Builder builder() {
return new Builder();
}

private final long count;
private final long unseen;

private MailboxCounters(long count, long unseen) {
this.count = count;
this.unseen = unseen;
}

public long getCount() {
return count;
}

public long getUnseen() {
return unseen;
}

@Override
public final boolean equals(Object o) {
if (o instanceof MailboxCounters) {
MailboxCounters that = (MailboxCounters) o;

return Objects.equal(this.count, that.count)
&& Objects.equal(this.unseen, that.unseen);
}
return false;
}

@Override
public final int hashCode() {
return Objects.hashCode(count, unseen);
}
}
@@ -0,0 +1,33 @@
/*
* 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.model;

import org.junit.Test;

import nl.jqno.equalsverifier.EqualsVerifier;

public class MailboxCountersTest {

@Test
public void mailboxCountersShouldRespectBeanContract() {
EqualsVerifier.forClass(MailboxCounters.class).verify();
}
}
Expand Up @@ -5,6 +5,7 @@

import org.apache.james.mailbox.MessageUid;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.MailboxCounters;
import org.apache.james.mailbox.model.MessageMetaData;
import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.model.UpdatedFlags;
Expand Down Expand Up @@ -67,6 +68,14 @@ public long countUnseenMessagesInMailbox(Mailbox mailbox)
return cache.countUnseenMessagesInMailbox(mailbox, underlying);
}

@Override
public MailboxCounters getMailboxCounters(Mailbox mailbox) throws MailboxException {
return MailboxCounters.builder()
.count(countMessagesInMailbox(mailbox))
.unseen(countUnseenMessagesInMailbox(mailbox))
.build();
}

@Override
public void delete(Mailbox mailbox, MailboxMessage message)
throws MailboxException {
Expand Down
Expand Up @@ -35,6 +35,7 @@
import org.apache.james.mailbox.cassandra.CassandraId;
import org.apache.james.mailbox.cassandra.table.CassandraMailboxCountersTable;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.MailboxCounters;
import org.apache.james.mailbox.store.mail.model.Mailbox;

import com.datastax.driver.core.BoundStatement;
Expand Down Expand Up @@ -75,6 +76,16 @@ private PreparedStatement updateMailboxStatement(Session session, Assignment ope
.where(eq(CassandraMailboxCountersTable.MAILBOX_ID, bindMarker(CassandraMailboxCountersTable.MAILBOX_ID))));
}

public CompletableFuture<Optional<MailboxCounters>> retrieveMailboxCounters(Mailbox mailbox) throws MailboxException {
CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();

return cassandraAsyncExecutor.executeSingleRow(bindWithMailbox(mailboxId, readStatement))
.thenApply(optional -> optional.map(row -> MailboxCounters.builder()
.count(row.getLong(CassandraMailboxCountersTable.COUNT))
.unseen(row.getLong(CassandraMailboxCountersTable.UNSEEN))
.build()));
}

public CompletableFuture<Optional<Long>> countMessagesInMailbox(Mailbox mailbox) throws MailboxException {
CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();

Expand Down
Expand Up @@ -42,6 +42,7 @@
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.ComposedMessageId;
import org.apache.james.mailbox.model.ComposedMessageIdWithMetaData;
import org.apache.james.mailbox.model.MailboxCounters;
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.MessageMetaData;
import org.apache.james.mailbox.model.MessageRange;
Expand All @@ -65,6 +66,10 @@

public class CassandraMessageMapper implements MessageMapper {
private static final Logger LOGGER = LoggerFactory.getLogger(CassandraMessageMapper.class);
public static final MailboxCounters INITIAL_COUNTERS = MailboxCounters.builder()
.count(0L)
.unseen(0L)
.build();

private final ModSeqProvider modSeqProvider;
private final MailboxSession mailboxSession;
Expand Down Expand Up @@ -109,6 +114,13 @@ public long countUnseenMessagesInMailbox(Mailbox mailbox) throws MailboxExceptio
.orElse(0L);
}

@Override
public MailboxCounters getMailboxCounters(Mailbox mailbox) throws MailboxException {
return mailboxCounterDAO.retrieveMailboxCounters(mailbox)
.join()
.orElse(INITIAL_COUNTERS);
}

@Override
public void delete(Mailbox mailbox, MailboxMessage message) {
CassandraId mailboxId = (CassandraId) mailbox.getMailboxId();
Expand Down
Expand Up @@ -21,9 +21,12 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.util.Optional;

import org.apache.james.backends.cassandra.CassandraCluster;
import org.apache.james.mailbox.cassandra.CassandraId;
import org.apache.james.mailbox.cassandra.modules.CassandraMailboxCounterModule;
import org.apache.james.mailbox.model.MailboxCounters;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.store.mail.model.impl.SimpleMailbox;
import org.junit.After;
Expand Down Expand Up @@ -63,6 +66,11 @@ public void countUnseenMessagesInMailboxShouldReturnEmptyByDefault() throws Exce
assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().isPresent()).isFalse();
}

@Test
public void retrieveMailboxCounterShouldReturnEmptyByDefault() throws Exception {
assertThat(testee.retrieveMailboxCounters(mailbox).join().isPresent()).isFalse();
}

@Test
public void incrementCountShouldAddOneWhenAbsent() throws Exception {
testee.incrementCount(MAILBOX_ID).join();
Expand All @@ -78,6 +86,46 @@ public void incrementUnseenShouldAddOneWhenAbsent() throws Exception {
assertThat(testee.countUnseenMessagesInMailbox(mailbox).join().get()).isEqualTo(1);
}

@Test
public void incrementUnseenShouldAddOneWhenAbsentOnMailboxCounters() throws Exception {
testee.incrementUnseen(MAILBOX_ID).join();

Optional<MailboxCounters> mailboxCounters = testee.retrieveMailboxCounters(mailbox).join();
assertThat(mailboxCounters.isPresent()).isTrue();
assertThat(mailboxCounters.get())
.isEqualTo(MailboxCounters.builder()
.count(0L)
.unseen(1L)
.build());
}

@Test
public void incrementCountShouldAddOneWhenAbsentOnMailboxCounters() throws Exception {
testee.incrementCount(MAILBOX_ID).join();

Optional<MailboxCounters> mailboxCounters = testee.retrieveMailboxCounters(mailbox).join();
assertThat(mailboxCounters.isPresent()).isTrue();
assertThat(mailboxCounters.get())
.isEqualTo(MailboxCounters.builder()
.count(1L)
.unseen(0L)
.build());
}

@Test
public void retrieveMailboxCounterShouldWorkWhenFullRow() throws Exception {
testee.incrementCount(MAILBOX_ID).join();
testee.incrementUnseen(MAILBOX_ID).join();

Optional<MailboxCounters> mailboxCounters = testee.retrieveMailboxCounters(mailbox).join();
assertThat(mailboxCounters.isPresent()).isTrue();
assertThat(mailboxCounters.get())
.isEqualTo(MailboxCounters.builder()
.count(1L)
.unseen(1L)
.build());
}

@Test
public void decrementCountShouldRemoveOne() throws Exception {
testee.incrementCount(MAILBOX_ID).join();
Expand Down
Expand Up @@ -68,6 +68,7 @@
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.hbase.HBaseId;
import org.apache.james.mailbox.hbase.io.ChunkOutputStream;
import org.apache.james.mailbox.model.MailboxCounters;
import org.apache.james.mailbox.model.MessageId;
import org.apache.james.mailbox.model.MessageId.Factory;
import org.apache.james.mailbox.model.MessageMetaData;
Expand Down Expand Up @@ -111,6 +112,14 @@ public HBaseMessageMapper(MailboxSession session,
this.conf = conf;
}

@Override
public MailboxCounters getMailboxCounters(Mailbox mailbox) throws MailboxException {
return MailboxCounters.builder()
.count(countMessagesInMailbox(mailbox))
.unseen(countUnseenMessagesInMailbox(mailbox))
.build();
}

@Override
public void endRequest() {
}
Expand Down
Expand Up @@ -38,6 +38,7 @@
import org.apache.james.mailbox.jpa.mail.model.openjpa.JPAEncryptedMailboxMessage;
import org.apache.james.mailbox.jpa.mail.model.openjpa.JPAMailboxMessage;
import org.apache.james.mailbox.jpa.mail.model.openjpa.JPAStreamingMailboxMessage;
import org.apache.james.mailbox.model.MailboxCounters;
import org.apache.james.mailbox.model.MessageMetaData;
import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.model.MessageRange.Type;
Expand Down Expand Up @@ -68,6 +69,14 @@ public JPAMessageMapper(MailboxSession mailboxSession, UidProvider uidProvider,
this.messageMetadataMapper = new MessageUtils(mailboxSession, uidProvider, modSeqProvider);
}

@Override
public MailboxCounters getMailboxCounters(Mailbox mailbox) throws MailboxException {
return MailboxCounters.builder()
.count(countMessagesInMailbox(mailbox))
.unseen(countUnseenMessagesInMailbox(mailbox))
.build();
}

/**
* @see org.apache.james.mailbox.store.mail.MessageMapper#findInMailbox(org.apache.james.mailbox.store.mail.model.Mailbox,
* org.apache.james.mailbox.model.MessageRange,
Expand Down
Expand Up @@ -26,6 +26,7 @@
import org.apache.commons.lang.NotImplementedException;
import org.apache.james.mailbox.MessageUid;
import org.apache.james.mailbox.exception.MailboxException;
import org.apache.james.mailbox.model.MailboxCounters;
import org.apache.james.mailbox.model.MessageMetaData;
import org.apache.james.mailbox.model.MessageRange;
import org.apache.james.mailbox.model.UpdatedFlags;
Expand All @@ -48,6 +49,14 @@ public void endRequest() {
throw new NotImplementedException();
}

@Override
public MailboxCounters getMailboxCounters(Mailbox mailbox) throws MailboxException {
return MailboxCounters.builder()
.count(countMessagesInMailbox(mailbox))
.unseen(countUnseenMessagesInMailbox(mailbox))
.build();
}

@Override
public <T> T execute(Transaction<T> transaction) throws MailboxException {
throw new NotImplementedException();
Expand Down
Expand Up @@ -54,6 +54,7 @@
import org.apache.james.mailbox.model.ComposedMessageId;
import org.apache.james.mailbox.model.MailboxACL;
import org.apache.james.mailbox.model.MailboxACL.MailboxACLRights;
import org.apache.james.mailbox.model.MailboxCounters;
import org.apache.james.mailbox.model.MailboxId;
import org.apache.james.mailbox.model.MailboxPath;
import org.apache.james.mailbox.model.MessageAttachment;
Expand Down Expand Up @@ -232,10 +233,10 @@ protected Flags getPermanentFlags(MailboxSession session) {
return new Flags(MINIMAL_PERMANET_FLAGS);
}


@Override
public long getUnseenMessageCount(MailboxSession mailboxSession) throws MailboxException {
return mapperFactory.createMessageMapper(mailboxSession)
.countUnseenMessagesInMailbox(mailbox);
public MailboxCounters getMailboxCounters(MailboxSession mailboxSession) throws MailboxException {
return mapperFactory.createMessageMapper(mailboxSession).getMailboxCounters(mailbox);
}

/**
Expand Down

0 comments on commit 052a344

Please sign in to comment.