codeconsole opened a new pull request, #16338: URL: https://github.com/apache/grails-core/pull/16338
### Problem Neo4j and MongoDB functional jobs on `8.0.x` were cancelled at GitHub's six-hour limit six times between 2026-08-26 and 2026-09-11, most recently [this Neo4j job](https://github.com/apache/grails-core/actions/runs/34655597581/job/103452387382). Each hung in `:grails-data-mongodb-embedded:test`: three after every spec had passed, and three while `stopping a server that is already stopped is what a shutdown hook does` was running. An ordinary shutdown stops the embedded server twice. `EmbeddedMongoLifecycle` stops it when the application context closes, and the shutdown hook `EmbeddedMongoInitializer` registers stops it again as the JVM exits. The in-memory backend called `MongoServer.shutdownNow()` both times: ```java @Override public void stop() { this.server.shutdownNow(); this.running = false; } ``` A second `shutdownNow()` can wait forever. A client that connected just as the first shutdown began can be registered by Netty after its event loop has closed its channels, so the loop terminates with the client still in mongo-java-server's channel group, and `closeClients()` waits, with no timeout, on a close the terminated loop never completes. Gradle's test worker exits through `System.exit`, which waits for every shutdown hook, so the task never finished. The Neo4j jobs ran these tests at all because `mongodb-test-config.gradle` did not skip its suites under `-PonlyNeo4jTests`. ### Fix `RunningInMemoryMongo.stop()` returns when the server is already stopped, and `stop()` and `restart()` are synchronized, since the context's shutdown hook and the initializer's run at the same time on the way out: ```java @Override public synchronized void stop() { if (!this.running) { return; } this.server.shutdownNow(); this.running = false; } ``` The rest is the test and CI configuration this exposed: - `EmbeddedMongoReplicaSetSpec` stops its servers through the lifecycle bean. It called `close()` on contexts that were never refreshed, which does nothing, so its four mongods were left for the shutdown hook. - `mongodb-test-config.gradle`, `hibernate5-test-config.gradle`, `hibernate7-test-config.gradle` and `redis-test-config.gradle` skip their suites under `-PonlyNeo4jTests`. The Neo4j jobs had been running the MongoDB suites and 19 Hibernate test tasks that the MongoDB and Hibernate jobs already run. - `grails-redis` applies only `redis-test-config.gradle`, as the Hibernate and MongoDB modules apply only their own config. With `test-config.gradle` applied as well, its `test` and `integrationTest` tasks ran in no CI job, the Redis jobs included. - The nine test jobs set `timeout-minutes` to twice the slowest run that finished on its own, passed or failed, over the 160 CI runs from 2026-09-03 to 2026-09-13, rounded up to the half hour: 90 minutes for the Spring Security config and CAS jobs, 120 to 210 for the rest. The build jobs keep GitHub's default, since the macOS build has passed after more than five hours. The unbounded wait itself is fixed in mongo-java-server by [bwaldvogel/mongo-java-server#256](https://github.com/bwaldvogel/mongo-java-server/pull/256). **Worth calling out:** - `grails-redis` no longer takes the heap, fork, `--add-opens` and CI compile-cache settings from `test-config.gradle`, the same as the Hibernate and MongoDB modules. Its tests pass without them against Redis 7.4 and 8.0. - The timeouts come from ten days of runs, and a job whose suite grows past half of its limit will need a higher one. ### Tests `an application whose context stopped its in-memory server still exits` runs an application in a JVM of its own that lets a client connect, stops the server as its context closes, and returns from `main`. Without the fix that JVM never exited in 8 of 8 runs, and the feature fails after 30 seconds instead of hanging; with the fix the JVM exits in about a second. The application, `ContextStoppedServerApplication`, is written in Java: started through Groovy and Spock, the same steps almost never hang even unfixed. The existing feature that stops a server twice now waits a bounded time for the second stop, so a regression fails instead of hanging the build. `:grails-data-mongodb-embedded:test`, and `:grails-redis:test` with `:grails-redis:integrationTest`, are green. -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
