Head's Up! These forums are read-only. All users and content have migrated. Please join us at community.neo4j.com.
04-13-2021 03:16 AM
Hi, I added Neo4j dependencies to an existing Spring Boot command line application — that is already connecting to a PostgreSQL database.
The issue is that after Neo4j connectivity addition, the application no longer exits after its main()
method finishes.
I've posted this on StackOverflow and received a suggestion to inspect the threads. In fact I saw that if no query is issued to the database, everything is fine and the application exits.
But if a query is executed, there are a bunch of Neo4j threads that stay active even though the main()
thread is finished.
I managed to replicate it in a smaller project, find the sources at the bottom (you'll need to provide database uri and credentials); just change the variable runQuery
to true
or false
to run or skip the query.
I'm using Spring Boot 2.3.9 with neo4j-java-driver-spring-boot-starter 4.2.4.0
/boot-neo4j-test/src/main/java/my/test/DatabaseGraphService.java
package my.test;
import javax.annotation.PreDestroy;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.neo4j.driver.Driver;
import org.springframework.stereotype.Service;
@Service
public class DatabaseGraphService {
private final Driver driver;
private final Logger LOG;
public DatabaseGraphService(Driver driver) {
this.driver = driver;
LOG = LogManager.getLogger(getClass());
}
@PreDestroy
public void close() throws Exception {
LOG.info("Shutting down Neo4j driver...");
}
public Driver getDriver() {
return driver;
}
}
/boot-neo4j-test/src/main/java/my/test/SpringBootConsoleApplication.java
package my.test;
import java.util.ArrayList;
import java.util.List;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.neo4j.driver.Record;
import org.neo4j.driver.Result;
import org.neo4j.driver.Session;
import org.neo4j.driver.Transaction;
import org.neo4j.driver.TransactionWork;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackages = { "my.test" })
public class SpringBootConsoleApplication implements CommandLineRunner {
@Autowired
private DatabaseGraphService graphService;
private static Logger LOG = LogManager.getLogger(SpringBootConsoleApplication.class);
public static void main(String[] args) {
LOG.info("STARTING THE APPLICATION");
SpringApplication.run(SpringBootConsoleApplication.class, args);
LOG.info("APPLICATION FINISHED");
}
@Override
public void run(String... args) {
LOG.info("EXECUTING : command line runner");
final boolean runQuery = true;
// wait a little to allow opening the process in VisualVM or similar
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
LOG.error(e.getMessage(), e);
}
try {
List<String> names = null;
if (runQuery) {
try (Session session = graphService.getDriver().session()) {
names = session.readTransaction(new TransactionWork<List<String>>() {
@Override
public List<String> execute(Transaction tx) {
List<String> names = new ArrayList<>();
Result result = tx.run("MATCH (a) RETURN a.name");
while (result.hasNext()) {
Record record = result.next();
names.add(record.get("name").asString());
}
return names;
}
});
}
}
LOG.info("Names found: " + (names == null ? 0 : names.size()));
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
/boot-neo4j-test/src/main/resources/application.properties
spring.main.web-application-type=NONE
org.neo4j.driver.uri=
org.neo4j.driver.authentication.username=
org.neo4j.driver.authentication.password=
/boot-neo4j-test/pom.xml
<?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 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>my.test</groupId>
<artifactId>boot-neo4j-test</artifactId>
<packaging>jar</packaging>
<version>0.0.1-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath></relativePath>
</parent>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- Local -->
<!-- Spring Boot -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.neo4j.driver</groupId>
<artifactId>neo4j-java-driver-spring-boot-starter</artifactId>
<version>4.2.4.0</version>
</dependency>
<!-- other -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
</dependencies>
</project>
04-15-2021 03:32 AM
I think Netty keeps an active thread pool for the driver.
You need to close the driver before your application exits.
04-15-2021 04:25 AM
Hi florent and thank you.
Though, I don't completely agree with you for the following opinions / questions (that ultimately led me to open an issue on Github):
main()
conclusion may not really be program termination: some threads may have been spawned and still have instructions to execute before the program is finally completed, so having to explicitly close the driver feels a bit error-prone to me04-15-2021 04:50 AM
You can disable the auto-configuration and explicitly manage the driver instantiation yourself, so that it is not instantiated when you do not need it.
Whether you rely on auto-configuration or not, the Driver lifetime management is the responsibility of your application.
All the sessions of the conference are now available online