Elena
Elena

Reputation: 501

Spring data with cassandra giving IllegalStateException

I'm completly new to cassandra, so my error might be obvious.

I'm trying to create an application with spring boot (version 2.3.0.M2) that contacts a cassandra (version 3.11.6) installed in localhost.

I've got an java.lang.IllegalStateException with the message: Since you provided explicit contact points, the local DC must be explicitly set (see basic.load-balancing-policy.local-datacenter in the config, or set it programmatically with SessionBuilder.withLocalDatacenter). Current contact points are: Node(endPoint=localhost:9042, hostId=16a785a4-eaf3-4a4d-a216-5244d75206aa, hashCode=7b0b99d7)=datacenter1. Current DCs in this cluster are: datacenter1

My pom is the following one:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.0.M2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.test</groupId>
    <artifactId>cassandra</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>cassandra</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-cassandra</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </repository>
    </repositories>
    <pluginRepositories>
        <pluginRepository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
        </pluginRepository>
    </pluginRepositories>
</project>

In the application code, I've got a the following configuration class:

public class CassandraConfig extends AbstractCassandraConfiguration {

    public static final String KEYSPACE = "test_keyspace";


    @Override
    public SchemaAction getSchemaAction() {
        return SchemaAction.CREATE_IF_NOT_EXISTS;
    }

    @Override
    protected List<CreateKeyspaceSpecification> getKeyspaceCreations() {
        CreateKeyspaceSpecification specification = CreateKeyspaceSpecification.createKeyspace(KEYSPACE);

        return Arrays.asList(specification);
    }

    @Override
    protected List<DropKeyspaceSpecification> getKeyspaceDrops() {
        return Arrays.asList(DropKeyspaceSpecification.dropKeyspace(KEYSPACE));
    }

    @Override
    protected String getKeyspaceName() {
        return KEYSPACE;
    }

    @Override
    public String[] getEntityBasePackages() {
        return new String[]{"com.test.cassandra.entity"};
    }
}

My properties file contains the following configuration:

spring.data.cassandara.keyspace-name=test_keyspace
spring.data.cassandra.contact-points=localhost
spring.data.cassanda.port=9042
spring.data.cassandra.schema-act=create_if_not_exists

I also have tried with

spring.data.cassandra.contact-points=dc1

When I execute the application, I got see from the logs that I'm using the following version DataStax Java driver for Apache Cassandra(R) (com.datastax.oss:java-driver-core) version 4.4.0

In the cassandra-rackdc.properties I've set the name to

dc=dc1

I've done several test adding configuration parameters, even adding an application.conf to the classpath as described datastax documentation, but didn't have any success. Any clue where should I do it?

Upvotes: 14

Views: 17705

Answers (7)

user21104075
user21104075

Reputation: 109

Since Spring Boot 3.0 Cassandra properties' prefix is spring.cassandra.*, not spring.data.cassandra.* (specifically since 3.0.0-M5, as can be seen from @ConfigurationProperties.prefix value here and here),

so you need to write

spring.cassandra.local-datacenter=datacenter1

in your application.properties file to set local datacenter

Upvotes: 10

Ali
Ali

Reputation: 2678

I solved this problem by setting local datacenter like this :

application properties file : cassandra.local.datacenter=datacenter1

@Value("${cassandra.local.datacenter}")
private String localDatacenter;

@Bean
@Override
public CqlSessionFactoryBean cassandraSession() {
    CqlSessionFactoryBean cqlSessionFactoryBean = new CqlSessionFactoryBean();
    cqlSessionFactoryBean.setUsername(username);
    cqlSessionFactoryBean.setPassword(password);
    cqlSessionFactoryBean.setKeyspaceName(keySpace);
    cqlSessionFactoryBean.setLocalDatacenter(localDatacenter);
    cqlSessionFactoryBean.setPort(port);
    cqlSessionFactoryBean.setContactPoints(contactPoint);
    return cqlSessionFactoryBean;
}

Note : This is not all of the configuration code. I just wanted to show local datacenter configuration by this code snippet.

Upvotes: 0

ken4ward
ken4ward

Reputation: 2296

There's actually a different config needed for Apache Cassandra to truly work with the new spring versions. It took me about 50 hours of searching before finding this out on this documentation - https://docs.spring.io/spring-data/cassandra/docs/current/reference/html/#cassandra.cassandra-java-config

You'll need to create a new config file as specified there. To do this, follow thus:

  1. Create a new package named config

  2. Create a .java class file in the package named - CassandraConfig

  3. Copy and paste the below code into it.

    @Configuration public class CassandraConfig { public @Bean CqlSession session() { return CqlSession.builder().withKeyspace("mykeyspacename").build(); } }

Note, change the Keyspace name to your Keyspace name defined in your property file. If you have not created a Keyspace, start Cassandra server on your system, login to the cqlsh on the terminal or cmd by issuing cqlsh, and issue this command -

CREATE KEYSPACE IF NOT EXISTS mykeyspacename WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 } AND DURABLE_WRITES = true ;

By now, when you start the spring boot app, it should run without any error.

Upvotes: 2

Aseem Savio
Aseem Savio

Reputation: 690

Run nodetool status in the Cassandra shell. It gives you the datacenter name.

> root@d21e7b0dfc44:/# nodetool status
Datacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
--  Address     Load        Tokens  Owns (effective)  Host ID                               Rack 
UN  172.17.0.2  143.37 KiB  16      ?                 a3584baf-0665-474b-84e9-ba30528a7781  rack1
root@d21e7b0dfc44:/# 

In my case, it is "datacenter1". This goes inside the properties file like so:

spring.data.cassandra.local-datacenter=datacenter1

Upvotes: 6

Abhijit
Abhijit

Reputation: 832

Its mentioned in your error logs "Current DCs in this cluster are: datacenter1"

Just add spring.data.cassandra.local-datacenter=datacenter1 in application.properties

Upvotes: 0

Alexander Revkov
Alexander Revkov

Reputation: 223

Simply add

spring.data.cassandra.local-datacenter=DC1

to your application.properties file, where DC1 your local datacenter name (default DC1).

Upvotes: 18

NikolaB
NikolaB

Reputation: 4936

Since Spring Boot 2.3.0.X you need to explicitly provide the name of the local datacenter (because of the added Cassandra driver 4.X support which requires local datacenter to be set explicitly). You can do this by overrirding the getLocalDataCenter method in AbstractSessionConfiguration. AbstractCassandraConfiguration extends this class so you just need to add in your CassandraConfig class:

@Override
protected String getLocalDataCenter() {
    return "dc1";
}

Upvotes: 9

Related Questions