0

I've added Neo4j connectivity to a simple Spring Boot command line application — it implements CommandLineRunner.

I've almost followed the examples in Neo4j driver manual, but my application doesn't terminate after main() finishes.
The only deviation from those sources is that the driver.close() method is not invoked at main() end, but in a @PreDestroy annotated method:

@Service
public class DatabaseGraphService {
    
    private volatile Driver driver;
    
    private final Logger LOG;
    
    public DatabaseGraphService() {
        LOG = LogManager.getLogger(getClass());
    }

    @PostConstruct
    private void initialize() throws Exception {
        driver = GraphDatabase.driver("bolt://localhost:7687", AuthTokens.basic("neo4j", "neo4j"));
    }
    
    @PreDestroy
    public void close() throws Exception {
        LOG.info("Shutting down Neo4j driver...");
        driver.close();
    }
    
    // ...
    
}

Main class:

@SpringBootApplication(scanBasePackages={ "it" })
public class SpringBootConsoleApplication implements CommandLineRunner {

    @Autowired
    private DatabaseGraphService graphService;
    
    private static Logger LOG = LoggerFactory
      .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");
    }
    
}

I run it from within Sprint Tools Suite (Eclipse-based IDE), this is what it logs:

i.c.c.e.SpringBootConsoleApplication     : APPLICATION FINISHED

but nothing else and the process stays active; when I hit the stop button, then:

inMXBeanRegistrar$SpringApplicationAdmin : Application shutdown requested.
i.c.c.services.DatabaseGraphService      : Shutting down Neo4j driver...
com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
[...]

If I run the program without executing any query, the process exists regularly. As long as I do a query - no matter if I consume all the results or do nothing with the Result object - it stays active.

Am I doing something wrong? Or maybe this is the expected behaviour, because when main() finishes it's just its thread that terminates?

Though, in the same application I'm connecting to a PostgreSQL instance, doing queries there too, but the application terminated correctly before adding the Neo4j connection.

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
watery
  • 5,026
  • 9
  • 52
  • 92
  • 1
    You are aware that you don't need to do this and Spring Boot will configure Neo4J for you (when included)? So I suggest to try that instead of doing it yourself. If you are using Neo4j I find it weird that Hikira is also part of the config? So depending on your dependencies things might still be running due to parts being auto configured. – M. Deinum Apr 13 '21 at 07:29
  • @M.Deinum Actually not, I was just following around Neo4j docs. Oh, just checked twice: I saw about Spring Data and Neo4j OGM but somehow didn't see the Boot Starter page! Thanks, I'll give that a check. About Hikari, the main datasource is a PostgreSQL database, Neo4j is a recent addition. – watery Apr 13 '21 at 07:39
  • @M.Deinum Nope, same result. I followed the instructions on the [Neo4j Java Driver Spring Boot Starter page](https://neo4j.com/developer/java-driver-spring-boot-starter/) — I'm using Boot 2.3.9 — but the application doesn't terminate. – watery Apr 13 '21 at 08:26
  • As mentioned there are probably more things started then only your command line job (maybe a monitoring thread? Didyou include actuator and/or spring-web etc.). Could you post/add more info as there is too little information currently. Did is only occur after adding Neo4j or already before that? – M. Deinum Apr 13 '21 at 08:46
  • @M.Deinum I'll try to post more code. In the meantime, I can tell you this started to show up only after adding Neo4j. Moreover, if the dependency / connection / etc is there, but no query is actually run toward Neo4j, the application terminates. It only stays active if a query is executed, seems like the connection pool "stays on". – watery Apr 13 '21 at 08:54
  • Looks like an issue with a rogue thread in the Neo4J driver. When it hangs you can take a threaddump and check what is happening. – M. Deinum Apr 13 '21 at 08:59
  • @M.Deinum I guess that's it. I just opened the process in VisualVM and there are three `Neo4jDriverIO` threads, started only after the first query, still sitting there after `main` finished. Reminds me of the times of webapp application leaks in Tomcat due to MySQL threads not stopping. – watery Apr 13 '21 at 09:28
  • I thought I did see an issue with that in the Spring BOot issue tracker but that was for Redis and the Lettuce driver. You might want to raise an issue with either Spring Boot (although it probably needs to be fixed in Neo4J) or Neo4J. – M. Deinum Apr 13 '21 at 09:33
  • @M.Deinum Sure. I'm going to post this on their community forum first right now. Thank you very much. – watery Apr 13 '21 at 09:36
  • @M.Deinum Just a quick follow up: looks like [my request didn't pass throught](https://github.com/neo4j/neo4j-java-driver/issues/877). – watery May 03 '21 at 19:15

1 Answers1

1

Neo4j Java Driver manages internal threads that are responsible for network communication. It bootstraps them on first API usage that involves networking.

Your application does not exit because those threads keep running. To shut them down, you have to call Driver's close method. 4.3.4 Javadoc snippet:

Close all the resources assigned to this driver, including open connections and IO threads.

In the described setup, it should be called on interrupt signal (CTRL+C) when running packaged JAR.

Starting from version 4.3.2, those threads have been updated to be daemon threads so that they do not prevent applications from finishing execution. In addition, this change has also been backported to versions 4.2.7 and 4.1.4.

injectives
  • 31
  • 2
  • While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. - [From Review](/review/late-answers/30173232) – Tapan Hegde Oct 25 '21 at 15:40
  • 1
    This answer actually contained essential information and had links for extra context. However, I revised it and inlined Javadoc for more convenience. – injectives Oct 27 '21 at 15:55