Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test container fails to stop when used as Spring bean #369

Closed
vpavic opened this issue Jun 14, 2017 · 36 comments
Closed

Test container fails to stop when used as Spring bean #369

vpavic opened this issue Jun 14, 2017 · 36 comments

Comments

@vpavic
Copy link
Contributor

vpavic commented Jun 14, 2017

If a container is used as a bean in Spring application (sort of like an embedded container) that runs on Tomcat, the container fails to stop when shutdown signal is sent.

Container is configured like this:

@Bean(initMethod = "start")
public GenericContainer redisContainer() {
	return new GenericContainer("redis:3.2.9").withExposedPorts(6379);
}

@Bean
public JedisConnectionFactory redisConnectionFactory() {
	JedisConnectionFactory connectionFactory = new JedisConnectionFactory();
	connectionFactory.setHostName(redisContainer().getContainerIpAddress());
	connectionFactory.setPort(redisContainer().getMappedPort(6379));
	return connectionFactory;
}

Shutdown error:

14-Jun-2017 14:48:10.987 SEVERE [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [ROOT] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@2b9ff8a8]) and a value of type [org.testcontainers.shaded.io.netty.util.internal.InternalThreadLocalMap] (value [org.testcontainers.shaded.io.netty.util.internal.InternalThreadLocalMap@4581e709]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
14-Jun-2017 14:48:10.988 SEVERE [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [ROOT] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@2b9ff8a8]) and a value of type [org.testcontainers.shaded.io.netty.util.internal.InternalThreadLocalMap] (value [org.testcontainers.shaded.io.netty.util.internal.InternalThreadLocalMap@13da7c79]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.
14-Jun-2017 14:48:10.988 SEVERE [localhost-startStop-2] org.apache.catalina.loader.WebappClassLoaderBase.checkThreadLocalMapForLeaks The web application [ROOT] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [java.lang.ThreadLocal@2b9ff8a8]) and a value of type [org.testcontainers.shaded.io.netty.util.internal.InternalThreadLocalMap] (value [org.testcontainers.shaded.io.netty.util.internal.InternalThreadLocalMap@34e88997]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak.

This leaves Tomcat hanging.

The sample (a simple Spring Boot app) to reproduce the problem is available in this repo. To reproduce the problem either run the integrationTest Gradle task using ./gradlew integrationTest (this uses gretty to integration test the app on Tomcat 8), or build the WAR and run it on Tomcat (in this case the -Dspring.profiles.active=redisContainer VM parameter is needed).

To give some background - we're in the process of migrating Spring Session integration tests to TestContainers (spring-projects/spring-session#798).

JUnit @ClassRule based integration tests were easy to migrate however the project also contains some sample application which are packaged as WARs and then integration tested on Tomcat 8 using gretty.

The idea was to provide a Spring profile which would start appropriate test container (in this case Redis) and configure the consumer (Redis connection factory) to connect to it but this has proved to be problematic due to reasons explained above.

@rnorth
Copy link
Member

rnorth commented Jun 14, 2017

Hmm, presumably there's nothing calling stop() on the container and that's what's leaving everything up in the air. The error you're getting, and the ThreadLocal it refers to, look unfamiliar, but I wonder if they correspond to unclosed DockerClient instances.

I'll try and have a better look tomorrow - I think we should be able to do something, it just might mean working out where the right hook point is to force a proper shutdown.

@vpavic
Copy link
Contributor Author

vpavic commented Jun 15, 2017

Thanks for the feedback @rnorth!

Well, with GenericContainer being AutoCloseable the Spring context should trigger it upon closing the application context, which is the same as having things explicit like this:

@Bean(initMethod = "start", destroyMethod = "stop")
public GenericContainer redisContainer() {
	return new GenericContainer("redis:3.2.9").withExposedPorts(6379);
}

@kiview
Copy link
Member

kiview commented Jun 23, 2017

I've discovered something which might help in pinpointing the actual bug. After changing the project to use an embedded Tomcat, an error will be thrown on shutdown:

2017-06-23 17:21:24.951  INFO 23777 --- [       Thread-6] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
2017-06-23 17:21:25.249 ERROR 23777 --- [rjava-netty-1-5] c.g.d.core.async.ResultCallbackTemplate  : Error during callback

com.github.dockerjava.api.exception.BadRequestException: {"message":"removal of container 3d5b469b19c0071ea3bf57c26ccee3633fec7ec3e2365376c5efd3d38ca91e55 is already in progress"}

This seems to be a race condition between the Spring Context calling the destroy method and the ResourceReaper being triggered by the JVM shutdown (

Runtime.getRuntime().addShutdownHook(new Thread(this::performCleanup));
).

I could reproduce the error when building a fat-jar with compile 'org.springframework.boot:spring-boot-starter-jetty. So the setup of using Gradle and gretty seems to add to the problem.

@kiview
Copy link
Member

kiview commented Jun 23, 2017

I've started to debug a litte more into the Spring code and I found, that destroySingletons() in DefaultSingletonBeanRegistry did correctly trigger the shutdown of the Testcontainers bean and the docker container was removed afterwards. However Tomcat still hangs on

2017-06-23 17:54:34.262  INFO 19593 --- [ost-startStop-1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

afterwards, so maybe we have to look deeper into Spring to find the actual reason?

@vpavic
Copy link
Contributor Author

vpavic commented Jun 23, 2017

Thanks for the feedback @kiview, we'll try to get a better look at Spring part of this.

@bsideup bsideup added this to the 1.4.0 milestone Jun 25, 2017
@bsideup
Copy link
Member

bsideup commented Jun 28, 2017

@vpavic
Small status update:
I see an issue on TestContainers' side, or maybe not an issue, but something we didn't consider yet.

When the test is being executed by JUnit, it calls System.exit(0) at the end of all tests. It makes interrupts all threads, including non-daemon ones.

When you run your app with TC inside some application server, it doesn't happen and for instance, Netty keeps its threads.

I'll look at it and will try to solve by providing additional API to "close" everything in TestContainers :)

@vpavic
Copy link
Contributor Author

vpavic commented Jun 28, 2017

Thanks for the update @bsideup!

Seeing this issue is assigned to 1.4.0 milestone which is scheduled for June 30 ATM, do you expect to have the described improvement in that timeline?

/cc @rwinch

@bsideup
Copy link
Member

bsideup commented Jun 28, 2017

@vpavic I'm working on it right now, but can't promise anything at this moment :) So far it seems that the problem might come from docker-java and not TC itself. If so, I'll try to workaround it, otherwise we have to wait until it's fixed & released there

@bsideup
Copy link
Member

bsideup commented Jul 7, 2017

@vpavic

Small status update:
After some digging, I've discovered a few places where threads are not released, mostly in docker-java.

However, with this PR I was able to achieve proper "exit 0" behaviour. The following snippet will exit once everything is cleaned & closed:

import org.testcontainers.DockerClientFactory;
import org.testcontainers.containers.GenericContainer;

public class Foo {

    public static void main(String[] args) throws Exception {
        try (GenericContainer container = new GenericContainer().withCommand("top")) {
            container.start();
        }

        DockerClientFactory.instance().client().close();

        System.out.println("QQQQ");
    }
}

I see some weird timeout-like behaviour after it prints "QQQQ", 30s or so, I think I know why will dig further, but at least it exits now :)
You can try it with jitpack ( https://www.testcontainers.org/usage.html#maven-dependencies ) or wait until 1.4.0, the release is around the corner :)

@vpavic
Copy link
Contributor Author

vpavic commented Jul 7, 2017

Thanks again for the update @bsideup!

I've tried the snapshot by applying the following diff to the original repro project:

diff --git a/build.gradle b/build.gradle
index 4aaa259..86009a8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -20,12 +20,13 @@ sourceCompatibility = 1.8
 
 repositories {
        jcenter()
+       maven { url 'https://jitpack.io' }
 }
 
 dependencies {
        compile 'org.springframework.boot:spring-boot-starter-data-redis'
        compile 'org.springframework.boot:spring-boot-starter-web'
-       compile 'org.testcontainers:testcontainers:1.3.0'
+       compile 'com.github.testcontainers.testcontainers-java:testcontainers:-SNAPSHOT'
 
        providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
 

After running the ./gradlew clean integrationTest --refresh-dependencies command the result is unfortunately still the same, it ends up hanging.

Is it possible that the snapshot is not yet updated with your changes?

@bsideup
Copy link
Member

bsideup commented Jul 7, 2017

@vpavic

it could be. Try compile 'com.github.testcontainers.testcontainers-java:testcontainers:3cce55a417' and let us know! Also, give it some time (like 30s or so), I see (locally) that it takes some time to exit (without doing anything, I'm still figuring out why)

@vpavic
Copy link
Contributor Author

vpavic commented Jul 7, 2017

Yeah, I saw your comment so I gave it a quite a bit more than 30 sec but it didn't help.
OK, will try to build it myself and let you know.

@bsideup
Copy link
Member

bsideup commented Jul 7, 2017

@vpavic FYI there was a mistake in my previous message (wrong GAV), I fixed it, sorry!

@bsideup
Copy link
Member

bsideup commented Jul 7, 2017

@vpavic D'oh! I just realized that you provided a sample project to test! Thanks a lot, I'll try it myself :) Sorry for bothering :)

@vpavic
Copy link
Contributor Author

vpavic commented Jul 7, 2017

Thanks @bsideup, I tried building locally twice but the project build is hanging for me at this point:

[INFO] Running org.testcontainers.junit.OutputStreamTest
13:52:24.369 DEBUG 🐳 [alpine:3.2] - Starting container: alpine:3.2
13:52:24.369 DEBUG 🐳 [alpine:3.2] - Trying to start container: alpine:3.2
13:52:24.369 DEBUG 🐳 [alpine:3.2] - Trying to start container: alpine:3.2 (attempt 1/1)
13:52:24.369 DEBUG 🐳 [alpine:3.2] - Starting container: alpine:3.2
13:52:24.370 INFO  🐳 [alpine:3.2] - Creating container for image: alpine:3.2
13:52:24.485 INFO  🐳 [alpine:3.2] - Starting container with ID: 23119a92bd0b08038e50c8d03dbdc3052eaef12cdaf0b81bac2c46ffe32a8f3a
13:52:24.977 INFO  🐳 [alpine:3.2] - Container alpine:3.2 is starting: 23119a92bd0b08038e50c8d03dbdc3052eaef12cdaf0b81bac2c46ffe32a8f3a
13:52:24.982 DEBUG org.testcontainers.containers.wait.HostPortWaitStrategy - Liveness check port of /cocky_wiles is empty. Not waiting.
13:52:24.982 INFO  🐳 [alpine:3.2] - Container alpine:3.2 started
13:52:30.028 DEBUG org.testcontainers.containers.output.WaitingConsumer - END: 

However that might be related to me being in an environment behind a corporate network proxy ATM.

@bsideup
Copy link
Member

bsideup commented Jul 7, 2017

@vpavic I got it working!

diff --git a/src/main/java/sample/TestContainerBeanApplication.java b/src/main/java/sample/TestContainerBeanApplication.java
index 01a912d..0df3997 100644
--- a/src/main/java/sample/TestContainerBeanApplication.java
+++ b/src/main/java/sample/TestContainerBeanApplication.java
@@ -42,7 +42,17 @@ public class TestContainerBeanApplication extends SpringBootServletInitializer
 
 		@Bean(initMethod = "start")
 		public GenericContainer redisContainer() {
-			return new GenericContainer("redis:3.2.9").withExposedPorts(6379);
+			return new GenericContainer("redis:3.2.9") {
+				@Override
+				public void close() {
+					super.close();
+					try {
+						dockerClient.close();
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+			}.withExposedPorts(6379);
 		}

Output:

14:42:19 INFO  Tomcat 8.0.33 started and listening on port 48080
14:42:19 INFO  testcontainer-bean runs at:
14:42:19 INFO    http://localhost:48080/testcontainer-bean
:integrationTest
Hello World!
:appAfterIntegrationTest
2017-07-07 14:42:19.678  INFO 4538 --- [           main] o.apache.catalina.core.StandardService   : Stopping service [Tomcat]
2017-07-07 14:42:19.680  INFO 4538 --- [ost-startStop-1] o.a.c.c.C.[.[.[/testcontainer-bean]      : Closing Spring root WebApplicationContext
2017-07-07 14:42:19.681  INFO 4538 --- [ost-startStop-1] ationConfigEmbeddedWebApplicationContext : Closing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@73ce805: startup date [Fri Jul 07 14:42:04 CEST 2017]; root of context hierarchy
2017-07-07 14:42:19.694  INFO 4538 --- [ost-startStop-1] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown
Server stopped.

BUILD SUCCESSFUL

Total time: 1 mins 37.544 secs

We need to close the client to allow JVM shutdown gracefully.

Here I use close method to call it because there was only 1 container, and this method must be called after all containers are closed.

Also as you can see it works with 1.3.0, no need to use the snapshot 🎉

@vpavic
Copy link
Contributor Author

vpavic commented Jul 7, 2017

That's great news, thanks for all your efforts @bsideup!

So we can expect the 1.4.0 release to ship with the handling of close operator similar to one from your workaround?

@bsideup
Copy link
Member

bsideup commented Jul 9, 2017

@vpavic unfortunately such behaviour should not be shipped as a default one, because for most scenarios global client should stay even if container is stopped (because next test might start a new container)

My workaround should work with 1.3.0/1.3.1 and very specific to your use case. We might think how to support such use case later, 1.5.0 maybe, but for now I recommend you to do it like that :)

Just to give you a bit more of technical background of the issue:

  1. Testcontainers starts a client
  2. starts a container
  3. Spring stops it on context's close
  4. client remains open, someone should close it as well, but not before the container is stopped

What workaround does:
When Spring calls .stop() on that container, we first call container's stop logic, and then close the global client.

@bsideup bsideup modified the milestones: 1.5.0, 1.4.0 Jul 9, 2017
@vpavic
Copy link
Contributor Author

vpavic commented Jul 10, 2017

Thanks for the insight and support @bsideup, we'll use the workaround you provided and keep an eye on future development on this topic.

@rwinch
Copy link
Contributor

rwinch commented Jul 10, 2017

Thanks for digging into this and providing a fix!

We might think how to support such use case later, 1.5.0 maybe, but for now I recommend you to do it like that :)

Would it potentially make sense to add a flag to GenericContainer that would close the docker client when genericContainer.close() is invoked?

@bsideup
Copy link
Member

bsideup commented Jul 11, 2017

@rwinch it would not because it uses a global (read - shared) docker client, and this client should be closed after all containers are stopped.
But for your case I'm thinking about changing Netty's ThreadPool in docker-java and setting the daemon flag on threads, so that it will not prevent JVM from exiting even if the client is not closed.

@samgurtman-zz
Copy link

Is there any current workaround for forcing those netty threads to close quickly? I would use System.exit(0) but that screws up maven exec:java. I'm successfully using TestContainers, Flyway and Jooq together to generate sources, but the 30 second delay on each build in a bit problematic.

@eksd
Copy link

eksd commented Dec 15, 2017

Any news about this issue?

@kiview
Copy link
Member

kiview commented Dec 15, 2017

@eksd Does this workaround work for you?
#369 (comment)

@eksd
Copy link

eksd commented Dec 18, 2017

@kiview , yes, it works, but with same side effect as @samgurtman has. Netty threads close too long.

@dadoonet
Copy link
Contributor

I think I'm hitting a similar issue. I'm running my tests using Randomized Testing framework which controls that there are no leaked threads when stopping.

Although I'm calling:

if (container != null) {
     container.close();
     container = null;
}

Randomized testing is complaining with:

com.carrotsearch.randomizedtesting.ThreadLeakError: There are still zombie threads that couldn't be terminated:
   1) Thread[id=21, name=dockerjava-netty-1-4, state=RUNNABLE, group=TGRP-FsCrawlerRestIT]
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native Method)
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native.java:76)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:158)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:149)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:219)
        at org.testcontainers.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at org.testcontainers.shaded.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
        at java.lang.Thread.run(Thread.java:748)
   2) Thread[id=27, name=dockerjava-netty-1-8, state=RUNNABLE, group=TGRP-FsCrawlerRestIT]
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native Method)
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native.java:76)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:158)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:149)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:219)
        at org.testcontainers.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at org.testcontainers.shaded.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
        at java.lang.Thread.run(Thread.java:748)
   3) Thread[id=18, name=threadDeathWatcher-3-1, state=TIMED_WAITING, group=TGRP-FsCrawlerRestIT]
        at java.lang.Thread.sleep(Native Method)
        at org.testcontainers.shaded.io.netty.util.ThreadDeathWatcher$Watcher.run(ThreadDeathWatcher.java:152)
        at org.testcontainers.shaded.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
        at java.lang.Thread.run(Thread.java:748)
   4) Thread[id=19, name=dockerjava-netty-1-2, state=RUNNABLE, group=TGRP-FsCrawlerRestIT]
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native Method)
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native.java:76)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:158)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:149)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:219)
        at org.testcontainers.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at org.testcontainers.shaded.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
        at java.lang.Thread.run(Thread.java:748)
   5) Thread[id=23, name=dockerjava-netty-1-5, state=RUNNABLE, group=TGRP-FsCrawlerRestIT]
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native Method)
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native.java:76)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:158)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:149)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:219)
        at org.testcontainers.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at org.testcontainers.shaded.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
        at java.lang.Thread.run(Thread.java:748)
   6) Thread[id=25, name=dockerjava-netty-1-7, state=RUNNABLE, group=TGRP-FsCrawlerRestIT]
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native Method)
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native.java:76)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:158)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:149)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:219)
        at org.testcontainers.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at org.testcontainers.shaded.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
        at java.lang.Thread.run(Thread.java:748)
   7) Thread[id=20, name=dockerjava-netty-1-3, state=RUNNABLE, group=TGRP-FsCrawlerRestIT]
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native Method)
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native.java:76)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:158)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:149)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:219)
        at org.testcontainers.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at org.testcontainers.shaded.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
        at java.lang.Thread.run(Thread.java:748)
   8) Thread[id=17, name=dockerjava-netty-1-1, state=RUNNABLE, group=TGRP-FsCrawlerRestIT]
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native Method)
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native.java:76)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:158)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:149)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:219)
        at org.testcontainers.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at org.testcontainers.shaded.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
        at java.lang.Thread.run(Thread.java:748)
   9) Thread[id=24, name=dockerjava-netty-1-6, state=RUNNABLE, group=TGRP-FsCrawlerRestIT]
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native Method)
        at org.testcontainers.shaded.io.netty.channel.kqueue.Native.keventWait(Native.java:76)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:158)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.kqueueWait(KQueueEventLoop.java:149)
        at org.testcontainers.shaded.io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:219)
        at org.testcontainers.shaded.io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:858)
        at org.testcontainers.shaded.io.netty.util.concurrent.DefaultThreadFactory$DefaultRunnableDecorator.run(DefaultThreadFactory.java:138)
        at java.lang.Thread.run(Thread.java:748)

	at __randomizedtesting.SeedInfo.seed([4C95F8E6A88F11D5]:0)

