You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2022/12/06 08:04:06 UTC

[james-project] 15/15: [PERF] Allow disabling SERIAL read for non critical UID/ModSeq read operations

This is an automated email from the ASF dual-hosted git repository.

btellier pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/james-project.git

commit e3904e5dabe7135793b38e639020ff47bf1123e6
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Mon Dec 5 16:00:12 2022 +0700

    [PERF] Allow disabling SERIAL read for non critical UID/ModSeq read operations
---
 .../init/configuration/CassandraConfiguration.java | 44 ++++++++++++++++++++--
 .../cassandra/mail/CassandraModSeqProvider.java    | 20 ++++++----
 .../cassandra/mail/CassandraUidProvider.java       | 20 ++++++----
 .../modules/ROOT/pages/configure/cassandra.adoc    | 14 +++++++
 4 files changed, 80 insertions(+), 18 deletions(-)

diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/CassandraConfiguration.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/CassandraConfiguration.java
index 2df55e6641..76c90968d6 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/CassandraConfiguration.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/CassandraConfiguration.java
@@ -76,6 +76,8 @@ public class CassandraConfiguration {
     private static final String MAILBOX_READ_STRONG_CONSISTENCY = "mailbox.read.strong.consistency";
     private static final String MESSAGE_READ_STRONG_CONSISTENCY = "message.read.strong.consistency";
     private static final String MESSAGE_WRITE_STRONG_CONSISTENCY = "message.write.strong.consistency.unsafe";
+    private static final String UID_READ_STRONG_CONSISTENCY = "uid.read.strong.consistency.unsafe";
+    private static final String MODSEQ_READ_STRONG_CONSISTENCY = "modseq.read.strong.consistency.unsafe";
     private static final String CONSISTENCY_LEVEL_REGULAR = "cassandra.consistency_level.regular";
     private static final String CONSISTENCY_LEVEL_LIGHTWEIGHT_TRANSACTION = "cassandra.consistency_level.lightweight_transaction";
     private static final String OPTIMISTIC_CONSISTENCY_LEVEL = "optimistic.consistency.level.enabled";
@@ -102,6 +104,8 @@ public class CassandraConfiguration {
         private Optional<Boolean> mailboxReadStrongConsistency = Optional.empty();
         private Optional<Boolean> messageReadStrongConsistency = Optional.empty();
         private Optional<Boolean> messageWriteStrongConsistency = Optional.empty();
+        private Optional<Boolean> uidReadStrongConsistency = Optional.empty();
+        private Optional<Boolean> modseqReadStrongConsistency = Optional.empty();
         private Optional<Boolean> optimisticConsistencyLevel = Optional.empty();
         private Optional<Boolean> mailRepositoryStrongConsistency = Optional.empty();
 
@@ -125,6 +129,16 @@ public class CassandraConfiguration {
             return this;
         }
 
+        public Builder uidReadStrongConsistency(Optional<Boolean> value) {
+            this.uidReadStrongConsistency = value;
+            return this;
+        }
+
+        public Builder modseqReadStrongConsistency(Optional<Boolean> value) {
+            this.modseqReadStrongConsistency = value;
+            return this;
+        }
+
         public Builder messageWriteStrongConsistency(boolean value) {
             this.messageWriteStrongConsistency = Optional.of(value);
             return this;
@@ -348,7 +362,9 @@ public class CassandraConfiguration {
                 messageReadStrongConsistency.orElse(DEFAULT_STRONG_CONSISTENCY),
                 messageWriteStrongConsistency.orElse(DEFAULT_STRONG_CONSISTENCY),
                 optimisticConsistencyLevel.orElse(DEFAULT_OPTIMISTIC_CONSISTENCY_LEVEL),
-                mailRepositoryStrongConsistency.orElse(DEFAULT_MAIL_REPOSITORY_STRONG_CONSISTENCY));
+                mailRepositoryStrongConsistency.orElse(DEFAULT_MAIL_REPOSITORY_STRONG_CONSISTENCY),
+                uidReadStrongConsistency.orElse(DEFAULT_STRONG_CONSISTENCY),
+                modseqReadStrongConsistency.orElse(DEFAULT_STRONG_CONSISTENCY));
         }
     }
 
@@ -392,6 +408,10 @@ public class CassandraConfiguration {
                 propertiesConfiguration.getBoolean(MAILBOX_READ_STRONG_CONSISTENCY, null)))
             .messageReadStrongConsistency(Optional.ofNullable(
                 propertiesConfiguration.getBoolean(MESSAGE_READ_STRONG_CONSISTENCY, null)))
