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