package fr.ippon.tatami.config; import me.prettyprint.cassandra.connection.HOpTimer; import me.prettyprint.cassandra.connection.MetricsOpTimer; import me.prettyprint.cassandra.model.ConfigurableConsistencyLevel; import me.prettyprint.cassandra.service.CassandraHostConfigurator; import me.prettyprint.cassandra.service.ThriftCfDef; import me.prettyprint.cassandra.service.ThriftCluster; import me.prettyprint.cassandra.service.ThriftKsDef; import me.prettyprint.hector.api.Cluster; import me.prettyprint.hector.api.HConsistencyLevel; import me.prettyprint.hector.api.Keyspace; import me.prettyprint.hector.api.ddl.ColumnFamilyDefinition; import me.prettyprint.hector.api.ddl.ComparatorType; import me.prettyprint.hector.api.ddl.KeyspaceDefinition; import me.prettyprint.hector.api.factory.HFactory; import me.prettyprint.hom.EntityManagerImpl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; import javax.annotation.PreDestroy; import javax.inject.Inject; import java.util.ArrayList; import java.util.List; /** * Cassandra configuration file. * * @author Julien Dubois */ @Configuration public class CassandraConfiguration { private final Logger log = LoggerFactory.getLogger(CassandraConfiguration.class); @Inject private Environment env; private Cluster myCluster; @PreDestroy public void destroy() { log.info("Closing Hector connection pool"); myCluster.getConnectionManager().shutdown(); HFactory.shutdownCluster(myCluster); } @Bean public Keyspace keyspaceOperator() { log.info("Configuring Cassandra keyspace"); String cassandraHost = env.getProperty("cassandra.host"); String cassandraClusterName = env.getProperty("cassandra.clusterName"); String cassandraKeyspace = env.getProperty("cassandra.keyspace"); CassandraHostConfigurator cassandraHostConfigurator = new CassandraHostConfigurator(cassandraHost); cassandraHostConfigurator.setMaxActive(100); if (env.acceptsProfiles(Constants.SPRING_PROFILE_METRICS)) { log.debug("Cassandra Metrics monitoring enabled"); HOpTimer hOpTimer = new MetricsOpTimer(cassandraClusterName); cassandraHostConfigurator.setOpTimer(hOpTimer); } ThriftCluster cluster = new ThriftCluster(cassandraClusterName, cassandraHostConfigurator); this.myCluster = cluster; // Keep a pointer to the cluster, as Hector is buggy and can't find it again... ConfigurableConsistencyLevel consistencyLevelPolicy = new ConfigurableConsistencyLevel(); consistencyLevelPolicy.setDefaultReadConsistencyLevel(HConsistencyLevel.ONE); KeyspaceDefinition keyspaceDef = cluster.describeKeyspace(cassandraKeyspace); if (keyspaceDef == null) { log.warn("Keyspace \" {} \" does not exist, creating it!", cassandraKeyspace); keyspaceDef = new ThriftKsDef(cassandraKeyspace); cluster.addKeyspace(keyspaceDef, true); addColumnFamily(cluster, ColumnFamilyKeys.USER_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.FRIENDS_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.FOLLOWERS_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.STATUS_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.DOMAIN_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.REGISTRATION_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.RSS_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.MAILDIGEST_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.SHARES_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.DISCUSSION_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.USER_TAGS_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.TAG_FOLLOWERS_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.GROUP_MEMBERS_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.USER_GROUPS_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.GROUP_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.GROUP_DETAILS_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.ATTACHMENT_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.AVATAR_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.DOMAIN_CONFIGURATION_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.TATAMIBOT_CONFIGURATION_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.APPLE_DEVICE_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.BLOCK_USERS_CF, 0); addColumnFamily(cluster, ColumnFamilyKeys.STATUS_REPORT_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.TIMELINE_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.TIMELINE_SHARES_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.MENTIONLINE_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.USERLINE_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.USERLINE_SHARES_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.FAVLINE_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.TAGLINE_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.TRENDS_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.USER_TRENDS_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.GROUPLINE_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.USER_ATTACHMENT_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.STATUS_ATTACHMENT_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.DOMAINLINE_CF, 0); addColumnFamilySortedbyUUID(cluster, ColumnFamilyKeys.DOMAIN_TATAMIBOT_CF, 0); addColumnFamilyCounter(cluster, ColumnFamilyKeys.COUNTER_CF, 0); addColumnFamilyCounter(cluster, ColumnFamilyKeys.TAG_COUNTER_CF, 0); addColumnFamilyCounter(cluster, ColumnFamilyKeys.GROUP_COUNTER_CF, 0); addColumnFamilyCounter(cluster, ColumnFamilyKeys.DAYLINE_CF, 0); //Tatami Bot CF addColumnFamily(cluster, ColumnFamilyKeys.TATAMIBOT_DUPLICATE_CF, 0); } else { /* This case only concerns the creation of new CF that didn't exist in the previous version of Tatami. As we cannot afford to drop the keyspace in production, this case is an update of the database that should be executed only one time in production. */ List<ColumnFamilyDefinition> lcf = keyspaceDef.getCfDefs(); List<String> lcfNames = new ArrayList<String>(); for(ColumnFamilyDefinition cfd : lcf){ lcfNames.add(cfd.getName()); } // The new tables if(!lcfNames.contains(ColumnFamilyKeys.BLOCK_USERS_CF)){ addColumnFamily(cluster, ColumnFamilyKeys.BLOCK_USERS_CF, 0); log.debug("{} column family successfully created", ColumnFamilyKeys.BLOCK_USERS_CF); } if(!lcfNames.contains(ColumnFamilyKeys.STATUS_REPORT_CF)){ addColumnFamily(cluster, ColumnFamilyKeys.STATUS_REPORT_CF, 0); log.debug("{} column family successfully created", ColumnFamilyKeys.STATUS_REPORT_CF); } } return HFactory.createKeyspace(cassandraKeyspace, cluster, consistencyLevelPolicy); } @Bean public EntityManagerImpl entityManager(Keyspace keyspace) { String[] packagesToScan = {"fr.ippon.tatami.domain", "fr.ippon.tatami.bot.config"}; return new EntityManagerImpl(keyspace, packagesToScan); } private void addColumnFamily(ThriftCluster cluster, String cfName, int rowCacheKeysToSave) { String cassandraKeyspace = this.env.getProperty("cassandra.keyspace"); ColumnFamilyDefinition cfd = HFactory.createColumnFamilyDefinition(cassandraKeyspace, cfName); cfd.setRowCacheKeysToSave(rowCacheKeysToSave); cluster.addColumnFamily(cfd); } private void addColumnFamilySortedbyUUID(ThriftCluster cluster, String cfName, int rowCacheKeysToSave) { String cassandraKeyspace = this.env.getProperty("cassandra.keyspace"); ColumnFamilyDefinition cfd = HFactory.createColumnFamilyDefinition(cassandraKeyspace, cfName); cfd.setRowCacheKeysToSave(rowCacheKeysToSave); cfd.setComparatorType(ComparatorType.UUIDTYPE); cluster.addColumnFamily(cfd); } private void addColumnFamilyCounter(ThriftCluster cluster, String cfName, int rowCacheKeysToSave) { String cassandraKeyspace = this.env.getProperty("cassandra.keyspace"); ThriftCfDef cfd = new ThriftCfDef(cassandraKeyspace, cfName, ComparatorType.UTF8TYPE); cfd.setRowCacheKeysToSave(rowCacheKeysToSave); cfd.setDefaultValidationClass(ComparatorType.COUNTERTYPE.getClassName()); cluster.addColumnFamily(cfd); } }