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/04/17 00:32:14 UTC

[james-project] 22/39: JAMES-3137 Inject a separate Cassandra session for cache keyspace

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 505d35385bd8f21bcce2eb1bb6f0e216930b47e7
Author: Benoit Tellier <bt...@linagora.com>
AuthorDate: Fri Mar 27 11:12:51 2020 +0700

    JAMES-3137 Inject a separate Cassandra session for cache keyspace
---
 .../init/SessionWithInitializedTablesFactory.java  |  36 +++++--
 .../init/configuration/InjectionNames.java         |  24 +++++
 .../james/backends/cassandra/CassandraCluster.java |   2 +-
 .../SessionWithInitializedTablesFactoryTest.java   |   3 +-
 .../modules/mailbox/CassandraSessionModule.java    |  26 +++++-
 .../java/org/apache/james/CacheSessionTest.java    | 103 +++++++++++++++++++++
 6 files changed, 185 insertions(+), 9 deletions(-)

diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java
index 89bcf0d..b3d18f4 100644
--- a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactory.java
@@ -23,6 +23,7 @@ import java.util.stream.Stream;
 
 import javax.annotation.PreDestroy;
 import javax.inject.Inject;
+import javax.inject.Named;
 import javax.inject.Provider;
 import javax.inject.Singleton;
 
@@ -30,6 +31,7 @@ import org.apache.james.backends.cassandra.components.CassandraModule;
 import org.apache.james.backends.cassandra.components.CassandraTable;
 import org.apache.james.backends.cassandra.components.CassandraType;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionManager;
 
