You are viewing a plain text version of this content. The canonical link for it is here.
Posted to server-dev@james.apache.org by bt...@apache.org on 2020/07/30 04:15:20 UTC

[james-project] 07/12: JAMES-3317 add deduplication propertie for blobstore in configuration

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 986002b275ee7c3dc689c85dcd17c5db9f8503e8
Author: RĂ©mi Kowalski <rk...@linagora.com>
AuthorDate: Wed Jul 22 15:51:50 2020 +0200

    JAMES-3317 add deduplication propertie for blobstore in configuration
---
 .../destination/conf/blob.properties               |  9 ++-
 .../destination/conf/blob.properties               |  7 ++
 .../destination/conf/blob.properties               |  7 ++
 .../cassandra/destination/conf/blob.properties     |  9 ++-
 .../modules/blobstore/BlobStoreConfiguration.java  | 87 +++++++++++++++++-----
 .../james/CassandraRabbitMQAwsS3JmapTestRule.java  |  5 +-
 .../james/CassandraRabbitMQJamesServerFixture.java |  5 +-
 .../james/CassandraRabbitMQSwiftJmapTestRule.java  |  5 +-
 .../java/org/apache/james/WithCacheExtension.java  |  5 +-
 .../org/apache/james/WithCassandraBlobStore.java   |  5 +-
 .../apache/james/WithScanningSearchExtension.java  |  5 +-
 .../BlobStoreCacheModulesChooserTest.java          | 10 ++-
 .../blobstore/BlobStoreConfigurationTest.java      | 63 ++++++++++++++--
 .../blobstore/BlobStoreModulesChooserTest.java     | 14 +++-
 .../CassandraRabbitMQLdapJmapJamesServerTest.java  | 15 +++-
 .../rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java   |  5 +-
 .../RabbitMQAwsS3SpamAssassinContractTest.java     |  5 +-
 .../cucumber/awss3/RabbitMQAwsS3Stepdefs.java      |  5 +-
 .../distributed/DistributedAuthenticationTest.java |  5 +-
 .../distributed/DistributedEchoMethodTest.java     |  5 +-
 .../DistributedMailboxGetMethodTest.java           |  5 +-
 .../distributed/DistributedSessionRouteTest.java   |  2 +-
 .../rabbitmq/ConsistencyTasksIntegrationTest.java  |  5 +-
 .../rabbitmq/FixingGhostMailboxTest.java           |  5 +-
 .../rabbitmq/RabbitMQAuthorizedEndpointsTest.java  |  5 +-
 .../RabbitMQEventDeadLettersIntegrationTest.java   |  5 +-
 ...stViewProjectionHealthCheckIntegrationTest.java |  5 +-
 .../rabbitmq/RabbitMQForwardIntegrationTest.java   |  5 +-
 .../rabbitmq/RabbitMQJmapExtension.java            |  5 +-
 .../rabbitmq/RabbitMQJwtFilterIntegrationTest.java |  5 +-
 ...RabbitMQReindexingWithEventDeadLettersTest.java |  5 +-
 .../RabbitMQWebAdminServerIntegrationTest.java     |  5 +-
 ...dminServerTaskSerializationIntegrationTest.java |  5 +-
 ...RabbitMQDeletedMessageVaultIntegrationTest.java |  5 +-
 ...LinshareBlobExportMechanismIntegrationTest.java |  5 +-
 35 files changed, 287 insertions(+), 61 deletions(-)

diff --git a/dockerfiles/run/guice/cassandra-ldap/destination/conf/blob.properties b/dockerfiles/run/guice/cassandra-ldap/destination/conf/blob.properties
index 90c5135..2ec4035 100644
--- a/dockerfiles/run/guice/cassandra-ldap/destination/conf/blob.properties
+++ b/dockerfiles/run/guice/cassandra-ldap/destination/conf/blob.properties
@@ -6,6 +6,13 @@
 # Optional, default is localFile
 blob.export.implementation=localFile
 
+# ========================================= ObjectStorage deduplication ========================================
+# If you choose to enable deduplication, the mails with the same content will be stored only once.
+# Warning: Once this feature is enabled, there is no turning back as turning it off will lead to the deletion of all
+# the mails sharing the same content once one is deleted.
+# Mandatory, Allowed values are: true, false
+deduplication.enable=false
+
 # ======================================= Local File Blobs Exporting ========================================
 # Optional, directory to store exported blob, directory path follows James file system format
 # default is file://var/blobExporting
@@ -23,4 +30,4 @@ blob.export.localFile.directory=file://var/blobExporting
 # For Example: It will be formalized to 'Authorization: Basic {Credential of UUID/password}'
 
 # blob.export.linshare.technical.account.uuid=Technical_Account_UUID
