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 643302285cf..00e570aace0 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 @@ -26,6 +26,7 @@ import java.util.StringTokenizer; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import java.util.concurrent.CompletionStage; import java.util.function.Function; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -108,12 +109,30 @@ private CompletableFuture> fromPreviousTable(MailboxPath cassandraIdOptional .map(CassandraIdAndPath::getCassandraId) .map(this::retrieveMailbox) - .orElse(CompletableFuture.completedFuture(Optional.empty()))); + .orElse(CompletableFuture.completedFuture(Optional.empty()))) + .thenCompose(maybeMailbox -> maybeMailbox.map(this::migrate) + .orElse(CompletableFuture.completedFuture(maybeMailbox))); } catch (CompletionException e) { throw DriverExceptionHelper.handleStorageException(e); } } + private CompletableFuture> migrate(SimpleMailbox mailbox) { + CassandraId mailboxId = (CassandraId) mailbox.getMailboxId(); + return mailboxPathV2DAO.save(mailbox.generateAssociatedPath(), mailboxId) + .thenCompose(success -> deleteIfSuccess(mailbox, success)) + .thenApply(any -> Optional.of(mailbox)); + } + + private CompletionStage deleteIfSuccess(SimpleMailbox mailbox, boolean success) { + if (success) { + return mailboxPathDAO.delete(mailbox.generateAssociatedPath()); + } + LOGGER.info("Concurrent execution lead to data race while migrating {} to 'mailboxPathV2DAO'.", + mailbox.generateAssociatedPath()); + return CompletableFuture.completedFuture(null); + } + @Override public Mailbox findMailboxById(MailboxId id) throws MailboxException { CassandraId mailboxId = (CassandraId) id; diff --git a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/MailboxPathV2MigrationTest.java b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/MailboxPathV2MigrationTest.java index 249ec594bee..00867a56149 100644 --- a/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/MailboxPathV2MigrationTest.java +++ b/mailbox/cassandra/src/test/java/org/apache/james/mailbox/cassandra/mail/migration/MailboxPathV2MigrationTest.java @@ -108,15 +108,16 @@ public void newValuesShouldNotBeSavedInOldDAO() throws Exception { } @Test - public void readingOldValuesShouldNotMigrateThem() throws Exception { + public void readingOldValuesShouldMigrateThem() throws Exception { daoV1.save(MAILBOX_PATH_1, MAILBOX_ID_1).join(); mailboxDAO.save(MAILBOX_1).join(); mailboxMapper.findMailboxByPath(MAILBOX_PATH_1); SoftAssertions softly = new SoftAssertions(); - softly.assertThat(daoV1.retrieveId(MAILBOX_PATH_1).join()).isNotEmpty(); - softly.assertThat(daoV2.retrieveId(MAILBOX_PATH_1).join()).isEmpty(); + softly.assertThat(daoV1.retrieveId(MAILBOX_PATH_1).join()).isEmpty(); + softly.assertThat(daoV2.retrieveId(MAILBOX_PATH_1).join()) + .contains(new CassandraIdAndPath(MAILBOX_ID_1, MAILBOX_PATH_1)); softly.assertAll(); }