@@ -39,18 +41,25 @@ import com.datastax.driver.core.Session;
 @Singleton
 public class SessionWithInitializedTablesFactory implements Provider<Session> {
     private final CassandraModule module;
+    private final CassandraModule cacheModule;
     private final Session session;
+    private final Session cacheSession;
 
     @Inject
-    public SessionWithInitializedTablesFactory(ClusterConfiguration clusterConfiguration, Cluster cluster, CassandraModule module) {
+    public SessionWithInitializedTablesFactory(ClusterConfiguration clusterConfiguration,
+                                               Cluster cluster,
+                                               CassandraModule module,
+                                               @Named(InjectionNames.CACHE) CassandraModule cacheModule) {
         this.module = module;
+        this.cacheModule = cacheModule;
         this.session = createSession(cluster, clusterConfiguration.getKeyspace());
+        this.cacheSession = createCacheSession(cluster, clusterConfiguration.getKeyspace());
     }
 
     private Session createSession(Cluster cluster, String keyspace) {
         Session session = cluster.connect(keyspace);
         try {
-            if (allOperationsAreFullyPerformed(session)) {
+            if (allOperationsAreFullyPerformed(session, module)) {
                 new CassandraSchemaVersionDAO(session)
                     .updateVersion(CassandraSchemaVersionManager.MAX_VERSION)
                     .block();
@@ -62,17 +71,28 @@ public class SessionWithInitializedTablesFactory implements Provider<Session> {
         }
     }
 
-    private boolean allOperationsAreFullyPerformed(Session session) {
-        Stream<Boolean> operations = Stream.of(createTypes(session), createTables(session));
+    private Session createCacheSession(Cluster cluster, String keyspace) {
+        Session session = cluster.connect(keyspace);
+        try {
+            allOperationsAreFullyPerformed(session, cacheModule);
+            return session;
+        } catch (Exception e) {
+            session.close();
+            throw e;
+        }
+    }
+
+    private boolean allOperationsAreFullyPerformed(Session session, CassandraModule module) {
+        Stream<Boolean> operations = Stream.of(createTypes(session, module), createTables(session, module));
         return operations.allMatch(updated -> updated);
     }
 
-    private boolean createTypes(Session session) {
+    private boolean createTypes(Session session, CassandraModule module) {
         return new CassandraTypesCreator(module, session)
                 .initializeTypes() == CassandraType.InitializationStatus.FULL;
     }
 
-    private boolean createTables(Session session) {
+    private boolean createTables(Session session, CassandraModule module) {
         return new CassandraTableManager(module, session)
             .initializeTables() == CassandraTable.InitializationStatus.FULL;
     }
@@ -82,6 +102,10 @@ public class SessionWithInitializedTablesFactory implements Provider<Session> {
         return session;
     }
 
+    public Session getCacheSession() {
+        return cacheSession;
+    }
+
     @PreDestroy
     public synchronized void destroy() {
         session.close();
diff --git a/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/InjectionNames.java b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/InjectionNames.java
new file mode 100644
index 0000000..482b707
--- /dev/null
+++ b/backends-common/cassandra/src/main/java/org/apache/james/backends/cassandra/init/configuration/InjectionNames.java
@@ -0,0 +1,24 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james.backends.cassandra.init.configuration;
+
+public interface InjectionNames {
+    String CACHE = "cache";
+}
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
index 91c61ad..3be615e 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/CassandraCluster.java
@@ -61,7 +61,7 @@ public final class CassandraCluster implements AutoCloseable {
             .build();
         this.nonPrivilegedCluster = ClusterFactory.create(configuration);
         this.nonPrivilegedSession = new TestingSession(new SessionWithInitializedTablesFactory(configuration,
-            nonPrivilegedCluster, module).get());
+            nonPrivilegedCluster, module, CassandraModule.EMPTY_MODULE).get());
         this.typesProvider = new CassandraTypesProvider(module, nonPrivilegedSession);
     }
 
diff --git a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java
index 817fe85..c971fea 100644
--- a/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java
+++ b/backends-common/cassandra/src/test/java/org/apache/james/backends/cassandra/init/SessionWithInitializedTablesFactoryTest.java
@@ -126,7 +126,8 @@ class SessionWithInitializedTablesFactoryTest {
         return () -> new SessionWithInitializedTablesFactory(
                 clusterConfiguration,
                 cluster,
-                MODULE)
+                MODULE,
+                CassandraModule.EMPTY_MODULE)
             .get();
     }
 
diff --git a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
index 7f097b3..dcf3842 100644
--- a/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
+++ b/server/container/guice/cassandra-guice/src/main/java/org/apache/james/modules/mailbox/CassandraSessionModule.java
@@ -29,6 +29,7 @@ import org.apache.james.backends.cassandra.init.ResilientClusterProvider;
 import org.apache.james.backends.cassandra.init.SessionWithInitializedTablesFactory;
 import org.apache.james.backends.cassandra.init.configuration.CassandraConfiguration;
 import org.apache.james.backends.cassandra.init.configuration.ClusterConfiguration;
+import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
 import org.apache.james.backends.cassandra.utils.CassandraHealthCheck;
 import org.apache.james.backends.cassandra.utils.CassandraUtils;
 import org.apache.james.backends.cassandra.versions.CassandraSchemaVersionDAO;
@@ -52,6 +53,8 @@ import com.google.inject.Provides;
 import com.google.inject.Scopes;
 import com.google.inject.Singleton;
 import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
 
 public class CassandraSessionModule extends AbstractModule {
 
@@ -65,13 +68,14 @@ public class CassandraSessionModule extends AbstractModule {
     @Override
     protected void configure() {
         bind(CassandraUtils.class).in(Scopes.SINGLETON);
-        bind(Session.class).toProvider(SessionWithInitializedTablesFactory.class);
         bind(Cluster.class).toProvider(ResilientClusterProvider.class);
 
         Multibinder<CassandraModule> cassandraDataDefinitions = Multibinder.newSetBinder(binder(), CassandraModule.class);
         cassandraDataDefinitions.addBinding().toInstance(CassandraZonedDateTimeModule.MODULE);
         cassandraDataDefinitions.addBinding().toInstance(CassandraSchemaVersionModule.MODULE);
 
+        Multibinder.newSetBinder(binder(), CassandraModule.class, Names.named(InjectionNames.CACHE));
+
         bind(CassandraSchemaVersionManager.class).in(Scopes.SINGLETON);
         bind(CassandraSchemaVersionDAO.class).in(Scopes.SINGLETON);
 
@@ -85,10 +89,30 @@ public class CassandraSessionModule extends AbstractModule {
 
     @Provides
     @Singleton
+    Session provideSession(SessionWithInitializedTablesFactory sessionFactory) {
+        return sessionFactory.get();
+    }
+
+    @Named(InjectionNames.CACHE)
+    @Provides
+    @Singleton
+    Session provideCacheSession(SessionWithInitializedTablesFactory sessionFactory) {
+        return sessionFactory.getCacheSession();
+    }
+
+    @Provides
+    @Singleton
     CassandraModule composeDataDefinitions(Set<CassandraModule> modules) {
         return CassandraModule.aggregateModules(modules);
     }
 
+    @Named(InjectionNames.CACHE)
+    @Provides
+    @Singleton
+    CassandraModule composeCacheDefinitions(@Named(InjectionNames.CACHE) Set<CassandraModule> modules) {
+        return CassandraModule.aggregateModules(modules);
+    }
+
     @Provides
     @Singleton
     BatchSizes getBatchSizesConfiguration(PropertiesProvider propertiesProvider) {
diff --git a/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java
new file mode 100644
index 0000000..ca6f9a7
--- /dev/null
+++ b/server/container/guice/cassandra-guice/src/test/java/org/apache/james/CacheSessionTest.java
@@ -0,0 +1,103 @@
+/****************************************************************
+ * Licensed to the Apache Software Foundation (ASF) under one   *
+ * or more contributor license agreements.  See the NOTICE file *
+ * distributed with this work for additional information        *
+ * regarding copyright ownership.  The ASF licenses this file   *
+ * to you under the Apache License, Version 2.0 (the            *
+ * "License"); you may not use this file except in compliance   *
+ * with the License.  You may obtain a copy of the License at   *
+ *                                                              *
+ *   http://www.apache.org/licenses/LICENSE-2.0                 *
+ *                                                              *
+ * Unless required by applicable law or agreed to in writing,   *
+ * software distributed under the License is distributed on an  *
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
+ * KIND, either express or implied.  See the License for the    *
+ * specific language governing permissions and limitations      *
+ * under the License.                                           *
+ ****************************************************************/
+
+package org.apache.james;
+
+import static com.datastax.driver.core.querybuilder.QueryBuilder.select;
+import static org.apache.james.CassandraJamesServerMain.ALL_BUT_JMX_CASSANDRA_MODULE;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+import org.apache.james.backends.cassandra.components.CassandraModule;
+import org.apache.james.backends.cassandra.init.configuration.InjectionNames;
+import org.apache.james.lifecycle.api.StartUpCheck;
+import org.apache.james.modules.ConfigurationProbe;
+import org.apache.james.modules.TestJMAPServerModule;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import com.datastax.driver.core.DataType;
+import com.datastax.driver.core.Session;
+import com.google.inject.Inject;
+import com.google.inject.multibindings.Multibinder;
+import com.google.inject.name.Named;
+import com.google.inject.name.Names;
+
+class CacheSessionTest {
+    private static final String TABLE_NAME = "tablename";
+
+    static class CacheSessionTestCheck implements StartUpCheck {
+        static final String NAME = "CacheSessionTest-check";
+        private final Session cacheSession;
+
+        @Inject
+        CacheSessionTestCheck(@Named(InjectionNames.CACHE) Session cacheSession) {
+            this.cacheSession = cacheSession;
+        }
+
+        @Override
+        public CheckResult check() {
+            try {
+                cacheSession.execute(select().from(TABLE_NAME));
+                return CheckResult.builder()
+                    .checkName(NAME)
+                    .resultType(ResultType.GOOD)
+                    .build();
+            } catch (Exception e) {
+                return CheckResult.builder()
+                    .checkName(NAME)
+                    .resultType(ResultType.BAD)
+                    .description(String.format("%s do not exist", TABLE_NAME))
+                    .build();
+            }
+        }
+
+        @Override
+        public String checkName() {
+            return NAME;
+        }
+    }
+
+    @RegisterExtension
+    static JamesServerExtension testExtension = new JamesServerBuilder()
+        .extension(new DockerElasticSearchExtension())
+        .extension(new CassandraExtension())
+        .server(configuration -> GuiceJamesServer.forConfiguration(configuration)
+            .combineWith(ALL_BUT_JMX_CASSANDRA_MODULE)
+            .overrideWith(TestJMAPServerModule.limitToTenMessages()))
+        .overrideServerModule(binder -> Multibinder.newSetBinder(binder, CassandraModule.class, Names.named(InjectionNames.CACHE))
+            .addBinding()
+            .toInstance(CassandraModule.table(TABLE_NAME)
+                .comment("Testing table")
+                .statement(statement -> statement
+                    .addPartitionKey("id", DataType.timeuuid())
+                    .addClusteringColumn("clustering", DataType.bigint()))
+                .build()))
+        .overrideServerModule(binder -> Multibinder.newSetBinder(binder, StartUpCheck.class)
+            .addBinding()
+            .to(CacheSessionTestCheck.class))
+        .disableAutoStart()
+        .build();
+
+    @Test
+    void cacheTableShouldBeWellCreated(GuiceJamesServer jamesServer) {
+        assertThatCode(jamesServer::start)
+            .doesNotThrowAnyException();
+    }
+}


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