-# blob.export.linshare.technical.account.password=password
\ No newline at end of file
+# blob.export.linshare.technical.account.password=password
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties
index 40aee03..6e5e8f8 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq-ldap/destination/conf/blob.properties
@@ -5,6 +5,13 @@
 # Mandatory, allowed values are: cassandra, objectstorage
 implementation=objectstorage
 
+# ========================================= ObjectStorage deduplication ========================================
+# If you choose to enable deduplication, the mails with the same content will be stored only once.
+# Warning: Once this feature is enabled, there is no turning back as turning it off will lead to the deletion of all
+# the mails sharing the same content once one is deleted.
+# Mandatory, Allowed values are: true, false
+deduplication.enable=false
+
 # ========================================= Cassandra BlobStore Cache ======================================
 # A cassandra cache can be enabled to reduce latency when reading small blobs frequently
 # A dedicated keyspace with a replication factor of one is then used
diff --git a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties
index 8c98913..c579d80 100644
--- a/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties
+++ b/dockerfiles/run/guice/cassandra-rabbitmq/destination/conf/blob.properties
@@ -5,6 +5,13 @@
 # Mandatory, allowed values are: cassandra, objectstorage
 implementation=objectstorage
 
+# ========================================= ObjectStorage deduplication ========================================
+# If you choose to enable deduplication, the mails with the same content will be stored only once.
+# Warning: Once this feature is enabled, there is no turning back as turning it off will lead to the deletion of all
+# the mails sharing the same content once one is deleted.
+# Mandatory, Allowed values are: true, false
+deduplication.enable=false
+
 # ========================================= Cassandra BlobStore Cache ======================================
 # A cassandra cache can be enabled to reduce latency when reading small blobs frequently
 # A dedicated keyspace with a replication factor of one is then used
diff --git a/dockerfiles/run/guice/cassandra/destination/conf/blob.properties b/dockerfiles/run/guice/cassandra/destination/conf/blob.properties
index 90c5135..2ec4035 100644
--- a/dockerfiles/run/guice/cassandra/destination/conf/blob.properties
+++ b/dockerfiles/run/guice/cassandra/destination/conf/blob.properties
@@ -6,6 +6,13 @@
 # Optional, default is localFile
 blob.export.implementation=localFile
 
+# ========================================= ObjectStorage deduplication ========================================
+# If you choose to enable deduplication, the mails with the same content will be stored only once.
+# Warning: Once this feature is enabled, there is no turning back as turning it off will lead to the deletion of all
+# the mails sharing the same content once one is deleted.
+# Mandatory, Allowed values are: true, false
+deduplication.enable=false
+
 # ======================================= Local File Blobs Exporting ========================================
 # Optional, directory to store exported blob, directory path follows James file system format
 # default is file://var/blobExporting
@@ -23,4 +30,4 @@ blob.export.localFile.directory=file://var/blobExporting
 # For Example: It will be formalized to 'Authorization: Basic {Credential of UUID/password}'
 
 # blob.export.linshare.technical.account.uuid=Technical_Account_UUID
-# blob.export.linshare.technical.account.password=password
\ No newline at end of file
+# blob.export.linshare.technical.account.password=password
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/blobstore/BlobStoreConfiguration.java b/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/blobstore/BlobStoreConfiguration.java
index 789813b..7507794 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/blobstore/BlobStoreConfiguration.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/main/java/org/apache/james/modules/blobstore/BlobStoreConfiguration.java
@@ -29,32 +29,63 @@ import org.apache.commons.configuration2.Configuration;
 import org.apache.commons.configuration2.ex.ConfigurationException;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.james.modules.mailbox.ConfigurationComponent;
+import org.apache.james.server.blob.deduplication.StorageStrategy;
 import org.apache.james.server.core.filesystem.FileSystemImpl;
 import org.apache.james.utils.PropertiesProvider;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.MoreObjects;
 
