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 2019/12/02 03:01:08 UTC

[james-project] 05/05: JAMES-2990 Force to use UUID in CassandraPreviewStore

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 089d792991629cd546bc5b03d023ebc5164ba9ce
Author: Tran Tien Duc <dt...@linagora.com>
AuthorDate: Fri Nov 29 15:14:40 2019 +0700

    JAMES-2990 Force to use UUID in CassandraPreviewStore
---
 server/data/data-jmap-cassandra/pom.xml            |  4 +
 .../preview/CassandraMessagePreviewModule.java     |  3 +-
 .../preview/CassandraMessagePreviewStore.java      | 20 ++++-
 .../preview/CassandraMessagePreviewStoreTest.java  | 34 ++++++++
 .../api/preview/MessagePreviewStoreContract.java   | 90 +++++++++++++---------
 .../preview/MemoryMessagePreviewStoreTest.java     |  9 +++
 6 files changed, 120 insertions(+), 40 deletions(-)

diff --git a/server/data/data-jmap-cassandra/pom.xml b/server/data/data-jmap-cassandra/pom.xml
index f02d792..058ca36 100644
--- a/server/data/data-jmap-cassandra/pom.xml
+++ b/server/data/data-jmap-cassandra/pom.xml
@@ -52,6 +52,10 @@
         </dependency>
         <dependency>
             <groupId>${james.groupId}</groupId>
+            <artifactId>apache-james-mailbox-cassandra</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>${james.groupId}</groupId>
             <artifactId>event-sourcing-event-store-cassandra</artifactId>
         </dependency>
         <dependency>
diff --git a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewModule.java b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewModule.java
index b67073b..c9b942a 100644
--- a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewModule.java
+++ b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewModule.java
@@ -20,6 +20,7 @@
 package org.apache.james.jmap.cassandra.preview;
 
 import static com.datastax.driver.core.DataType.text;
+import static com.datastax.driver.core.DataType.uuid;
 
 import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.utils.CassandraConstants;
@@ -34,7 +35,7 @@ public interface CassandraMessagePreviewModule {
             .caching(SchemaBuilder.KeyCaching.ALL,
                 SchemaBuilder.rows(CassandraConstants.DEFAULT_CACHED_ROW_PER_PARTITION)))
         .statement(statement -> statement
-            .addPartitionKey(CassandraMessagePreviewTable.MESSAGE_ID, text())
+            .addPartitionKey(CassandraMessagePreviewTable.MESSAGE_ID, uuid())
             .addColumn(CassandraMessagePreviewTable.PREVIEW, text()))
         .build();
 }
diff --git a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewStore.java b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewStore.java
index 59b0e0b..f97b024 100644
--- a/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewStore.java
+++ b/server/data/data-jmap-cassandra/src/main/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewStore.java
@@ -32,12 +32,14 @@ import javax.inject.Inject;
 import org.apache.james.backends.cassandra.utils.CassandraAsyncExecutor;
 import org.apache.james.jmap.api.preview.MessagePreviewStore;
 import org.apache.james.jmap.api.preview.Preview;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
 import org.apache.james.mailbox.model.MessageId;
 import org.reactivestreams.Publisher;
 
 import com.datastax.driver.core.PreparedStatement;
 import com.datastax.driver.core.Session;
 import com.datastax.driver.core.querybuilder.QueryBuilder;
