-
Notifications
You must be signed in to change notification settings - Fork 310
Description
John Blum opened DATACASS-723 and commented
The o.s.d.c.core.cql.session.init.SessionFactoryInitializer is not sufficient to replace the o.s.d.c.config.AbstractSessionConfiguration class's now deprecated getStartupScripts() and getShutdownScripts() methods.
As as application developer using Spring Data for Apache Cassandra, if I want to specify a Cassandra Keyspace used by my application, then in my application configuration, I might do, or start with the following, which is very useful...
package ...;
import org.springframework.data.cassandra.config.AbstractCassandraConfiguration;
class MyCassandraApplicationConfiguration extends AbstractCassandraConfiguration {
@Override
public String getKeyspaceName() {
return "MyAppKeyspace";
}
...
}However, without also specifying the now deprecated methods, for example...
...
class MyCassandraApplicationConfiguration extends AbstractCassandraConfiguration {
@Override
protected List<String> getStartupScripts() {
return Collections.singletonList("schema.cql");
}
@Override
protected List<String> getShutdownScripts() {
...
}
...
} Where schema.cql is defined as...
CREATE KEYSPACE IF NOT EXISTS MyAppKeyspace WITH replication = { 'class':'SimpleStrategy', 'replication_factor':1 };
USE MyAppKeyspace;
CREATE TABLE IF NOT EXISTS customers (id BIGINT PRIMARY KEY, name TEXT);
CREATE INDEX IF NOT EXISTS CustomerNameIdx ON customers(name);
...Then the application will throw an Exception on startup stating that the named/specified Keyspace (i.e. MyAppKeyspace does not exist!
java.lang.IllegalStateException: Failed to load ApplicationContext
....
..
.
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'cassandraSessionFactory' defined in example.app.crm.config.CassandraConfiguration: Unsatisfied dependency expressed through method 'cassandraSessionFactory' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cassandraSession' defined in example.app.crm.config.CassandraConfiguration: Invocation of init method failed; nested exception is com.datastax.oss.driver.api.core.InvalidKeyspaceException: Invalid keyspace customerservice
at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:798)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:539)
....
..
.
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'cassandraSession' defined in example.app.crm.config.CassandraConfiguration: Invocation of init method failed; nested exception is com.datastax.oss.driver.api.core.InvalidKeyspaceException: Invalid keyspace customerservice
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1796)
...
..
.
Caused by: com.datastax.oss.driver.api.core.InvalidKeyspaceException: Invalid keyspace customerservice
at com.datastax.oss.driver.api.core.InvalidKeyspaceException.copy(InvalidKeyspaceException.java:34)
at com.datastax.oss.driver.internal.core.util.concurrent.CompletableFutures.getUninterruptibly(CompletableFutures.java:149)
at com.datastax.oss.driver.api.core.session.SessionBuilder.build(SessionBuilder.java:501)
at org.springframework.data.cassandra.config.CqlSessionFactoryBean.buildSession(CqlSessionFactoryBean.java:456)
at org.springframework.data.cassandra.config.CqlSessionFactoryBean.afterPropertiesSet(CqlSessionFactoryBean.java:427)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1855)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1792)
... 83 more
The SessionFactoryInitializer (or even the KeyspacePopulator registered on the SessionFactoryFactoryBean provided via extension of the AbstractCassandraConfiguration}) is, or are far too late in the initialization process to "initialize" the "application" (-defined) Keyspace using the Cassandra {{Session provided by the SD Cassandra SessionFactory, which is created by the CqlSessionFactoryBean definition and is based off the "name", application-defined Keyspace anyway.
Essentially the problem can be reproduced by:
-
First declaring the application
Keyspacename. -
The
Keyspacename is then configured (i.e. set) on the {{CqlSessionFactoryBean} bean definition declared in the
AbstractSessionConfigurationclass, which theAbstractCassandraConfigurationbase class, extended by application code to simplify configuration, extends.
NOTE: Notice that, fortunately, the
getStartupScripts()andgetShutdownScripts()methods are still used in SD Cassandra despite the deprecation and (e.g. this) comment.
NOTE: Because the
SessionFactoryInitializeris too late in the initialization process, it technically breaks the contract stated in the comment ofgetStartupScripts()andgetShutdownScripts()methods. However, the comment(s) are also ambiguous because they do not clarify that the startup and shutdown (CQL) scripts are applied before the application "named"Keyspaceis created/initialized, as will be witnessed in #3 following...
- Therefore, the only opportunity to "create" and "initialize" the application "named"
Keyspaceis directly after a SD Cassandra framework (internal)Sessionis opened to the Cassandra "system"Keyspaceand subsequently initialized that would then further allow additionalKeyspacesto be defined, created and initialized, via CQL scripts applied on startup.
NOTE: Indeed, if we trace through the code, we notice that the
keyspaceStartupScriptsare passed to theexecuteSpecsAndScripts(..)method, which ultimately executes the CQL statements. ThekeyspaceStartupScriptswere initialized from the now deprecatedAbstractCassandraConfiguration.getStartupScripts()method (also see here).
-
However, the very next thing to happen is that now the application "named"
Keyspaceis created, which leads to theIllegalStateExceptionshown above. -
If we tried to follow the logic of using a
SessionFactoryInitializerto perform the schema actions above, we'd see that A) theSessionconnected to the application "named"Keyspacewould then be supplied by theCqlSessionFactoryBeanbean definition from theAbstractSessionConfigurationclass (which again, the user's application would indirectly extend) to theSessionFactoryFactoryBeanbean definition declared in theAbstractCassandraConfigurationclass (the class our application configuration class extends). -
It is the
SessionFactoryFactoryBeanthat supplies theSessionFactorythat ultimately is post processed by the declaredSessionFactoryInitializersin the Spring context. -
Yet, as stated above, this is too late in the initialization process.
See comments below for possible solutions.
Affects: 3.0 M2 (Neumann)