+            .uidReadStrongConsistency(Optional.ofNullable(
+                propertiesConfiguration.getBoolean(UID_READ_STRONG_CONSISTENCY, null)))
+            .modseqReadStrongConsistency(Optional.ofNullable(
+                propertiesConfiguration.getBoolean(MODSEQ_READ_STRONG_CONSISTENCY, null)))
             .messageWriteStrongConsistency(Optional.ofNullable(
                 propertiesConfiguration.getBoolean(MESSAGE_WRITE_STRONG_CONSISTENCY, null)))
             .optimisticConsistencyLevel(Optional.ofNullable(
@@ -421,6 +441,8 @@ public class CassandraConfiguration {
     private final boolean messageWriteStrongConsistency;
     private final boolean optimisticConsistencyLevel;
     private final boolean mailRepositoryStrongConsistency;
+    private final boolean uidReadStrongConsistency;
+    private final boolean modseqReadStrongConsistency;
 
     @VisibleForTesting
     CassandraConfiguration(int aclMaxRetry, int expungeChunkSize,
@@ -431,7 +453,8 @@ public class CassandraConfiguration {
                            float mailboxReadRepair, float mailboxCountersReadRepairChanceMax,
                            float mailboxCountersReadRepairChanceOneHundred, boolean mailboxReadStrongConsistency,
                            boolean messageReadStrongConsistency, boolean messageWriteStrongConsistency,
-                           boolean optimisticConsistencyLevel, boolean mailRepositoryStrongConsistency) {
+                           boolean optimisticConsistencyLevel, boolean mailRepositoryStrongConsistency,
+                           boolean uidReadStrongConsistency, boolean modseqReadStrongConsistency) {
         this.aclMaxRetry = aclMaxRetry;
         this.expungeChunkSize = expungeChunkSize;
         this.flagsUpdateMessageIdMaxRetry = flagsUpdateMessageIdMaxRetry;
@@ -452,6 +475,16 @@ public class CassandraConfiguration {
         this.messageWriteStrongConsistency = messageWriteStrongConsistency;
         this.optimisticConsistencyLevel = optimisticConsistencyLevel;
         this.mailRepositoryStrongConsistency = mailRepositoryStrongConsistency;
+        this.uidReadStrongConsistency = uidReadStrongConsistency;
+        this.modseqReadStrongConsistency = modseqReadStrongConsistency;
+    }
+
+    public boolean isUidReadStrongConsistency() {
+        return uidReadStrongConsistency;
+    }
+
+    public boolean isModseqReadStrongConsistency() {
+        return modseqReadStrongConsistency;
     }
 
     public boolean isMailboxReadStrongConsistency() {
@@ -558,6 +591,8 @@ public class CassandraConfiguration {
                 && Objects.equals(this.consistencyLevelRegular, that.consistencyLevelRegular)
                 && Objects.equals(this.consistencyLevelLightweightTransaction, that.consistencyLevelLightweightTransaction)
                 && Objects.equals(this.optimisticConsistencyLevel, that.optimisticConsistencyLevel)
+                && Objects.equals(this.uidReadStrongConsistency, that.uidReadStrongConsistency)
+                && Objects.equals(this.modseqReadStrongConsistency, that.modseqReadStrongConsistency)
                 && Objects.equals(this.mailRepositoryStrongConsistency, that.mailRepositoryStrongConsistency);
 
         }
@@ -572,7 +607,8 @@ public class CassandraConfiguration {
             blobPartSize, attachmentV2MigrationReadTimeout, messageAttachmentIdsReadTimeout,
             consistencyLevelRegular, consistencyLevelLightweightTransaction, mailboxReadRepair,
             messageReadStrongConsistency, mailboxReadStrongConsistency, messageWriteStrongConsistency,
-            optimisticConsistencyLevel, mailRepositoryStrongConsistency);
+            optimisticConsistencyLevel, mailRepositoryStrongConsistency, uidReadStrongConsistency,
+            modseqReadStrongConsistency);
     }
 
     @Override
@@ -598,6 +634,8 @@ public class CassandraConfiguration {
             .add("consistencyLevelLightweightTransaction", consistencyLevelLightweightTransaction)
             .add("optimisticConsistencyLevel", optimisticConsistencyLevel)
             .add("mailRepositoryStrongConsistency", mailRepositoryStrongConsistency)
+            .add("modseqReadStrongConsistency", modseqReadStrongConsistency)
+            .add("uidReadStrongConsistency", uidReadStrongConsistency)
             .toString();
     }
 }
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProvider.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProvider.java
index dd31a7bdcf..4aeca8e044 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProvider.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraModSeqProvider.java
@@ -48,6 +48,7 @@ import org.apache.james.mailbox.store.mail.ModSeqProvider;
 
 import com.datastax.oss.driver.api.core.CqlSession;
 import com.datastax.oss.driver.api.core.config.DriverExecutionProfile;
+import com.datastax.oss.driver.api.core.cql.BoundStatement;
 import com.datastax.oss.driver.api.core.cql.PreparedStatement;
 import com.datastax.oss.driver.api.core.type.codec.TypeCodecs;
 
@@ -90,6 +91,7 @@ public class CassandraModSeqProvider implements ModSeqProvider {
     private final PreparedStatement insert;
     private final RetryBackoffSpec retrySpec;
     private final DriverExecutionProfile lwtProfile;
+    private final CassandraConfiguration cassandraConfiguration;
 
     @Inject
     public CassandraModSeqProvider(CqlSession session, CassandraConfiguration cassandraConfiguration) {
@@ -101,6 +103,7 @@ public class CassandraModSeqProvider implements ModSeqProvider {
         Duration firstBackoff = Duration.ofMillis(10);
         this.retrySpec = Retry.backoff(cassandraConfiguration.getModSeqMaxRetry(), firstBackoff)
             .scheduler(Schedulers.parallel());
+        this.cassandraConfiguration = cassandraConfiguration;
     }
 
     private PreparedStatement prepareInsert(CqlSession session) {
@@ -147,14 +150,17 @@ public class CassandraModSeqProvider implements ModSeqProvider {
 
     @Override
     public ModSeq highestModSeq(MailboxId mailboxId) throws MailboxException {
-        return unbox(() -> findHighestModSeq((CassandraId) mailboxId).block().orElse(ModSeq.first()));
+        return unbox(() -> findHighestModSeq((CassandraId) mailboxId,
+            Optional.of(lwtProfile).filter(any -> cassandraConfiguration.isUidReadStrongConsistency()))
+            .block().orElse(ModSeq.first()));
     }
 
-    private Mono<Optional<ModSeq>> findHighestModSeq(CassandraId mailboxId) {
+    private Mono<Optional<ModSeq>> findHighestModSeq(CassandraId mailboxId, Optional<DriverExecutionProfile> executionProfile) {
+        BoundStatement statement = select.bind()
+            .set(MAILBOX_ID, mailboxId.asUuid(), TypeCodecs.TIMEUUID);
         return cassandraAsyncExecutor.executeSingleRowOptional(
-                select.bind()
-                    .set(MAILBOX_ID, mailboxId.asUuid(), TypeCodecs.TIMEUUID)
-                    .setExecutionProfile(lwtProfile))
+            executionProfile.map(statement::setExecutionProfile)
+                .orElse(statement))
             .map(maybeRow -> maybeRow.map(row -> ModSeq.of(row.getLong(0))));
     }
 
@@ -189,7 +195,7 @@ public class CassandraModSeqProvider implements ModSeqProvider {
     @Override
     public Mono<ModSeq> nextModSeqReactive(MailboxId mailboxId) {
         CassandraId cassandraId = (CassandraId) mailboxId;
-        return findHighestModSeq(cassandraId)
+        return findHighestModSeq(cassandraId, Optional.of(lwtProfile))
             .flatMap(maybeHighestModSeq -> maybeHighestModSeq
                 .map(highestModSeq -> tryUpdateModSeq(cassandraId, highestModSeq))
                 .orElseGet(() -> tryInsertModSeq(cassandraId, ModSeq.first())))
@@ -199,7 +205,7 @@ public class CassandraModSeqProvider implements ModSeqProvider {
 
     @Override
     public Mono<ModSeq> highestModSeqReactive(Mailbox mailbox) {
-        return findHighestModSeq((CassandraId) mailbox.getMailboxId())
+        return findHighestModSeq((CassandraId) mailbox.getMailboxId(), Optional.empty())
             .map(optional -> optional.orElse(ModSeq.first()));
     }
 }
diff --git a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProvider.java b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProvider.java
index 570ed05f9b..1a019d35fa 100644
--- a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProvider.java
+++ b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraUidProvider.java
@@ -48,6 +48,7 @@ import org.apache.james.mailbox.store.mail.UidProvider;
 
 import com.datastax.oss.driver.api.core.CqlSession;
 import com.datastax.oss.driver.api.core.config.DriverExecutionProfile;
+import com.datastax.oss.driver.api.core.cql.BoundStatement;
 import com.datastax.oss.driver.api.core.cql.PreparedStatement;
 import com.datastax.oss.driver.api.core.type.codec.TypeCodecs;
 import com.google.common.collect.ImmutableList;
@@ -66,6 +67,7 @@ public class CassandraUidProvider implements UidProvider {
     private final PreparedStatement selectStatement;
     private final DriverExecutionProfile lwtProfile;
     private final RetryBackoffSpec retrySpec;
+    private final CassandraConfiguration cassandraConfiguration;
 
     @Inject
     public CassandraUidProvider(CqlSession session, CassandraConfiguration cassandraConfiguration) {
@@ -77,6 +79,7 @@ public class CassandraUidProvider implements UidProvider {
         Duration firstBackoff = Duration.ofMillis(10);
         this.retrySpec = Retry.backoff(cassandraConfiguration.getUidMaxRetry(), firstBackoff)
             .scheduler(Schedulers.parallel());
+        this.cassandraConfiguration = cassandraConfiguration;
     }
 
     private PreparedStatement prepareSelect(CqlSession session) {
@@ -118,7 +121,7 @@ public class CassandraUidProvider implements UidProvider {
     @Override
     public Mono<MessageUid> nextUidReactive(MailboxId mailboxId) {
         CassandraId cassandraId = (CassandraId) mailboxId;
-        Mono<MessageUid> updateUid = findHighestUid(cassandraId)
+        Mono<MessageUid> updateUid = findHighestUid(cassandraId, Optional.of(lwtProfile))
             .flatMap(messageUid -> tryUpdateUid(cassandraId, messageUid));
 
         return updateUid
@@ -132,7 +135,7 @@ public class CassandraUidProvider implements UidProvider {
     public Mono<List<MessageUid>> nextUids(MailboxId mailboxId, int count) {
         CassandraId cassandraId = (CassandraId) mailboxId;
 
-        Mono<List<MessageUid>> updateUid = findHighestUid(cassandraId)
+        Mono<List<MessageUid>> updateUid = findHighestUid(cassandraId, Optional.of(lwtProfile))
             .flatMap(messageUid -> tryUpdateUid(cassandraId, messageUid, count)
                 .map(highest -> range(messageUid, highest)));
 
@@ -152,22 +155,23 @@ public class CassandraUidProvider implements UidProvider {
 
     @Override
     public Optional<MessageUid> lastUid(Mailbox mailbox) {
-        return findHighestUid((CassandraId) mailbox.getMailboxId())
+        return findHighestUid((CassandraId) mailbox.getMailboxId(), Optional.of(lwtProfile).filter(any -> cassandraConfiguration.isUidReadStrongConsistency()))
             .blockOptional();
     }
 
     @Override
     public Mono<Optional<MessageUid>> lastUidReactive(Mailbox mailbox) {
-        return findHighestUid((CassandraId) mailbox.getMailboxId())
+        return findHighestUid((CassandraId) mailbox.getMailboxId(), Optional.of(lwtProfile).filter(any -> cassandraConfiguration.isUidReadStrongConsistency()))
             .map(Optional::of)
             .switchIfEmpty(Mono.just(Optional.empty()));
     }
 
-    private Mono<MessageUid> findHighestUid(CassandraId mailboxId) {
+    private Mono<MessageUid> findHighestUid(CassandraId mailboxId, Optional<DriverExecutionProfile> executionProfile) {
+        BoundStatement statement = selectStatement.bind()
+            .set(MAILBOX_ID, mailboxId.asUuid(), TypeCodecs.TIMEUUID);
         return executor.executeSingleRow(
-                selectStatement.bind()
-                    .set(MAILBOX_ID, mailboxId.asUuid(), TypeCodecs.TIMEUUID)
-                    .setExecutionProfile(lwtProfile))
+            executionProfile.map(statement::setExecutionProfile)
+                .orElse(statement))
             .map(row -> MessageUid.of(row.getLong(0)));
     }
 
diff --git a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/cassandra.adoc b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/cassandra.adoc
index bb91e32536..d72b530cc8 100644
--- a/server/apps/distributed-app/docs/modules/ROOT/pages/configure/cassandra.adoc
+++ b/server/apps/distributed-app/docs/modules/ROOT/pages/configure/cassandra.adoc
@@ -115,6 +115,20 @@ in stale reads as the system.paxos table will not be checked for latest updates.
 by turning it off. Note that reads performed as part of write transactions are always performed with a strong
 consistency.
 
+| uid.read.strong.consistency.unsafe
+| Optional. Boolean, defaults to true. Disabling should be considered experimental.
+If enabled, regular consistency level is used for read transactions for uid upon read oepration (eg IMAP status, select).
+Not doing so might result in stale reads as the system.paxos table will not be checked for latest updates.
+Better performance are expected by turning it off.
+Note that reads performed as part of write transactions are always performed with a strong consistency.
+
+| modseq.read.strong.consistency.unsafe
+| Optional. Boolean, defaults to true. Disabling should be considered experimental.
+If enabled, regular consistency level is used for read transactions for modseq upon read operation (eg IMAP status, select).
+Not doing so might result in stale reads as the system.paxos table will not be checked for latest updates.
+Better performance are expected by turning it off.
+Note that reads performed as part of write transactions are always performed with a strong consistency.
+
 | message.read.strong.consistency
 | Optional. Boolean, defaults to true. Disabling should be considered experimental.
 If enabled, regular consistency level is used for read transactions for message. Not doing so might result


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