+import com.google.common.base.Preconditions;
 
 public class CassandraMessagePreviewStore implements MessagePreviewStore {
 
@@ -66,22 +68,34 @@ public class CassandraMessagePreviewStore implements MessagePreviewStore {
 
     @Override
     public Publisher<Void> store(MessageId messageId, Preview preview) {
+        checkMessage(messageId);
+
         return cassandraAsyncExecutor.executeVoid(storeStatement.bind()
-            .setString(MESSAGE_ID, messageId.serialize())
+            .setUUID(MESSAGE_ID, ((CassandraMessageId) messageId).get())
             .setString(PREVIEW, preview.getValue()));
     }
 
     @Override
     public Publisher<Preview> retrieve(MessageId messageId) {
+        checkMessage(messageId);
+
         return cassandraAsyncExecutor.executeSingleRow(retrieveStatement.bind()
-                .setString(MESSAGE_ID, messageId.serialize()))
+                .setUUID(MESSAGE_ID, ((CassandraMessageId) messageId).get()))
             .map(row -> row.getString(PREVIEW))
             .map(Preview::from);
     }
 
     @Override
     public Publisher<Void> delete(MessageId messageId) {
+        checkMessage(messageId);
+
         return cassandraAsyncExecutor.executeVoid(deleteStatement.bind()
-            .setString(MESSAGE_ID, messageId.serialize()));
+            .setUUID(MESSAGE_ID, ((CassandraMessageId) messageId).get()));
+    }
+
+    private void checkMessage(MessageId messageId) {
+        Preconditions.checkNotNull(messageId);
+        Preconditions.checkArgument(messageId instanceof CassandraMessageId,
+            "MessageId type is required to be CassandraMessageId");
     }
 }
diff --git a/server/data/data-jmap-cassandra/src/test/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewStoreTest.java b/server/data/data-jmap-cassandra/src/test/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewStoreTest.java
index 26297c6..9369f39 100644
--- a/server/data/data-jmap-cassandra/src/test/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewStoreTest.java
+++ b/server/data/data-jmap-cassandra/src/test/java/org/apache/james/jmap/cassandra/preview/CassandraMessagePreviewStoreTest.java
@@ -19,10 +19,16 @@
 
 package org.apache.james.jmap.cassandra.preview;
 
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
 import org.apache.james.backends.cassandra.CassandraClusterExtension;
 import org.apache.james.jmap.api.preview.MessagePreviewStore;
 import org.apache.james.jmap.api.preview.MessagePreviewStoreContract;