+import io.vavr.control.Try;
+
 public class BlobStoreConfiguration {
     private static final Logger LOGGER = LoggerFactory.getLogger(BlobStoreConfiguration.class);
 
-    public static class CacheChoice {
-        private final  BlobStoreImplName implementation;
+    @FunctionalInterface
+    public interface RequireImplementation {
+        RequireCache implementation(BlobStoreImplName implementation);
+
+        default RequireCache cassandra() {
+            return implementation(BlobStoreImplName.CASSANDRA);
+        }
+
+        default RequireCache objectStorage() {
+            return implementation(BlobStoreImplName.OBJECTSTORAGE);
+        }
+    }
+
+    @FunctionalInterface
+    public interface RequireCache {
+        RequireStoringStrategy enableCache(boolean enable);
+
+        default RequireStoringStrategy enableCache() {
+            return enableCache(CACHE_ENABLED);
+        }
 
-        private CacheChoice(BlobStoreImplName implementation) {
-            this.implementation = implementation;
+        default RequireStoringStrategy disableCache() {
+            return enableCache(!CACHE_ENABLED);
         }
+    }
+
+    @FunctionalInterface
+    public interface RequireStoringStrategy {
+        BlobStoreConfiguration strategy(StorageStrategy storageStrategy);
 
-        public BlobStoreConfiguration enableCache() {
-            return new BlobStoreConfiguration(implementation, CACHE_ENABLED);
+        default BlobStoreConfiguration passthrough() {
+            return strategy(StorageStrategy.PASSTHROUGH);
         }
 
-        public BlobStoreConfiguration disableCache() {
-            return new BlobStoreConfiguration(implementation, !CACHE_ENABLED);
+        default BlobStoreConfiguration deduplication() {
+            return strategy(StorageStrategy.DEDUPLICATION);
         }
     }
 
+    public static RequireImplementation builder() {
+        return implementation -> enableCache -> storageStrategy -> new BlobStoreConfiguration(implementation, enableCache, storageStrategy);
+    }
+
     public enum BlobStoreImplName {
         CASSANDRA("cassandra"),
         OBJECTSTORAGE("objectstorage");
@@ -87,6 +118,7 @@ public class BlobStoreConfiguration {
     static final String BLOBSTORE_IMPLEMENTATION_PROPERTY = "implementation";
     static final String CACHE_ENABLE_PROPERTY = "cache.enable";
     static final boolean CACHE_ENABLED = true;
+    static final String DEDUPLICATION_ENABLE_PROPERTY = "deduplication.enable";
 
     public static BlobStoreConfiguration parse(org.apache.james.server.core.configuration.Configuration configuration) throws ConfigurationException {
         PropertiesProvider propertiesProvider = new PropertiesProvider(new FileSystemImpl(configuration.directories()),
@@ -101,7 +133,10 @@ public class BlobStoreConfiguration {
             return BlobStoreConfiguration.from(configuration);
         } catch (FileNotFoundException e) {
             LOGGER.warn("Could not find " + ConfigurationComponent.NAME + " configuration file, using cassandra blobstore as the default");
-            return BlobStoreConfiguration.cassandra();
+            return BlobStoreConfiguration.builder()
+                    .cassandra()
+                    .disableCache()
+                    .passthrough();
         }
     }
 
@@ -114,30 +149,44 @@ public class BlobStoreConfiguration {
                 "supported values in: %s", BLOBSTORE_IMPLEMENTATION_PROPERTY, BlobStoreImplName.supportedImplNames())));
 
         boolean cacheEnabled = configuration.getBoolean(CACHE_ENABLE_PROPERTY, false);
-
-        return new BlobStoreConfiguration(blobStoreImplName, cacheEnabled);
+        boolean deduplicationEnabled = Try.ofCallable(() -> configuration.getBoolean(DEDUPLICATION_ENABLE_PROPERTY))
+                .getOrElseThrow(() -> new IllegalStateException("deduplication.enable property is missing please use one of the supported values in: true, false\n" +
+                        "If you choose to enable deduplication, the mails with the same content will be stored only once.\n" +
+                        "Warning: Once this feature is enabled, there is no turning back as turning it off will lead to the deletion of all\n" +
+                        "the mails sharing the same content once one is deleted.\n" +
+                        "Upgrade note: If you are upgrading from James 3.5 or older, the deduplication was enabled."));
+
+        if (deduplicationEnabled) {
+            return new BlobStoreConfiguration(blobStoreImplName, cacheEnabled, StorageStrategy.DEDUPLICATION);
+        } else {
+            return new BlobStoreConfiguration(blobStoreImplName, cacheEnabled, StorageStrategy.PASSTHROUGH);
+        }
     }
 
+    @VisibleForTesting
     public static BlobStoreConfiguration cassandra() {
-        return new BlobStoreConfiguration(BlobStoreImplName.CASSANDRA, !CACHE_ENABLED);
+        return new BlobStoreConfiguration(BlobStoreImplName.CASSANDRA, !CACHE_ENABLED, StorageStrategy.PASSTHROUGH);
     }
 
-    public static CacheChoice objectStorage() {
-        return new CacheChoice(BlobStoreImplName.OBJECTSTORAGE);
-    }
 
     private final BlobStoreImplName implementation;
     private final boolean cacheEnabled;
+    private final StorageStrategy storageStrategy;
 
-    BlobStoreConfiguration(BlobStoreImplName implementation, boolean cacheEnabled) {
+    BlobStoreConfiguration(BlobStoreImplName implementation, boolean cacheEnabled, StorageStrategy storageStrategy) {
         this.implementation = implementation;
         this.cacheEnabled = cacheEnabled;
+        this.storageStrategy = storageStrategy;
     }
 
     public boolean cacheEnabled() {
         return cacheEnabled;
     }
 
+    public StorageStrategy storageStrategy() {
+        return storageStrategy;
+    }
+
     BlobStoreImplName getImplementation() {
         return implementation;
     }
@@ -148,14 +197,15 @@ public class BlobStoreConfiguration {
             BlobStoreConfiguration that = (BlobStoreConfiguration) o;
 
             return Objects.equals(this.implementation, that.implementation)
-                && Objects.equals(this.cacheEnabled, that.cacheEnabled);
+                && Objects.equals(this.cacheEnabled, that.cacheEnabled)
+                && Objects.equals(this.storageStrategy, that.storageStrategy);
         }
         return false;
     }
 
     @Override
     public final int hashCode() {
-        return Objects.hash(implementation, cacheEnabled);
+        return Objects.hash(implementation, cacheEnabled, storageStrategy);
     }
 
     @Override
@@ -163,6 +213,7 @@ public class BlobStoreConfiguration {
         return MoreObjects.toStringHelper(this)
             .add("implementation", implementation)
             .add("cacheEnabled", cacheEnabled)
+            .add("storageStrategy", storageStrategy.name())
             .toString();
     }
 }
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQAwsS3JmapTestRule.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQAwsS3JmapTestRule.java
index 0fe2807..9986b37 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQAwsS3JmapTestRule.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQAwsS3JmapTestRule.java
@@ -62,7 +62,10 @@ public class CassandraRabbitMQAwsS3JmapTestRule implements TestRule {
         CassandraRabbitMQJamesConfiguration configuration = CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(temporaryFolder.newFolder())
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build();
 
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQJamesServerFixture.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQJamesServerFixture.java
index 264faac..b9878db 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQJamesServerFixture.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQJamesServerFixture.java
@@ -40,7 +40,10 @@ public class CassandraRabbitMQJamesServerFixture {
             CassandraRabbitMQJamesConfiguration.builder()
                 .workingDirectory(tmpDir)
                 .configurationFromClasspath()
-                .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+                .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
                 .searchConfiguration(SearchConfiguration.elasticSearch())
                 .build())
             .extension(new DockerElasticSearchExtension())
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQSwiftJmapTestRule.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQSwiftJmapTestRule.java
index ae2891a..c359987 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQSwiftJmapTestRule.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/CassandraRabbitMQSwiftJmapTestRule.java
@@ -61,7 +61,10 @@ public class CassandraRabbitMQSwiftJmapTestRule implements TestRule {
         CassandraRabbitMQJamesConfiguration configuration = CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(temporaryFolder.newFolder())
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build();
 
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCacheExtension.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCacheExtension.java
index 0df288c..a971e3e 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCacheExtension.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCacheExtension.java
@@ -42,7 +42,10 @@ public class WithCacheExtension implements BeforeAllCallback, AfterAllCallback,
             CassandraRabbitMQJamesConfiguration.builder()
                 .workingDirectory(tmpDir)
                 .configurationFromClasspath()
-                .blobStore(BlobStoreConfiguration.objectStorage().enableCache())
+                .blobStore(BlobStoreConfiguration.builder()
+                        .objectStorage()
+                        .enableCache()
+                        .deduplication())
                 .searchConfiguration(SearchConfiguration.elasticSearch())
                 .build())
             .extension(new DockerElasticSearchExtension())
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraBlobStore.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraBlobStore.java
index 36f9889..98dd3a6 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraBlobStore.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithCassandraBlobStore.java
@@ -41,7 +41,10 @@ public class WithCassandraBlobStore implements BeforeAllCallback, AfterAllCallba
             CassandraRabbitMQJamesConfiguration.builder()
                 .workingDirectory(tmpDir)
                 .configurationFromClasspath()
-                .blobStore(BlobStoreConfiguration.cassandra())
+                .blobStore(BlobStoreConfiguration.builder()
+                        .cassandra()
+                        .disableCache()
+                        .passthrough())
                 .searchConfiguration(SearchConfiguration.elasticSearch())
                 .build())
             .extension(new DockerElasticSearchExtension())
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithScanningSearchExtension.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithScanningSearchExtension.java
index 01088b7..ddeaabe 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithScanningSearchExtension.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/WithScanningSearchExtension.java
@@ -42,7 +42,10 @@ public class WithScanningSearchExtension implements BeforeAllCallback, AfterAllC
             CassandraRabbitMQJamesConfiguration.builder()
                 .workingDirectory(tmpDir)
                 .configurationFromClasspath()
-                .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+                .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
                 .searchConfiguration(SearchConfiguration.scanning())
                 .build())
             .extension(new CassandraExtension())
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreCacheModulesChooserTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreCacheModulesChooserTest.java
index 5cb8c50..03fc807 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreCacheModulesChooserTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreCacheModulesChooserTest.java
@@ -27,7 +27,10 @@ import org.junit.jupiter.api.Test;
 class BlobStoreCacheModulesChooserTest {
     @Test
     void chooseModulesShouldReturnCacheDisabledModuleWhenCacheDisabled() {
-        assertThat(BlobStoreCacheModulesChooser.chooseModules(BlobStoreConfiguration.objectStorage().disableCache()))
+        assertThat(BlobStoreCacheModulesChooser.chooseModules(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication()))
             .hasSize(1)
             .first()
             .isInstanceOf(BlobStoreCacheModulesChooser.CacheDisabledModule.class);
@@ -35,7 +38,10 @@ class BlobStoreCacheModulesChooserTest {
 
     @Test
     void chooseModulesShouldReturnCacheEnabledAndCassandraCacheModulesWhenCacheEnabled() {
-        assertThat(BlobStoreCacheModulesChooser.chooseModules(BlobStoreConfiguration.objectStorage().enableCache()))
+        assertThat(BlobStoreCacheModulesChooser.chooseModules(BlobStoreConfiguration.builder()
+                .objectStorage()
+                .enableCache()
+                .deduplication()))
             .hasSize(2)
             .allSatisfy(module ->
                 assertThat(module).isOfAnyClassIn(
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationTest.java
index e870fb7..731d6a7 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreConfigurationTest.java
@@ -26,6 +26,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import org.apache.commons.configuration2.PropertiesConfiguration;
 import org.apache.james.FakePropertiesProvider;
 import org.apache.james.modules.mailbox.ConfigurationComponent;
+import org.apache.james.server.blob.deduplication.StorageStrategy;
 import org.junit.jupiter.api.Test;
 
 import nl.jqno.equalsverifier.EqualsVerifier;
@@ -84,34 +85,44 @@ class BlobStoreConfigurationTest {
             .build();
 
         assertThat(parse(propertyProvider))
-            .isEqualTo(BlobStoreConfiguration.cassandra());
+            .isEqualTo(BlobStoreConfiguration.builder()
+                    .cassandra()
+                    .disableCache()
+                    .passthrough());
     }
 
     @Test
     void provideChoosingConfigurationShouldReturnObjectStorageFactoryWhenConfigurationImplIsObjectStorage() throws Exception {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         configuration.addProperty("implementation", BlobStoreConfiguration.BlobStoreImplName.OBJECTSTORAGE.getName());
+        configuration.addProperty("deduplication.enable", "true");
         FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder()
             .register(ConfigurationComponent.NAME, configuration)
             .build();
 
         assertThat(parse(propertyProvider))
-            .isEqualTo(BlobStoreConfiguration.objectStorage().disableCache());
+            .isEqualTo(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication());
     }
 
     @Test
     void provideChoosingConfigurationShouldReturnCassandraFactoryWhenConfigurationImplIsCassandra() throws Exception {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         configuration.addProperty("implementation", BlobStoreConfiguration.BlobStoreImplName.CASSANDRA.getName());
+        configuration.addProperty("deduplication.enable", "false");
         FakePropertiesProvider propertyProvider = FakePropertiesProvider.builder()
             .register(ConfigurationComponent.NAME, configuration)
             .build();
 
         assertThat(parse(propertyProvider))
-            .isEqualTo(BlobStoreConfiguration.cassandra());
+            .isEqualTo(BlobStoreConfiguration.builder()
+                    .cassandra()
+                    .disableCache()
+                    .passthrough());
     }
 
-
     @Test
     void fromShouldThrowWhenBlobStoreImplIsMissing() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
@@ -155,6 +166,7 @@ class BlobStoreConfigurationTest {
     void fromShouldReturnConfigurationWhenBlobStoreImplIsCassandra() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         configuration.addProperty("implementation", CASSANDRA);
+        configuration.addProperty("deduplication.enable", "false");
 
         assertThat(
             BlobStoreConfiguration.from(configuration)
@@ -167,7 +179,7 @@ class BlobStoreConfigurationTest {
     void fromShouldReturnConfigurationWhenBlobStoreImplIsObjectStorage() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         configuration.addProperty("implementation", OBJECT_STORAGE);
-
+        configuration.addProperty("deduplication.enable", "true");
         assertThat(
             BlobStoreConfiguration.from(configuration)
                 .getImplementation()
@@ -179,6 +191,7 @@ class BlobStoreConfigurationTest {
     void fromShouldReturnConfigurationWhenBlobStoreImplIsSupportedAndCaseInsensitive() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         configuration.addProperty("implementation", "OBjecTStorAGE");
+        configuration.addProperty("deduplication.enable", "true");
 
         assertThat(
             BlobStoreConfiguration.from(configuration)
@@ -191,6 +204,7 @@ class BlobStoreConfigurationTest {
     void fromShouldReturnConfigurationWhenBlobStoreImplIsSupportedAndHasExtraSpaces() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         configuration.addProperty("implementation", " cassandra ");
+        configuration.addProperty("deduplication.enable", "false");
 
         assertThat(
             BlobStoreConfiguration.from(configuration)
@@ -204,6 +218,7 @@ class BlobStoreConfigurationTest {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         configuration.addProperty("implementation", BlobStoreConfiguration.BlobStoreImplName.OBJECTSTORAGE.getName());
         configuration.addProperty("cache.enable", true);
+        configuration.addProperty("deduplication.enable", "true");
 
         assertThat(BlobStoreConfiguration.from(configuration).cacheEnabled())
             .isTrue();
@@ -214,6 +229,7 @@ class BlobStoreConfigurationTest {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         configuration.addProperty("implementation", BlobStoreConfiguration.BlobStoreImplName.OBJECTSTORAGE.getName());
         configuration.addProperty("cache.enable", false);
+        configuration.addProperty("deduplication.enable", "true");
 
         assertThat(BlobStoreConfiguration.from(configuration).cacheEnabled())
             .isFalse();
@@ -223,8 +239,43 @@ class BlobStoreConfigurationTest {
     void cacheEnabledShouldDefaultToFalse() {
         PropertiesConfiguration configuration = new PropertiesConfiguration();
         configuration.addProperty("implementation", BlobStoreConfiguration.BlobStoreImplName.OBJECTSTORAGE.getName());
+        configuration.addProperty("deduplication.enable", "true");
 
         assertThat(BlobStoreConfiguration.from(configuration).cacheEnabled())
             .isFalse();
     }
-}
\ No newline at end of file
+
+    @Test
+    void storageStrategyShouldBePassthroughWhenDeduplicationDisabled() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("implementation", BlobStoreConfiguration.BlobStoreImplName.OBJECTSTORAGE.getName());
+        configuration.addProperty("deduplication.enable", "false");
+
+        assertThat(BlobStoreConfiguration.from(configuration).storageStrategy())
+            .isEqualTo(StorageStrategy.PASSTHROUGH);
+    }
+
+    @Test
+    void storageStrategyShouldBeDeduplicationWhenDeduplicationEnabled() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("implementation", BlobStoreConfiguration.BlobStoreImplName.OBJECTSTORAGE.getName());
+        configuration.addProperty("deduplication.enable", "true");
+
+        assertThat(BlobStoreConfiguration.from(configuration).storageStrategy())
+                .isEqualTo(StorageStrategy.DEDUPLICATION);
+    }
+
+    @Test
+    void buildingConfigurationShouldThrowWhenDeduplicationPropertieIsOmitted() {
+        PropertiesConfiguration configuration = new PropertiesConfiguration();
+        configuration.addProperty("implementation", BlobStoreConfiguration.BlobStoreImplName.OBJECTSTORAGE.getName());
+
+        assertThatThrownBy(() -> BlobStoreConfiguration.from(configuration)).isInstanceOf(IllegalStateException.class)
+                .hasMessage("deduplication.enable property is missing please use one of the supported values in: true, false\n" +
+         "If you choose to enable deduplication, the mails with the same content will be stored only once.\n" +
+         "Warning: Once this feature is enabled, there is no turning back as turning it off will lead to the deletion of all\n" +
+         "the mails sharing the same content once one is deleted.\n" +
+        "Upgrade note: If you are upgrading from James 3.5 or older, the deduplication was enabled.");
+    }
+
+}
diff --git a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreModulesChooserTest.java b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreModulesChooserTest.java
index da99509..65735b5 100644
--- a/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreModulesChooserTest.java
+++ b/server/container/guice/cassandra-rabbitmq-guice/src/test/java/org/apache/james/modules/blobstore/BlobStoreModulesChooserTest.java
@@ -27,15 +27,21 @@ class BlobStoreModulesChooserTest {
 
     @Test
     void provideBlobStoreShouldReturnObjectStoreBlobStoreWhenObjectStoreConfigured() {
-        assertThat(BlobStoreModulesChooser.chooseModules(BlobStoreConfiguration.objectStorage().disableCache()))
+        assertThat(BlobStoreModulesChooser.chooseModules(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication()))
             .first()
-            .isInstanceOf(BlobStoreModulesChooser.ObjectStorageDeclarationModule.class);
+            .isInstanceOf(BlobStoreModulesChooser.ObjectStorageDumdBlobStoreDeclarationModule.class);
     }
 
     @Test
     void provideBlobStoreShouldReturnCassandraBlobStoreWhenCassandraConfigured() {
-        assertThat(BlobStoreModulesChooser.chooseModules(BlobStoreConfiguration.cassandra()))
+        assertThat(BlobStoreModulesChooser.chooseModules(BlobStoreConfiguration.builder()
+                .cassandra()
+                .disableCache()
+                .passthrough()))
             .first()
-            .isInstanceOf(BlobStoreModulesChooser.CassandraDeclarationModule.class);
+            .isInstanceOf(BlobStoreModulesChooser.CassandraDumbBlobStoreDeclarationModule.class);
     }
 }
\ No newline at end of file
diff --git a/server/container/guice/cassandra-rabbitmq-ldap-guice/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java b/server/container/guice/cassandra-rabbitmq-ldap-guice/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java
index ae150ca..91c8934 100644
--- a/server/container/guice/cassandra-rabbitmq-ldap-guice/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java
+++ b/server/container/guice/cassandra-rabbitmq-ldap-guice/src/test/java/org/apache/james/CassandraRabbitMQLdapJmapJamesServerTest.java
@@ -57,7 +57,10 @@ class CassandraRabbitMQLdapJmapJamesServerTest {
     @TestInstance(TestInstance.Lifecycle.PER_CLASS)
     class WithSwift implements ContractSuite {
         @RegisterExtension
-        JamesServerExtension testExtension = baseJamesServerExtensionBuilder(BlobStoreConfiguration.objectStorage().disableCache())
+        JamesServerExtension testExtension = baseJamesServerExtensionBuilder(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .extension(new SwiftBlobStoreExtension())
             .build();
     }
@@ -66,7 +69,10 @@ class CassandraRabbitMQLdapJmapJamesServerTest {
     @TestInstance(TestInstance.Lifecycle.PER_CLASS)
     class WithAwsS3 implements ContractSuite {
         @RegisterExtension
-        JamesServerExtension testExtension = baseJamesServerExtensionBuilder(BlobStoreConfiguration.objectStorage().disableCache())
+        JamesServerExtension testExtension = baseJamesServerExtensionBuilder(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .extension(new AwsS3BlobStoreExtension())
             .build();
     }
@@ -75,7 +81,10 @@ class CassandraRabbitMQLdapJmapJamesServerTest {
     @TestInstance(TestInstance.Lifecycle.PER_CLASS)
     class WithoutSwiftOrAwsS3 implements ContractSuite {
         @RegisterExtension
-        JamesServerExtension testExtension = baseJamesServerExtensionBuilder(BlobStoreConfiguration.cassandra())
+        JamesServerExtension testExtension = baseJamesServerExtensionBuilder(BlobStoreConfiguration.builder()
+                .cassandra()
+                .disableCache()
+                .passthrough())
             .build();
     }
 
diff --git a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java
index 93506b8..cecf617 100644
--- a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java
+++ b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SendMDNMethodTest.java
@@ -42,7 +42,10 @@ public class RabbitMQAwsS3SendMDNMethodTest extends SendMDNMethodTest {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SpamAssassinContractTest.java b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SpamAssassinContractTest.java
index b74ddc7..97a9793 100644
--- a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SpamAssassinContractTest.java
+++ b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/RabbitMQAwsS3SpamAssassinContractTest.java
@@ -41,7 +41,10 @@ class RabbitMQAwsS3SpamAssassinContractTest implements SpamAssassinContract {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/cucumber/awss3/RabbitMQAwsS3Stepdefs.java b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/cucumber/awss3/RabbitMQAwsS3Stepdefs.java
index 19bdf2e..f9f60e9 100644
--- a/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/cucumber/awss3/RabbitMQAwsS3Stepdefs.java
+++ b/server/protocols/jmap-draft-integration-testing/rabbitmq-jmap-draft-integration-testing/src/test/java/org/apache/james/jmap/rabbitmq/cucumber/awss3/RabbitMQAwsS3Stepdefs.java
@@ -81,7 +81,10 @@ public class RabbitMQAwsS3Stepdefs {
         CassandraRabbitMQJamesConfiguration configuration = CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(temporaryFolder.newFolder())
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build();
 
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedAuthenticationTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedAuthenticationTest.java
index 52f9764..219ff54 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedAuthenticationTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedAuthenticationTest.java
@@ -39,7 +39,10 @@ class DistributedAuthenticationTest implements AuthenticationContract {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
index 9827396..79ff634 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedEchoMethodTest.java
@@ -40,7 +40,10 @@ public class DistributedEchoMethodTest implements EchoMethodContract {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java
index 1e34353..2ccd522 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedMailboxGetMethodTest.java
@@ -42,7 +42,10 @@ public class DistributedMailboxGetMethodTest implements MailboxGetMethodContract
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .build())
         .extension(new DockerElasticSearchExtension())
         .extension(new CassandraExtension())
diff --git a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedSessionRouteTest.java b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedSessionRouteTest.java
index ef34f84..27d9664 100644
--- a/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedSessionRouteTest.java
+++ b/server/protocols/jmap-rfc-8621-integration-tests/distributed-jmap-rfc-8621-integration-tests/src/test/java/org/apache/james/jmap/rfc8621/distributed/DistributedSessionRouteTest.java
@@ -39,7 +39,7 @@ public class DistributedSessionRouteTest implements SessionRoutesContract {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder().objectStorage().disableCache().passthrough())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java
index 3a5c87a..bdcfa96 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/ConsistencyTasksIntegrationTest.java
@@ -128,7 +128,10 @@ class ConsistencyTasksIntegrationTest {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java
index cd59cd5..603e008 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/FixingGhostMailboxTest.java
@@ -116,7 +116,10 @@ class FixingGhostMailboxTest {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQAuthorizedEndpointsTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQAuthorizedEndpointsTest.java
index afca267..bebfaa3 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQAuthorizedEndpointsTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQAuthorizedEndpointsTest.java
@@ -44,7 +44,10 @@ class RabbitMQAuthorizedEndpointsTest extends AuthorizedEndpointsTest {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
index 70ec14c..29ca1ab 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQEventDeadLettersIntegrationTest.java
@@ -205,7 +205,10 @@ class RabbitMQEventDeadLettersIntegrationTest {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQFastViewProjectionHealthCheckIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQFastViewProjectionHealthCheckIntegrationTest.java
index 45416a9..3ecfa00 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQFastViewProjectionHealthCheckIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQFastViewProjectionHealthCheckIntegrationTest.java
@@ -40,7 +40,10 @@ class RabbitMQFastViewProjectionHealthCheckIntegrationTest extends FastViewProje
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQForwardIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQForwardIntegrationTest.java
index e72de6d..cdf1848 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQForwardIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQForwardIntegrationTest.java
@@ -40,7 +40,10 @@ class RabbitMQForwardIntegrationTest extends ForwardIntegrationTest {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJmapExtension.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJmapExtension.java
index da86d14..aaa763f 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJmapExtension.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJmapExtension.java
@@ -150,7 +150,10 @@ public class RabbitMQJmapExtension implements BeforeAllCallback, AfterAllCallbac
         CassandraRabbitMQJamesConfiguration configuration = CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(temporaryFolder.newFolder())
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build();
 
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java
index fca871b..1a96a6a 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQJwtFilterIntegrationTest.java
@@ -42,7 +42,10 @@ class RabbitMQJwtFilterIntegrationTest extends JwtFilterIntegrationTest {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQReindexingWithEventDeadLettersTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQReindexingWithEventDeadLettersTest.java
index fd3168a..df2390a 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQReindexingWithEventDeadLettersTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQReindexingWithEventDeadLettersTest.java
@@ -87,7 +87,10 @@ class RabbitMQReindexingWithEventDeadLettersTest {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(dockerElasticSearch)
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java
index 7162cb3..5f8a1fa 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerIntegrationTest.java
@@ -64,7 +64,10 @@ class RabbitMQWebAdminServerIntegrationTest extends WebAdminServerIntegrationTes
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java
index 3d5e3ed..6ef4410 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/RabbitMQWebAdminServerTaskSerializationIntegrationTest.java
@@ -103,7 +103,10 @@ class RabbitMQWebAdminServerTaskSerializationIntegrationTest {
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java
index d4f94d9..df878ac 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQDeletedMessageVaultIntegrationTest.java
@@ -49,7 +49,10 @@ class RabbitMQDeletedMessageVaultIntegrationTest extends DeletedMessageVaultInte
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(ES_EXTENSION)
diff --git a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
index ed82af5..43136e1 100644
--- a/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
+++ b/server/protocols/webadmin-integration-test/distributed-webadmin-integration-test/src/test/java/org/apache/james/webadmin/integration/rabbitmq/vault/RabbitMQLinshareBlobExportMechanismIntegrationTest.java
@@ -46,7 +46,10 @@ class RabbitMQLinshareBlobExportMechanismIntegrationTest extends LinshareBlobExp
         CassandraRabbitMQJamesConfiguration.builder()
             .workingDirectory(tmpDir)
             .configurationFromClasspath()
-            .blobStore(BlobStoreConfiguration.objectStorage().disableCache())
+            .blobStore(BlobStoreConfiguration.builder()
+                    .objectStorage()
+                    .disableCache()
+                    .deduplication())
             .searchConfiguration(SearchConfiguration.elasticSearch())
             .build())
         .extension(new DockerElasticSearchExtension())


---------------------------------------------------------------------
To unsubscribe, e-mail: server-dev-unsubscribe@james.apache.org
For additional commands, e-mail: server-dev-help@james.apache.org