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