+import org.apache.james.mailbox.cassandra.ids.CassandraMessageId;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.TestMessageId;
 import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 class CassandraMessagePreviewStoreTest implements MessagePreviewStoreContract {
@@ -31,9 +37,11 @@ class CassandraMessagePreviewStoreTest implements MessagePreviewStoreContract {
     static CassandraClusterExtension cassandra = new CassandraClusterExtension(CassandraMessagePreviewModule.MODULE);
 
     private CassandraMessagePreviewStore testee;
+    private CassandraMessageId.Factory cassandraMessageIdFactory;
 
     @BeforeEach
     void setUp() {
+        cassandraMessageIdFactory = new CassandraMessageId.Factory();
         testee = new CassandraMessagePreviewStore(cassandra.getCassandraCluster().getConf());
     }
 
@@ -41,4 +49,30 @@ class CassandraMessagePreviewStoreTest implements MessagePreviewStoreContract {
     public MessagePreviewStore testee() {
         return testee;
     }
+
+    @Override
+    public MessageId newMessageId() {
+        return cassandraMessageIdFactory.generate();
+    }
+
+    @Test
+    void storeShouldThrowWhenMessageIdIsNotCassandraType() {
+        assertThatThrownBy(() -> testee.store(TestMessageId.of(1), PREVIEW_1))
+            .isInstanceOf(IllegalArgumentException.class)
+            .hasMessage("MessageId type is required to be CassandraMessageId");
+    }
+
+    @Test
+    void retrieveShouldThrowWhenMessageIdIsNotCassandraType() {
+        assertThatThrownBy(() -> testee.retrieve(TestMessageId.of(1)))
+            .isInstanceOf(IllegalArgumentException.class)
+            .hasMessage("MessageId type is required to be CassandraMessageId");
+    }
+
+    @Test
+    void deleteShouldThrowWhenMessageIdIsNotCassandraType() {
+        assertThatThrownBy(() -> testee.retrieve(TestMessageId.of(1)))
+            .isInstanceOf(IllegalArgumentException.class)
+            .hasMessage("MessageId type is required to be CassandraMessageId");
+    }
 }
\ No newline at end of file
diff --git a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/preview/MessagePreviewStoreContract.java b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/preview/MessagePreviewStoreContract.java
index 297e0b6..bb8546f 100644
--- a/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/preview/MessagePreviewStoreContract.java
+++ b/server/data/data-jmap/src/test/java/org/apache/james/jmap/api/preview/MessagePreviewStoreContract.java
@@ -24,10 +24,10 @@ import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
 import java.time.Duration;
+import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.IntStream;
 
 import org.apache.james.mailbox.model.MessageId;
-import org.apache.james.mailbox.model.TestMessageId;
 import org.apache.james.util.concurrency.ConcurrentTestRunner;
 import org.assertj.core.api.SoftAssertions;
 import org.junit.jupiter.api.Test;
@@ -36,13 +36,13 @@ import reactor.core.publisher.Mono;
 
 public interface MessagePreviewStoreContract {
 
-    MessageId MESSAGE_ID_1 = TestMessageId.of(1);
-    Preview PREVIEW_1 = Preview.from("message id 1");
-    MessageId MESSAGE_ID_2 = TestMessageId.of(2);
-    Preview PREVIEW_2 = Preview.from("message id 2");
+    Preview PREVIEW_1 = Preview.from("preview 1");
+    Preview PREVIEW_2 = Preview.from("preview 2");
 
     MessagePreviewStore testee();
 
+    MessageId newMessageId();
+
     @Test
     default void retrieveShouldThrowWhenNullMessageId() {
         assertThatThrownBy(() -> Mono.from(testee().retrieve(null)).block())
@@ -51,33 +51,38 @@ public interface MessagePreviewStoreContract {
 
     @Test
     default void retrieveShouldReturnStoredPreview() {
-        Mono.from(testee().store(MESSAGE_ID_1, PREVIEW_1))
+        MessageId messageId = newMessageId();
+        Mono.from(testee().store(messageId, PREVIEW_1))
             .block();
 
-        assertThat(Mono.from(testee().retrieve(MESSAGE_ID_1)).block())
+        assertThat(Mono.from(testee().retrieve(messageId)).block())
             .isEqualTo(PREVIEW_1);
     }
 
     @Test
     default void retrieveShouldReturnEmptyWhenMessageIdNotFound() {
-        Mono.from(testee().store(MESSAGE_ID_1, PREVIEW_1))
+        MessageId messageId1 = newMessageId();
+        MessageId messageId2 = newMessageId();
+        Mono.from(testee().store(messageId1, PREVIEW_1))
             .block();
 
-        assertThat(Mono.from(testee().retrieve(MESSAGE_ID_2)).blockOptional())
+        assertThat(Mono.from(testee().retrieve(messageId2)).blockOptional())
             .isEmpty();
     }
 
     @Test
     default void retrieveShouldReturnTheRightPreviewWhenStoringMultipleMessageIds() {
-        Mono.from(testee().store(MESSAGE_ID_1, PREVIEW_1))
+        MessageId messageId1 = newMessageId();
+        MessageId messageId2 = newMessageId();
+        Mono.from(testee().store(messageId1, PREVIEW_1))
             .block();
-        Mono.from(testee().store(MESSAGE_ID_2, PREVIEW_2))
+        Mono.from(testee().store(messageId2, PREVIEW_2))
             .block();
 
         SoftAssertions.assertSoftly(softly -> {
-           softly.assertThat(Mono.from(testee().retrieve(MESSAGE_ID_1)).block())
+           softly.assertThat(Mono.from(testee().retrieve(messageId1)).block())
                .isEqualTo(PREVIEW_1);
-           softly.assertThat(Mono.from(testee().retrieve(MESSAGE_ID_2)).block())
+           softly.assertThat(Mono.from(testee().retrieve(messageId2)).block())
                .isEqualTo(PREVIEW_2);
         });
     }
@@ -90,31 +95,34 @@ public interface MessagePreviewStoreContract {
 
     @Test
     default void storeShouldThrowWhenNullPreview() {
-        assertThatThrownBy(() -> Mono.from(testee().store(MESSAGE_ID_1, null)).block())
+        MessageId messageId1 = newMessageId();
+        assertThatThrownBy(() -> Mono.from(testee().store(messageId1, null)).block())
             .isInstanceOf(NullPointerException.class);
     }
 
     @Test
     default void storeShouldOverrideOldRecord() {
-        Mono.from(testee().store(MESSAGE_ID_1, PREVIEW_1))
+        MessageId messageId1 = newMessageId();
+        Mono.from(testee().store(messageId1, PREVIEW_1))
             .block();
 
-        Mono.from(testee().store(MESSAGE_ID_1, PREVIEW_2))
+        Mono.from(testee().store(messageId1, PREVIEW_2))
             .block();
 
-        assertThat(Mono.from(testee().retrieve(MESSAGE_ID_1)).block())
+        assertThat(Mono.from(testee().retrieve(messageId1)).block())
             .isEqualTo(PREVIEW_2);
     }
 
     @Test
     default void storeShouldBeIdempotent() {
-        Mono.from(testee().store(MESSAGE_ID_1, PREVIEW_1))
+        MessageId messageId1 = newMessageId();
+        Mono.from(testee().store(messageId1, PREVIEW_1))
             .block();
 
-        Mono.from(testee().store(MESSAGE_ID_1, PREVIEW_1))
+        Mono.from(testee().store(messageId1, PREVIEW_1))
             .block();
 
-        assertThat(Mono.from(testee().retrieve(MESSAGE_ID_1)).block())
+        assertThat(Mono.from(testee().retrieve(messageId1)).block())
             .isEqualTo(PREVIEW_1);
     }
 
@@ -123,33 +131,38 @@ public interface MessagePreviewStoreContract {
         int threadCount = 10;
         int stepCount = 100;
 
+        ConcurrentHashMap<Integer, MessageId> messageIds = new ConcurrentHashMap<>();
+        IntStream.range(0, threadCount)
+            .forEach(thread -> messageIds.put(thread, newMessageId()));
+
         ConcurrentTestRunner.builder()
             .reactorOperation((thread, step) -> testee()
-                .store(TestMessageId.of(thread), Preview.from(String.valueOf(step))))
+                .store(messageIds.get(thread), Preview.from(String.valueOf(step))))
             .threadCount(threadCount)
             .operationCount(stepCount)
             .runSuccessfullyWithin(Duration.ofMinutes(1));
 
         IntStream.range(0, threadCount)
             .forEach(index -> assertThat(Mono.from(testee()
-                    .retrieve(TestMessageId.of(index)))
+                    .retrieve(messageIds.get(index)))
                     .block())
                 .isEqualTo(Preview.from(String.valueOf(stepCount - 1))));
     }
 
     @Test
     default void storeShouldBeConsistentUponSingleKeyOperation() throws Exception {
+        MessageId messageId = newMessageId();
         int threadCount = 10;
         int operationCount = 100;
 
         ConcurrentTestRunner.builder()
             .reactorOperation((thread, step) -> testee()
-                .store(MESSAGE_ID_1, Preview.from(String.valueOf(step * threadCount + thread))))
+                .store(messageId, Preview.from(String.valueOf(step * threadCount + thread))))
             .threadCount(threadCount)
             .operationCount(operationCount)
             .runSuccessfullyWithin(Duration.ofMinutes(1));
 
-        Preview preview = Mono.from(testee().retrieve(MESSAGE_ID_1)).block();
+        Preview preview = Mono.from(testee().retrieve(messageId)).block();
         Integer previewAsInt = Integer.valueOf(preview.getValue());
 
         assertThat(previewAsInt)
@@ -165,47 +178,52 @@ public interface MessagePreviewStoreContract {
 
     @Test
     default void deleteShouldNotThrowWhenMessageIdNotFound() {
-        assertThatCode(() -> Mono.from(testee().delete(MESSAGE_ID_1)).block())
+        MessageId messageId1 = newMessageId();
+        assertThatCode(() -> Mono.from(testee().delete(messageId1)).block())
             .doesNotThrowAnyException();
     }
 
     @Test
     default void deleteShouldDeleteStoredRecord() {
-        Mono.from(testee().store(MESSAGE_ID_1, PREVIEW_1))
+        MessageId messageId1 = newMessageId();
+        Mono.from(testee().store(messageId1, PREVIEW_1))
             .block();
 
-        Mono.from(testee().delete(MESSAGE_ID_1))
+        Mono.from(testee().delete(messageId1))
             .block();
 
-        assertThat(Mono.from(testee().retrieve(MESSAGE_ID_1)).blockOptional())
+        assertThat(Mono.from(testee().retrieve(messageId1)).blockOptional())
             .isEmpty();
     }
 
     @Test
     default void deleteShouldNotDeleteAnotherRecord() {
-        Mono.from(testee().store(MESSAGE_ID_1, PREVIEW_1))
+        MessageId messageId1 = newMessageId();
+        MessageId messageId2 = newMessageId();
+        Mono.from(testee().store(messageId1, PREVIEW_1))
             .block();
-        Mono.from(testee().store(MESSAGE_ID_2, PREVIEW_2))
+        Mono.from(testee().store(messageId2, PREVIEW_2))
             .block();
 
-        Mono.from(testee().delete(MESSAGE_ID_1))
+        Mono.from(testee().delete(messageId1))
             .block();
 
-        assertThat(Mono.from(testee().retrieve(MESSAGE_ID_2)).block())
+        assertThat(Mono.from(testee().retrieve(messageId2)).block())
             .isEqualTo(PREVIEW_2);
     }
 
     @Test
     default void deleteShouldBeIdempotent() {
-        Mono.from(testee().store(MESSAGE_ID_1, PREVIEW_1))
+        MessageId messageId1 = newMessageId();
+        Mono.from(testee().store(messageId1, PREVIEW_1))
             .block();
 
-        Mono.from(testee().delete(MESSAGE_ID_1))
+        Mono.from(testee().delete(messageId1))
             .block();
-        Mono.from(testee().delete(MESSAGE_ID_1))
+        Mono.from(testee().delete(messageId1))
             .block();
 
-        assertThat(Mono.from(testee().retrieve(MESSAGE_ID_1)).blockOptional())
+        assertThat(Mono.from(testee().retrieve(messageId1)).blockOptional())
             .isEmpty();
     }
 }
\ No newline at end of file
diff --git a/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/preview/MemoryMessagePreviewStoreTest.java b/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/preview/MemoryMessagePreviewStoreTest.java
index d56c38f..73d7809 100644
--- a/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/preview/MemoryMessagePreviewStoreTest.java
+++ b/server/data/data-jmap/src/test/java/org/apache/james/jmap/memory/preview/MemoryMessagePreviewStoreTest.java
@@ -21,14 +21,18 @@ package org.apache.james.jmap.memory.preview;
 
 import org.apache.james.jmap.api.preview.MessagePreviewStore;
 import org.apache.james.jmap.api.preview.MessagePreviewStoreContract;
+import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.TestMessageId;
 import org.junit.jupiter.api.BeforeEach;
 
 class MemoryMessagePreviewStoreTest implements MessagePreviewStoreContract {
 
     private MemoryMessagePreviewStore testee;
+    private TestMessageId.Factory messageIdFactory;
 
     @BeforeEach
     void setUp() {
+        messageIdFactory = new TestMessageId.Factory();
         testee = new MemoryMessagePreviewStore();
     }
 
@@ -36,4 +40,9 @@ class MemoryMessagePreviewStoreTest implements MessagePreviewStoreContract {
     public MessagePreviewStore testee() {
         return testee;
     }
+
+    @Override
+    public MessageId newMessageId() {
+        return messageIdFactory.generate();
+    }
 }
\ No newline at end of file


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