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 <btell...@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-unsubscr...@james.apache.org For additional commands, e-mail: notifications-h...@james.apache.org