@bsideup
Copy link
Member

bsideup commented Feb 11, 2018

@dadoonet we're waiting for a release of docker-java where this PR was merged:
docker-java/docker-java#980

once it's released, we will be able to control Netty from our side and mark Netty threads as daemon threads, so that it will not prevent JVM from exiting.

I'll update this issue once it's done :)

@dadoonet
Copy link
Contributor

Awesome thanks!

@dadoonet
Copy link
Contributor

dadoonet commented Feb 11, 2018

In case someone had this, here is the workaround I'm using for now:

@ThreadLeakFilters(filters = {TestContainerThreadFilter.class})
@ThreadLeakScope(ThreadLeakScope.Scope.SUITE)
@ThreadLeakLingering(linger = 5000) // 5 sec lingering
public abstract class AbstractITCase extends AbstractFSCrawlerTestCase {
 //
}
public class TestContainerThreadFilter implements ThreadFilter {
    @Override
    public boolean reject(Thread t) {
        return t.getName().contains("dockerjava-netty") ||
                t.getName().contains("pool-2-thread-1") ||
                t.getName().contains("testcontainers") ||
                t.getName().contains("threadDeathWatcher");
    }
}

bsideup added a commit that referenced this issue Apr 17, 2018
bsideup added a commit that referenced this issue Apr 17, 2018
bsideup added a commit that referenced this issue Apr 17, 2018
@bsideup bsideup added this to the next milestone Apr 17, 2018
bsideup added a commit that referenced this issue Apr 18, 2018
* #369 Mark all long-living threads started by Testcontainers as daemons and group them

