liquibase/liquibase-neo4j

Documentation for Spring boot integration and potential bug

marcindz88 opened this issue · 7 comments

Hi,
I was trying to configure liquibase-neo4j to run on spring boot startup, but encountered weird behavior where it runs an endless loop to acquire a lock "unique_liquibase_lock" and no changesets are executed. I tried to change the changesets but it does not change the situation.

Could you add documentation on how to configure liquibase-neo4j with spring boot because maybe I am doing something wrong.

My configuration currently:

application.yaml:

spring:
  datasource:
    driver-class-name: liquibase.ext.neo4j.database.jdbc.Neo4jDriver
    url: jdbc:neo4j:bolt://localhost:7687
    username: neo4j
    password: xxxxxxxxx

  liquibase:
    change-log: classpath:db/changelog/db.changelog-master.xml

pom.xml

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.liquibase.ext</groupId>
            <artifactId>liquibase-neo4j</artifactId>
        </dependency>
    </dependencies>

Spring boot version: 3.1.4
Liquibase-neo4j version 4.24.0

I get repeating logs like following:

2023-10-12T23:33:48.604+02:00 DEBUG 24056 --- [pool-4-thread-1] liquibase.executor                       : CREATE CONSTRAINT `unique_liquibase_lock` IF NOT EXISTS FOR (n:`__LiquibaseLock`) REQUIRE n.`lockedBy` IS UNIQUE
2023-10-12T23:33:48.611+02:00 DEBUG 24056 --- [pool-4-thread-1] liquibase.executor                       : 0 row(s) affected

After a while application crashes and logs are as follows:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguration$LiquibaseConfiguration.class]: Could not acquire change log lock despite no lock currently stored

Caused by: liquibase.exception.LockException: Could not acquire change log lock despite no lock currently stored
	at liquibase.ext.neo4j.lockservice.Neo4jLockService.waitForLock(Neo4jLockService.java:73) ~[liquibase-neo4j-4.24.0.jar:4.24.0]
	at liquibase.Liquibase.lambda$update$1(Liquibase.java:243) ~[liquibase-core-4.20.0.jar:na]
	at liquibase.Scope.lambda$child$0(Scope.java:187) ~[liquibase-core-4.20.0.jar:na]
	at liquibase.Scope.child(Scope.java:196) ~[liquibase-core-4.20.0.jar:na]
	at liquibase.Scope.child(Scope.java:186) ~[liquibase-core-4.20.0.jar:na]
	at liquibase.Scope.child(Scope.java:165) ~[liquibase-core-4.20.0.jar:na]
	at liquibase.Liquibase.runInScope(Liquibase.java:2639) ~[liquibase-core-4.20.0.jar:na]
	at liquibase.Liquibase.update(Liquibase.java:236) ~[liquibase-core-4.20.0.jar:na]
	at liquibase.Liquibase.update(Liquibase.java:221) ~[liquibase-core-4.20.0.jar:na]
	at liquibase.integration.spring.SpringLiquibase.performUpdate(SpringLiquibase.java:329) ~[liquibase-core-4.20.0.jar:na]
	at liquibase.integration.spring.SpringLiquibase.afterPropertiesSet(SpringLiquibase.java:284) ~[liquibase-core-4.20.0.jar:na]

Caused by: java.util.concurrent.ExecutionException: java.lang.RuntimeException: Cannot run more queries in this transaction, it has either experienced an fatal error or was explicitly terminated [Failed SQL: CREATE CONSTRAINT `unique_liquibase_lock` IF NOT EXISTS FOR (n:`__LiquibaseLock`) REQUIRE n.`lockedBy` IS UNIQUE]
	at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:122) ~[na:na]
	at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:205) ~[na:na]

Hello, @marcindz88, thanks for the report.
Let me try to reproduce the error.

@marcindz88 can you try https://github.com/fbiville/liquibase-neo4j-issue-479 and tweak it if necessary (please send a PR to that repo if that's the case)?
I could not reproduce the issue you are having, I'm probably missing a few details?

Side note: I highly recommend the use of spring.liquibase.driver-class-name instead of spring.datasource.driver-class-name if you can.
liquibase.ext.neo4j.database.jdbc.Neo4jDriver is not a general-purpose JDBC implementation, but one tailored for Liquibase usage.

Hi, thanks a lot for a quick reply.
I found what the issue was...
I have set the version of liquibase-neo4j to 4.2.4, and did not set the version for liquibase (as it is inherited from spring default config 4.2.0). I could not find what was the difference between your reproduction repo and my code. The difference was that you named the version property for liquibase-neo4j and liquibase as liquibase.version which overrided spring default version. I had a different name, and hence the versions were not compatible...
Anyway, thanks for the example, I was struggling to find one.

Could you tell me how can I set the configuration in spring.liquibase.driver-class-name and not in spring.datasource as I am not using spring jdbc (I am using spring-boot-starter-data-neo4j) and it throws an exception as follows:

Description:
Failed to configure a DataSource: 'url' attribute is not specified and no embedded datasource could be configured.
Reason: Failed to determine a suitable driver class
Action:
Consider the following:
	If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.
	If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active).

I have neo4j configuration in different location as I am using spring-data-neo4j:

spring:
  neo4j:
    uri: neo4j://localhost:7687
    authentication:
      username: neo4j
      password: xxxxx

I would have to get another dependency for neo4j jdbc client to set it in spring.datasource, while I am using JDBC only for liquibase.

@marcindz88 unfortunately, there is a hard dependency in Spring Boot Liquibase autoconfiguration with the JDBC starter that I do not really understand.

Ideally, the JDBC starter would be removed entirely, but then Liquibase is not configured at all.
The autoconfiguration report explains why:

LiquibaseAutoConfiguration.LiquibaseConfiguration:
      Did not match:
         - @ConditionalOnClass did not find required class 'org.springframework.jdbc.core.ConnectionCallback' (OnClassCondition)

I think the only way around this is for liquibase-neo4j to bring its own autoconfiguration class.

Ok, I think that there should be a way for setting up liquibase without jdbc starter, but for now I will stay with current configuration.
Thanks a lot for help.

@marcindz88 I agree with you.
I opened spring-projects/spring-boot#37936 but that won't be implemented just yet.
In the meantime, the extension (or a new downstream project) should provide a SpringLiquibase bean that just works without JDBC starter/