Open spring-projects-issues opened 4 years ago
John Blum commented
This problem was first detected in Spring Boot for Apache Geode (SBDG), testing the Inline Caching capabilities provided by SBDG to back an Apache Geode cache with a persistent data store like Apache Cassandra to perform read/write-through or write-behind data access operations.
This test class driving the configuration and test cases lives here.
The configuration of Apache Cassandra using Spring Data for Apache Cassandra lives here.
When trying to use either a KeyspacePopulator
or SessionFactoryInitializer
, the IllegalStateException
noted in the description above was thrown
John Blum commented
I see 2 possible solutions (there maybe others as well):
First is to define getKeyspaceStartupScripts()
and getKeyspaceShutdownScripts()
methods in the AbstractCassandraConfiguration
class (again, see here) matching the CqlSessionFactoryBean
configuration properties (i.e. keyspaceStartupScripts
and keyspaceShutdownScripts
) and corresponding setters by the same name.
Alternatively (or additively), the SessionFactoryInitializer
bean definitions could be applied to the Session
object created (internally) by Spring Data for Apache Cassandra in the CqlSessionFactoryBean
class for the "system" Keyspace
. Again, see here. This would allow the deprecated getStartupScripts()
and getShutdownScripts()
methods to be truly replaceable by the SessionFactoryInitializer
as stated in the comment.
Of course, the SessionFactoryInitializers
would need to be looked up in the Spring container and applied manually to the "system" Keyspace
Session
object built and used by CqlSessionFactroyBean
since no Spring bean exists for the "system" Keyspace
Session
object, which is therefore not subject to bean post processing where the SessionFactoryInitializers
are applied.
If a bean definition were created for the "system" Keyspace
Session
object created by the CqlSessionFactoryBean
in addition to the application "named" Keyspace
based Session
object (the actual target Session
object intended to be created by the CqlSessionFactoryBean
), then the application "named" Keyspace
Session
object bean would need to be made the "primary" bean definition for the Session
since then there would be minimally 2 Session
beans in the Spring context created by Spring Data for Apache Cassandra. This gets tricky since users may also be defining their own CqlSessionFactoyBean
bean definitions to define additional Sessions
, most likely to different Keyspaces
. As such, I would not recommend this approach, although possible.
Mark Paluch commented
Keyspace creation and initialization were intentionally split with the driver upgrade. Previously, factory beans carried a lot of functionality that blurred the lines of responsibility. We have a clear separation now between keyspace creation and keyspace initialization.
In general, keyspace creation on startup through configuration classes is discouraged as all data frameworks require the database (which corresponds with the keyspace) already created.
AbstractSessionConfiguration
provides two entrypoints to keyspace creation:
getKeyspaceCreations()
getStartupScripts()
getStartupScripts()
had no clear responsibility and a verbatim script allows all sorts of CQL to run, therefore it's deprecated as of version 3.0.
Regarding the schema.cql
file from above, it already mixes the concerns of keyspace creation and keyspace initialization. To initialize a keyspace, we introduced KeyspacePopulator
to safely execute CQL scripts (i.e. an arrangement such as schema.cql
and data.cql
that define objects and data within a keyspace) within the context of a keyspace. SessionFactoryInitializer
and KeyspacePopulator
materialize the intentional split from CREATE KEYSPACE
statements.
SessionFactoryInitializer
is generally a standalone utility to achieve the same from above. That being said, I struggle to see which aspect is missing. Care to elaborate?
John Blum commented
Mark Paluch- Just acknowledging that I saw your reply comments, but need more time to respond to your questions. Will followup more shortly
John Blum opened DATACASS-723 and commented
The
o.s.d.c.core.cql.session.init.SessionFactoryInitializer
is not sufficient to replace theo.s.d.c.config.AbstractSessionConfiguration
class's now deprecatedgetStartupScripts()
andgetShutdownScripts()
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...
However, without also specifying the now deprecated methods, for example...
Where
schema.cql
is defined as...Then the application will throw an Exception on startup stating that the named/specified Keyspace (i.e.
MyAppKeyspace
does not exist!The
SessionFactoryInitializer
(or even theKeyspacePopulator
registered on theSessionFactoryFactoryBean
provided via extension of theAbstractCassandraConfiguration}) is, or are far too late in the initialization process to "initialize" the "application" (-defined) Keyspace using the Cassandra {{Session
provided by the SD CassandraSessionFactory
, which is created by theCqlSessionFactoryBean
definition and is based off the "name", application-defined Keyspace anyway.Essentially the problem can be reproduced by:
First declaring the application
Keyspace
name.The
Keyspace
name is then configured (i.e. set) on the {{CqlSessionFactoryBean} bean definition declared in theAbstractSessionConfiguration
class, which theAbstractCassandraConfiguration
base class, extended by application code to simplify configuration, extends.Keyspace
is directly after a SD Cassandra framework (internal)Session
is opened to the Cassandra "system"Keyspace
and subsequently initialized that would then further allow additionalKeyspaces
to be defined, created and initialized, via CQL scripts applied on startup.However, the very next thing to happen is that now the application "named"
Keyspace
is created, which leads to theIllegalStateException
shown above.If we tried to follow the logic of using a
SessionFactoryInitializer
to perform the schema actions above, we'd see that A) theSession
connected to the application "named"Keyspace
would then be supplied by theCqlSessionFactoryBean
bean definition from theAbstractSessionConfiguration
class (which again, the user's application would indirectly extend) to theSessionFactoryFactoryBean
bean definition declared in theAbstractCassandraConfiguration
class (the class our application configuration class extends).It is the
SessionFactoryFactoryBean
that supplies theSessionFactory
that ultimately is post processed by the declaredSessionFactoryInitializers
in 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)