* simplify the classpath logic

* fixes

* Add a line about the logs to CHANGELOG and explain the DaemonTest and why we fork
@bsideup
Copy link
Member

bsideup commented Apr 20, 2018

@dadoonet @vpavic
I have great news for you!

We just released 1.7.1 with fully daemonized threads and even have a test for it now :)

@vpavic would be great if you can test and finally close this issue :D

@vpavic
Copy link
Contributor Author

vpavic commented Apr 20, 2018

Thanks for the update @bsideup, great news indeed!

ATM I still don't see 1.7.1 in Maven Central but will give it a shot at some point today and provide feedback.

@bsideup
Copy link
Member

bsideup commented Apr 20, 2018

@vpavic oh, yes, we use Bintray to do the releases, so it's in jcenter already, but will take ~15-20 min before appearing in Maven Central, sorry for not mentioning it :)

@bsideup
Copy link
Member

bsideup commented Apr 20, 2018

@vpavic
Copy link
Contributor Author

vpavic commented Apr 20, 2018

@bsideup I've upgraded Spring Session integration tests to TestContainers 1.7.1 and was able to remove the workaround you provided earlier.

We are however still experiencing memory leak log warnings reported in spring-projects/spring-session#869.

@bsideup
Copy link
Member

bsideup commented Apr 20, 2018

@vpavic good to know! The memory leak is something else and will be gone soon-ish when we replace our Netty transport with something less heavyweight (i.e. OkHttp), could you please report it separately? :)

@vpavic
Copy link
Contributor Author

vpavic commented Apr 20, 2018

Thanks for the quick follow up @bsideup! Closing this one, will open a new issue for memory leak topic in a moment.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants