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

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

commit 6968a6bcc44e56a871e3e8181dc7c1220c06e7dc
Author: Benoit TELLIER <[email protected]>
AuthorDate: Wed Jun 17 09:58:05 2026 +0200

    [FIX] Cassandra folder rename: use SERIAL for initial picture
    
    Improve overall atomicity
    
    Please note that we do not address TOCTOU races.
    We also do not need serial for destination mailbix as it is gated by an 
idempotent DELETE...IF NOT EXIST
---
 .../james/mailbox/cassandra/CassandraMailboxManager.java    | 13 +++++++++++++
 .../mailbox/cassandra/mail/CassandraMailboxMapper.java      | 13 +++++--------
 .../org/apache/james/mailbox/store/StoreMailboxManager.java |  6 +++++-
 3 files changed, 23 insertions(+), 9 deletions(-)

diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java
index fe1dac1827..51ee7225a9 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/CassandraMailboxManager.java
@@ -24,24 +24,29 @@ import java.util.EnumSet;
 
 import jakarta.inject.Inject;
 
+import 
org.apache.james.backends.cassandra.init.configuration.JamesExecutionProfiles;
 import org.apache.james.events.EventBus;
 import org.apache.james.mailbox.MailboxManager;
 import org.apache.james.mailbox.MailboxPathLocker;
 import org.apache.james.mailbox.MailboxSession;
 import org.apache.james.mailbox.SessionProvider;
+import org.apache.james.mailbox.cassandra.mail.CassandraMailboxMapper;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MessageId;
+import org.apache.james.mailbox.model.search.MailboxQuery;
 import org.apache.james.mailbox.store.MailboxManagerConfiguration;
 import org.apache.james.mailbox.store.PreDeletionHooks;
 import org.apache.james.mailbox.store.StoreMailboxAnnotationManager;
 import org.apache.james.mailbox.store.StoreMailboxManager;
 import org.apache.james.mailbox.store.StoreMessageManager;
 import org.apache.james.mailbox.store.StoreRightManager;
+import org.apache.james.mailbox.store.mail.MailboxMapper;
 import org.apache.james.mailbox.store.mail.ThreadIdGuessingAlgorithm;
 import org.apache.james.mailbox.store.mail.model.impl.MessageParser;
 import org.apache.james.mailbox.store.quota.QuotaComponents;
 import org.apache.james.mailbox.store.search.MessageSearchIndex;
 
+import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
 
 /**
@@ -118,4 +123,12 @@ public class CassandraMailboxManager extends 
StoreMailboxManager {
     public <T> Mono<T> manageProcessing(Mono<T> toBeWrapped, MailboxSession 
mailboxSession) {
         return toBeWrapped;
     }
+
+    @Override
+    protected Flux<Mailbox> getMailboxWithPathLikeUponRename(MailboxMapper 
mapper, MailboxQuery.UserBound query) {
+        if (mapper instanceof CassandraMailboxMapper cassandraMailboxMapper) {
+            return cassandraMailboxMapper.findMailboxWithPathLike(query, 
JamesExecutionProfiles.ConsistencyChoice.STRONG);
+        }
+        return super.getMailboxWithPathLikeUponRename(mapper, query);
+    }
 }
diff --git 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
index b5fd64291e..125d142781 100644
--- 
a/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
+++ 
b/mailbox/cassandra/src/main/java/org/apache/james/mailbox/cassandra/mail/CassandraMailboxMapper.java
@@ -192,18 +192,15 @@ public class CassandraMailboxMapper implements 
MailboxMapper {
 
     @Override
     public Flux<Mailbox> findMailboxWithPathLike(MailboxQuery.UserBound query) 
{
-        String fixedNamespace = query.getFixedNamespace();
-        Username fixedUser = query.getFixedUser();
+        return findMailboxWithPathLike(query, consistencyChoice());
+    }
 
-        return performReadRepair(listMailboxes(fixedNamespace, fixedUser))
+    public Flux<Mailbox> findMailboxWithPathLike(MailboxQuery.UserBound query, 
JamesExecutionProfiles.ConsistencyChoice consistencyChoice) {
+        return 
performReadRepair(mailboxPathV3DAO.listUserMailboxes(query.getFixedNamespace(), 
query.getFixedUser(), consistencyChoice))
             .filter(mailbox -> 
query.isPathMatch(mailbox.generateAssociatedPath()))
             .flatMap(this::addAcl, CONCURRENCY);
     }
 
-    private Flux<Mailbox> listMailboxes(String fixedNamespace, Username 
fixedUser) {
-        return mailboxPathV3DAO.listUserMailboxes(fixedNamespace, fixedUser, 
consistencyChoice());
-    }
-
     @Override
     public Mono<Mailbox> create(MailboxPath mailboxPath, UidValidity 
uidValidity) {
         CassandraId cassandraId = CassandraId.timeBased();
@@ -254,7 +251,7 @@ public class CassandraMailboxMapper implements 
MailboxMapper {
 
     @Override
     public Mono<Boolean> hasChildren(Mailbox mailbox, char delimiter) {
-        return performReadRepair(listMailboxes(mailbox.getNamespace(), 
mailbox.getUser()))
+        return 
performReadRepair(mailboxPathV3DAO.listUserMailboxes(mailbox.getNamespace(), 
mailbox.getUser(), consistencyChoice()))
             .filter(idAndPath -> isPathChildOfMailbox(idAndPath, mailbox, 
delimiter))
             .hasElements();
     }
diff --git 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
index ca35e2c18d..7404512cf2 100644
--- 
a/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
+++ 
b/mailbox/store/src/main/java/org/apache/james/mailbox/store/StoreMailboxManager.java
@@ -606,7 +606,7 @@ public class StoreMailboxManager implements MailboxManager {
             .asUserBound();
 
         return assertCanDeleteWhenRename(fromSession, from)
-            .then(mapper.findMailboxWithPathLike(query)
+            .then(getMailboxWithPathLikeUponRename(mapper, query)
                 .filter(mailbox -> 
mailbox.generateAssociatedPath().getHierarchyLevels(fromSession.getPathDelimiter()).contains(from))
                 .sort(Comparator.comparing(mailbox -> 
mailbox.generateAssociatedPath().getHierarchyLevels(fromSession.getPathDelimiter()).size()))
                 .collectList()
@@ -619,6 +619,10 @@ public class StoreMailboxManager implements MailboxManager 
{
                 }));
     }
 
+    protected Flux<Mailbox> getMailboxWithPathLikeUponRename(MailboxMapper 
mapper, MailboxQuery.UserBound query) {
+        return mapper.findMailboxWithPathLike(query);
+    }
+
     private Mono<List<MailboxRenamedResult>> 
renameSubscriptionsIfNeeded(List<MailboxRenamedResult> renamedResults,
                                                                          
RenameOption option, MailboxSession fromSession, MailboxSession toSession) {
         if (option == RenameOption.RENAME_SUBSCRIPTIONS) {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to