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


The following commit(s) were added to refs/heads/master by this push:
     new 37c88ebdbd JAMES-2586 Fix concurrency issue of updating ACL in 
RLSSupportPostgresMailboxMapper
37c88ebdbd is described below

commit 37c88ebdbdfa5aa6bb426ce9fbd3c285fa5e985f
Author: hung phan <hp...@linagora.com>
AuthorDate: Mon Feb 24 14:09:44 2025 +0700

    JAMES-2586 Fix concurrency issue of updating ACL in 
RLSSupportPostgresMailboxMapper
---
 .../postgres/mail/PostgresMailboxMemberDAO.java    |  3 +-
 .../mail/RLSSupportPostgresMailboxMapper.java      | 47 +++++++++++++---------
 .../RLSSupportPostgresMailboxMapperACLTest.java    | 32 ++++++++++-----
 .../store/mail/model/MailboxMapperACLTest.java     |  3 +-
 4 files changed, 52 insertions(+), 33 deletions(-)

diff --git 
a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMemberDAO.java
 
b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMemberDAO.java
index 5cf73eb29f..b5557ab11f 100644
--- 
a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMemberDAO.java
+++ 
b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/PostgresMailboxMemberDAO.java
@@ -52,7 +52,8 @@ public class PostgresMailboxMemberDAO {
                 .map(username -> dslContext.newRecord(USER_NAME, MAILBOX_ID)
                     .value1(username.asString())
                     .value2(mailboxId.asUuid()))
-                .toList())));
+                .toList())
+            .onConflictDoNothing()));
     }
 
     public Mono<Void> delete(PostgresMailboxId mailboxId, List<Username> 
usernames) {
diff --git 
a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/RLSSupportPostgresMailboxMapper.java
 
b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/RLSSupportPostgresMailboxMapper.java
index aa3db2b311..efa77a24eb 100644
--- 
a/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/RLSSupportPostgresMailboxMapper.java
+++ 
b/mailbox/postgres/src/main/java/org/apache/james/mailbox/postgres/mail/RLSSupportPostgresMailboxMapper.java
@@ -19,20 +19,21 @@
 
 package org.apache.james.mailbox.postgres.mail;
 
+import java.time.Duration;
 import java.util.function.Function;
 
 import org.apache.james.core.Username;
 import org.apache.james.mailbox.acl.ACLDiff;
 import org.apache.james.mailbox.acl.PositiveUserACLDiff;
+import org.apache.james.mailbox.exception.UnsupportedRightException;
 import org.apache.james.mailbox.model.Mailbox;
 import org.apache.james.mailbox.model.MailboxACL;
 import org.apache.james.mailbox.postgres.PostgresMailboxId;
 import org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxDAO;
 
-import com.github.fge.lambdas.Throwing;
-
 import reactor.core.publisher.Flux;
 import reactor.core.publisher.Mono;
+import reactor.util.retry.Retry;
 
 public class RLSSupportPostgresMailboxMapper extends PostgresMailboxMapper {
     private final PostgresMailboxDAO postgresMailboxDAO;
@@ -56,30 +57,36 @@ public class RLSSupportPostgresMailboxMapper extends 
PostgresMailboxMapper {
 
     @Override
     public Mono<ACLDiff> updateACL(Mailbox mailbox, MailboxACL.ACLCommand 
mailboxACLCommand) {
-        MailboxACL oldACL = mailbox.getACL();
-        MailboxACL newACL = Throwing.supplier(() -> 
oldACL.apply(mailboxACLCommand)).get();
-        ACLDiff aclDiff = ACLDiff.computeDiff(oldACL, newACL);
-        PositiveUserACLDiff userACLDiff = new PositiveUserACLDiff(aclDiff);
-        return upsertACL(mailbox, newACL, aclDiff, userACLDiff);
+        return postgresMailboxDAO.getACL(mailbox.getMailboxId())
+            .flatMap(pairMailboxACLAndVersion -> {
+                try {
+                    MailboxACL newACL = 
pairMailboxACLAndVersion.getLeft().apply(mailboxACLCommand);
+                    return 
postgresMailboxDAO.upsertACL(mailbox.getMailboxId(), newACL, 
pairMailboxACLAndVersion.getRight())
+                        
.thenReturn(ACLDiff.computeDiff(pairMailboxACLAndVersion.getLeft(), newACL));
+                } catch (UnsupportedRightException e) {
+                    throw new RuntimeException(e);
+                }
+            }).retryWhen(Retry.backoff(3, Duration.ofMillis(100))
+                .filter(throwable -> throwable instanceof 
PostgresACLUpsertException))
+            .flatMap(aclDiff -> updateMembersOfMailbox(mailbox, new 
PositiveUserACLDiff(aclDiff))
+                .thenReturn(aclDiff));
     }
 
     @Override
     public Mono<ACLDiff> setACL(Mailbox mailbox, MailboxACL mailboxACL) {
-        MailboxACL oldACL = mailbox.getACL();
-        ACLDiff aclDiff = ACLDiff.computeDiff(oldACL, mailboxACL);
-        PositiveUserACLDiff userACLDiff = new PositiveUserACLDiff(aclDiff);
-        return upsertACL(mailbox, mailboxACL, aclDiff, userACLDiff);
+        return postgresMailboxDAO.getACL(mailbox.getMailboxId())
+            .flatMap(pairMailboxACLAndVersion ->
+                postgresMailboxDAO.upsertACL(mailbox.getMailboxId(), 
mailboxACL, pairMailboxACLAndVersion.getRight())
+                    
.thenReturn(ACLDiff.computeDiff(pairMailboxACLAndVersion.getLeft(), 
mailboxACL))).retryWhen(Retry.backoff(3, Duration.ofMillis(100))
+                .filter(throwable -> throwable instanceof 
PostgresACLUpsertException))
+            .flatMap(aclDiff -> updateMembersOfMailbox(mailbox, new 
PositiveUserACLDiff(aclDiff))
+                .thenReturn(aclDiff));
     }
 
-    private Mono<ACLDiff> upsertACL(Mailbox mailbox, MailboxACL newACL, 
ACLDiff aclDiff, PositiveUserACLDiff userACLDiff) {
-        return postgresMailboxDAO.upsertACL(mailbox.getMailboxId(), newACL)
-            
.then(postgresMailboxMemberDAO.delete(PostgresMailboxId.class.cast(mailbox.getMailboxId()),
-                userACLDiff.removedEntries().map(entry -> 
Username.of(entry.getKey().getName())).toList()))
+    private Mono<Void> updateMembersOfMailbox(Mailbox mailbox, 
PositiveUserACLDiff userACLDiff) {
+        return 
postgresMailboxMemberDAO.delete(PostgresMailboxId.class.cast(mailbox.getMailboxId()),
+                userACLDiff.removedEntries().map(entry -> 
Username.of(entry.getKey().getName())).toList())
             
.then(postgresMailboxMemberDAO.insert(PostgresMailboxId.class.cast(mailbox.getMailboxId()),
-                userACLDiff.addedEntries().map(entry -> 
Username.of(entry.getKey().getName())).toList()))
-            .then(Mono.fromCallable(() -> {
-                mailbox.setACL(newACL);
-                return aclDiff;
-            }));
+                userACLDiff.addedEntries().map(entry -> 
Username.of(entry.getKey().getName())).toList()));
     }
 }
diff --git 
a/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/RLSSupportPostgresMailboxMapperACLTest.java
 
b/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/RLSSupportPostgresMailboxMapperACLTest.java
index 9811865f8d..3e8edd578e 100644
--- 
a/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/RLSSupportPostgresMailboxMapperACLTest.java
+++ 
b/mailbox/postgres/src/test/java/org/apache/james/mailbox/postgres/mail/RLSSupportPostgresMailboxMapperACLTest.java
@@ -19,29 +19,39 @@
 
 package org.apache.james.mailbox.postgres.mail;
 
-import java.util.concurrent.ExecutionException;
+import java.time.Instant;
 
+import org.apache.james.backends.postgres.PostgresConfiguration;
 import org.apache.james.backends.postgres.PostgresExtension;
 import org.apache.james.backends.postgres.PostgresModule;
-import org.apache.james.mailbox.postgres.mail.dao.PostgresMailboxDAO;
+import org.apache.james.blob.api.BlobId;
+import org.apache.james.blob.api.BucketName;
+import org.apache.james.blob.api.PlainBlobId;
+import org.apache.james.blob.memory.MemoryBlobStoreDAO;
+import org.apache.james.mailbox.MailboxSessionUtil;
+import org.apache.james.mailbox.StringBackedAttachmentIdFactory;
+import org.apache.james.mailbox.postgres.PostgresMailboxSessionMapperFactory;
+import org.apache.james.mailbox.store.mail.AttachmentIdAssignationStrategy;
 import org.apache.james.mailbox.store.mail.MailboxMapper;
 import org.apache.james.mailbox.store.mail.model.MailboxMapperACLTest;
-import org.junit.jupiter.api.Disabled;
+import org.apache.james.server.blob.deduplication.DeDuplicationBlobStore;
+import org.apache.james.utils.UpdatableTickingClock;
 import org.junit.jupiter.api.extension.RegisterExtension;
 
 class RLSSupportPostgresMailboxMapperACLTest extends MailboxMapperACLTest {
     @RegisterExtension
-    static PostgresExtension postgresExtension = 
PostgresExtension.withoutRowLevelSecurity(PostgresModule.aggregateModules(PostgresMailboxModule.MODULE,
+    static PostgresExtension postgresExtension = 
PostgresExtension.withRowLevelSecurity(PostgresModule.aggregateModules(PostgresMailboxModule.MODULE,
         PostgresMailboxMemberModule.MODULE));
 
     @Override
     protected MailboxMapper createMailboxMapper() {
-        return new RLSSupportPostgresMailboxMapper(new 
PostgresMailboxDAO(postgresExtension.getDefaultPostgresExecutor()),
-            new 
PostgresMailboxMemberDAO(postgresExtension.getDefaultPostgresExecutor()));
-    }
-
-    @Override
-    @Disabled("not yet implemented")
-    protected void updateAclShouldWorkWellInMultiThreadEnv() throws 
ExecutionException, InterruptedException {
+        BlobId.Factory blobIdFactory = new PlainBlobId.Factory();
+        PostgresMailboxSessionMapperFactory 
postgresMailboxSessionMapperFactory = new 
PostgresMailboxSessionMapperFactory(postgresExtension.getExecutorFactory(),
+            new UpdatableTickingClock(Instant.now()),
+            new DeDuplicationBlobStore(new MemoryBlobStoreDAO(), 
BucketName.DEFAULT, blobIdFactory),
+            blobIdFactory,
+            
PostgresConfiguration.builder().username("a").password("a").rowLevelSecurityEnabled().byPassRLSUser("b").byPassRLSPassword("b").build(),
+            new AttachmentIdAssignationStrategy.Default(new 
StringBackedAttachmentIdFactory()));
+        return 
postgresMailboxSessionMapperFactory.getMailboxMapper(MailboxSessionUtil.create(BENWA));
     }
 }
diff --git 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperACLTest.java
 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperACLTest.java
index d0e9ebccaf..f2411d8318 100644
--- 
a/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperACLTest.java
+++ 
b/mailbox/store/src/test/java/org/apache/james/mailbox/store/mail/model/MailboxMapperACLTest.java
@@ -43,6 +43,7 @@ import org.junit.jupiter.api.Test;
 import com.google.common.collect.ImmutableMap;
 
 public abstract class MailboxMapperACLTest {
+    public static Username BENWA = Username.of("be...@domain.org");
     private static final UidValidity UID_VALIDITY = UidValidity.of(42);
     private static final Username USER = Username.of("user");
     private static final Username USER_1 = Username.of("user1");
@@ -57,7 +58,7 @@ public abstract class MailboxMapperACLTest {
     @BeforeEach
     void setUp() {
         mailboxMapper = createMailboxMapper();
-        MailboxPath benwaInboxPath = MailboxPath.forUser(Username.of("benwa"), 
"INBOX");
+        MailboxPath benwaInboxPath = MailboxPath.forUser(BENWA, "INBOX");
         benwaInboxMailbox = mailboxMapper.create(benwaInboxPath, 
UID_VALIDITY).block();
     }
 


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

Reply via email to