spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
74.81k stars 40.6k forks source link

Application does not terminate upon completion of CommandLineRunner anymore #25885

Closed eduardobarrena-tc closed 3 years ago

eduardobarrena-tc commented 3 years ago

I run some java batch processes using this code structure:

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public CommandLineRunner commandLineRunner(MyServiceInstance service) {
        return args -> {
            service.doJob();
        };
    }

}

Prior spring 2.4.2 my service/process terminates after doing it's Job, so the code was convenient to run batch jobs. Starting from spring 2.4.2 the service instances do not terminate. So image the result all my instances are running in my AWS Batch account causing cost increases and blocking other jobs.

<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.2</version>
scottfrederick commented 3 years ago

@eduardobarrena-tc Thanks for the report, but we'll need more information to be able to determine what's changed in Spring Boot 2.4.2 to cause this change in behavior. Please provide a minimal sample that reproduces the problem so we can take a look. You can add a comment with a link to a project on GitHub, or attach a project to the issue as a zip file.

snicoll commented 3 years ago

@eduardobarrena-tc I don't think the caps in your description are very useful. To isolate what's holding a daemon thread in the app (Spring Boot should not), you can trigger a thread dump when the job completes and analyze the thread dump.

eduardobarrena-tc commented 3 years ago

@eduardobarrena-tc I don't think the caps in your description are very useful. To isolate what's holding a daemon thread in the app (Spring Boot should not), you can trigger a thread dump when the job completes and analyze the thread dump.

Just emotions man... I will investigate and bring more information, not sure yet what is going on. I have a maven parent structure with several dependencies so it is not easy. I am updating from '2.2.5.RELEASE'

eduardobarrena-tc commented 3 years ago

This is a thread dump. The issue happens if I update from '2.2.5.RELEASE' to '2.4.2'

2021-04-06 10:05:28
Full thread dump OpenJDK 64-Bit Server VM (11.0.2+9 mixed mode):

Threads class SMR info:
_java_thread_list=0x00007efdd0001ec0, length=19, elements={
0x00007efe18268800, 0x00007efe1826c800, 0x00007efe1827f000, 0x00007efe18281000,
0x00007efe18283800, 0x00007efe18285800, 0x00007efe18334000, 0x00007efe1833f800,
0x00007efe1917c800, 0x00007efe18ef1800, 0x00007efe19564000, 0x00007efe18a34800,
0x00007efd6c096000, 0x00007efe19b83800, 0x00007efe18011000, 0x00007efd6c011000,
0x00007efd6c03a000, 0x00007efd6c065000, 0x00007efdd0001000
}

"Reference Handler" #2 daemon prio=10 os_prio=0 cpu=7.55ms elapsed=27.33s tid=0x00007efe18268800 nid=0x4802 waiting on condition  [0x00007efde35fa000]
   java.lang.Thread.State: RUNNABLE
    at java.lang.ref.Reference.waitForReferencePendingList(java.base@11.0.2/Native Method)
    at java.lang.ref.Reference.processPendingReferences(java.base@11.0.2/Reference.java:241)
    at java.lang.ref.Reference$ReferenceHandler.run(java.base@11.0.2/Reference.java:213)

   Locked ownable synchronizers:
    - None

"Finalizer" #3 daemon prio=8 os_prio=0 cpu=0.47ms elapsed=27.33s tid=0x00007efe1826c800 nid=0x4803 in Object.wait()  [0x00007efde34f9000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(java.base@11.0.2/Native Method)
    - waiting on <0x0000000709c03358> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(java.base@11.0.2/ReferenceQueue.java:155)
    - waiting to re-lock in wait() <0x0000000709c03358> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(java.base@11.0.2/ReferenceQueue.java:176)
    at java.lang.ref.Finalizer$FinalizerThread.run(java.base@11.0.2/Finalizer.java:170)

   Locked ownable synchronizers:
    - None

"Signal Dispatcher" #4 daemon prio=9 os_prio=0 cpu=0.24ms elapsed=27.32s tid=0x00007efe1827f000 nid=0x4804 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 cpu=6310.45ms elapsed=27.32s tid=0x00007efe18281000 nid=0x4805 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

   Locked ownable synchronizers:
    - None

"C1 CompilerThread0" #8 daemon prio=9 os_prio=0 cpu=2834.72ms elapsed=27.32s tid=0x00007efe18283800 nid=0x4806 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE
   No compile task

   Locked ownable synchronizers:
    - None

"Sweeper thread" #9 daemon prio=9 os_prio=0 cpu=81.79ms elapsed=27.32s tid=0x00007efe18285800 nid=0x4807 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"Service Thread" #10 daemon prio=9 os_prio=0 cpu=0.16ms elapsed=27.30s tid=0x00007efe18334000 nid=0x4808 runnable  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"Common-Cleaner" #11 daemon prio=8 os_prio=0 cpu=11.68ms elapsed=27.30s tid=0x00007efe1833f800 nid=0x480a in Object.wait()  [0x00007efde2b0c000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(java.base@11.0.2/Native Method)
    - waiting on <0x0000000709c04288> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(java.base@11.0.2/ReferenceQueue.java:155)
    - waiting to re-lock in wait() <0x0000000709c04288> (a java.lang.ref.ReferenceQueue$Lock)
    at jdk.internal.ref.CleanerImpl.run(java.base@11.0.2/CleanerImpl.java:148)
    at java.lang.Thread.run(java.base@11.0.2/Thread.java:834)
    at jdk.internal.misc.InnocuousThread.run(java.base@11.0.2/InnocuousThread.java:134)

   Locked ownable synchronizers:
    - None

"mysql-cj-abandoned-connection-cleanup" #16 daemon prio=5 os_prio=0 cpu=1.11ms elapsed=24.87s tid=0x00007efe1917c800 nid=0x4814 in Object.wait()  [0x00007efde10f9000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(java.base@11.0.2/Native Method)
    - waiting on <0x000000070a7c1578> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(java.base@11.0.2/ReferenceQueue.java:155)
    - waiting to re-lock in wait() <0x000000070a7c1578> (a java.lang.ref.ReferenceQueue$Lock)
    at com.mysql.cj.jdbc.AbandonedConnectionCleanupThread.run(AbandonedConnectionCleanupThread.java:91)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@11.0.2/ThreadPoolExecutor.java:1128)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@11.0.2/ThreadPoolExecutor.java:628)
    at java.lang.Thread.run(java.base@11.0.2/Thread.java:834)

   Locked ownable synchronizers:
    - <0x000000070a7c1720> (a java.util.concurrent.ThreadPoolExecutor$Worker)

"HikariPool-1 housekeeper" #17 daemon prio=5 os_prio=0 cpu=1.29ms elapsed=19.95s tid=0x00007efe18ef1800 nid=0x481b waiting on condition  [0x00007efde25e2000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at jdk.internal.misc.Unsafe.park(java.base@11.0.2/Native Method)
    - parking to wait for  <0x000000070ab14578> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.parkNanos(java.base@11.0.2/LockSupport.java:234)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@11.0.2/AbstractQueuedSynchronizer.java:2123)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@11.0.2/ScheduledThreadPoolExecutor.java:1182)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@11.0.2/ScheduledThreadPoolExecutor.java:899)
    at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@11.0.2/ThreadPoolExecutor.java:1054)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@11.0.2/ThreadPoolExecutor.java:1114)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@11.0.2/ThreadPoolExecutor.java:628)
    at java.lang.Thread.run(java.base@11.0.2/Thread.java:834)

   Locked ownable synchronizers:
    - None

"Thread-1" #19 daemon prio=5 os_prio=0 cpu=11.62ms elapsed=16.43s tid=0x00007efe19564000 nid=0x4824 runnable  [0x00007efde24e1000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.EPoll.wait(java.base@11.0.2/Native Method)
    at sun.nio.ch.EPollSelectorImpl.doSelect(java.base@11.0.2/EPollSelectorImpl.java:120)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@11.0.2/SelectorImpl.java:124)
    - locked <0x000000070b3063d0> (a sun.nio.ch.Util$2)
    - locked <0x000000070b306378> (a sun.nio.ch.EPollSelectorImpl)
    at sun.nio.ch.SelectorImpl.select(java.base@11.0.2/SelectorImpl.java:136)
    at com.amazon.jdbc.communications.InboundMessagesThread.run(Unknown Source)

   Locked ownable synchronizers:
    - None

"HikariPool-2 housekeeper" #21 daemon prio=5 os_prio=0 cpu=0.35ms elapsed=15.84s tid=0x00007efe18a34800 nid=0x4826 waiting on condition  [0x00007efd931b1000]
   java.lang.Thread.State: TIMED_WAITING (parking)
    at jdk.internal.misc.Unsafe.park(java.base@11.0.2/Native Method)
    - parking to wait for  <0x000000070b2fac00> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
    at java.util.concurrent.locks.LockSupport.parkNanos(java.base@11.0.2/LockSupport.java:234)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(java.base@11.0.2/AbstractQueuedSynchronizer.java:2123)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@11.0.2/ScheduledThreadPoolExecutor.java:1182)
    at java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(java.base@11.0.2/ScheduledThreadPoolExecutor.java:899)
    at java.util.concurrent.ThreadPoolExecutor.getTask(java.base@11.0.2/ThreadPoolExecutor.java:1054)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(java.base@11.0.2/ThreadPoolExecutor.java:1114)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(java.base@11.0.2/ThreadPoolExecutor.java:628)
    at java.lang.Thread.run(java.base@11.0.2/Thread.java:834)

   Locked ownable synchronizers:
    - None

"Thread-2" #23 daemon prio=5 os_prio=0 cpu=4.09ms elapsed=14.31s tid=0x00007efd6c096000 nid=0x482a runnable  [0x00007efd915aa000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.EPoll.wait(java.base@11.0.2/Native Method)
    at sun.nio.ch.EPollSelectorImpl.doSelect(java.base@11.0.2/EPollSelectorImpl.java:120)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@11.0.2/SelectorImpl.java:124)
    - locked <0x000000070b43cfa0> (a sun.nio.ch.Util$2)
    - locked <0x000000070b43cf48> (a sun.nio.ch.EPollSelectorImpl)
    at sun.nio.ch.SelectorImpl.select(java.base@11.0.2/SelectorImpl.java:136)
    at com.amazon.jdbc.communications.InboundMessagesThread.run(Unknown Source)

   Locked ownable synchronizers:
    - None

"lettuce-timer-3-1" #24 prio=5 os_prio=0 cpu=14.68ms elapsed=14.14s tid=0x00007efe19b83800 nid=0x482b sleeping [0x00007efd914a9000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(java.base@11.0.2/Native Method)
    at io.netty.util.HashedWheelTimer$Worker.waitForNextTick(HashedWheelTimer.java:567)
    at io.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:466)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(java.base@11.0.2/Thread.java:834)

   Locked ownable synchronizers:
    - None

"DestroyJavaVM" #25 prio=5 os_prio=0 cpu=7302.73ms elapsed=13.73s tid=0x00007efe18011000 nid=0x47fb waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"Thread-3" #26 daemon prio=5 os_prio=0 cpu=5.26ms elapsed=12.18s tid=0x00007efd6c011000 nid=0x4832 runnable  [0x00007efd91ead000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.EPoll.wait(java.base@11.0.2/Native Method)
    at sun.nio.ch.EPollSelectorImpl.doSelect(java.base@11.0.2/EPollSelectorImpl.java:120)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@11.0.2/SelectorImpl.java:124)
    - locked <0x000000070bd257a0> (a sun.nio.ch.Util$2)
    - locked <0x000000070bd25748> (a sun.nio.ch.EPollSelectorImpl)
    at sun.nio.ch.SelectorImpl.select(java.base@11.0.2/SelectorImpl.java:136)
    at com.amazon.jdbc.communications.InboundMessagesThread.run(Unknown Source)

   Locked ownable synchronizers:
    - None

"Thread-4" #27 daemon prio=5 os_prio=0 cpu=4.97ms elapsed=10.06s tid=0x00007efd6c03a000 nid=0x4833 runnable  [0x00007efd91bac000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.EPoll.wait(java.base@11.0.2/Native Method)
    at sun.nio.ch.EPollSelectorImpl.doSelect(java.base@11.0.2/EPollSelectorImpl.java:120)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@11.0.2/SelectorImpl.java:124)
    - locked <0x0000000716a706c8> (a sun.nio.ch.Util$2)
    - locked <0x0000000716a70670> (a sun.nio.ch.EPollSelectorImpl)
    at sun.nio.ch.SelectorImpl.select(java.base@11.0.2/SelectorImpl.java:136)
    at com.amazon.jdbc.communications.InboundMessagesThread.run(Unknown Source)

   Locked ownable synchronizers:
    - None

"Thread-5" #28 daemon prio=5 os_prio=0 cpu=5.01ms elapsed=7.98s tid=0x00007efd6c065000 nid=0x4834 runnable  [0x00007efd90cb0000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.EPoll.wait(java.base@11.0.2/Native Method)
    at sun.nio.ch.EPollSelectorImpl.doSelect(java.base@11.0.2/EPollSelectorImpl.java:120)
    at sun.nio.ch.SelectorImpl.lockAndDoSelect(java.base@11.0.2/SelectorImpl.java:124)
    - locked <0x0000000716ab1ee0> (a sun.nio.ch.Util$2)
    - locked <0x0000000716ab1d90> (a sun.nio.ch.EPollSelectorImpl)
    at sun.nio.ch.SelectorImpl.select(java.base@11.0.2/SelectorImpl.java:136)
    at com.amazon.jdbc.communications.InboundMessagesThread.run(Unknown Source)

   Locked ownable synchronizers:
    - None

"Attach Listener" #29 daemon prio=9 os_prio=0 cpu=0.33ms elapsed=0.16s tid=0x00007efdd0001000 nid=0x4856 waiting on condition  [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

   Locked ownable synchronizers:
    - None

"VM Thread" os_prio=0 cpu=117.33ms elapsed=27.34s tid=0x00007efe18260800 nid=0x4801 runnable  

"GC Thread#0" os_prio=0 cpu=260.05ms elapsed=27.36s tid=0x00007efe18038800 nid=0x47fc runnable  

"GC Thread#1" os_prio=0 cpu=234.61ms elapsed=27.18s tid=0x00007efdd4001000 nid=0x480d runnable  

"GC Thread#2" os_prio=0 cpu=233.74ms elapsed=27.18s tid=0x00007efdd4002800 nid=0x480e runnable  

"GC Thread#3" os_prio=0 cpu=224.87ms elapsed=27.18s tid=0x00007efdd4004000 nid=0x480f runnable  

"GC Thread#4" os_prio=0 cpu=250.96ms elapsed=27.18s tid=0x00007efdd4005800 nid=0x4810 runnable  

"GC Thread#5" os_prio=0 cpu=213.38ms elapsed=24.84s tid=0x00007efdd403d000 nid=0x4815 runnable  

"GC Thread#6" os_prio=0 cpu=215.24ms elapsed=24.84s tid=0x00007efdd403e000 nid=0x4816 runnable  

"GC Thread#7" os_prio=0 cpu=154.08ms elapsed=16.97s tid=0x00007efdd4075000 nid=0x4823 runnable  

"G1 Main Marker" os_prio=0 cpu=1.60ms elapsed=27.36s tid=0x00007efe18095800 nid=0x47fd runnable  

"G1 Conc#0" os_prio=0 cpu=45.39ms elapsed=27.36s tid=0x00007efe18097800 nid=0x47fe runnable  

"G1 Conc#1" os_prio=0 cpu=47.29ms elapsed=26.18s tid=0x00007efdec001000 nid=0x4812 runnable  

"G1 Refine#0" os_prio=0 cpu=2.68ms elapsed=27.35s tid=0x00007efe18195000 nid=0x47ff runnable  

"G1 Young RemSet Sampling" os_prio=0 cpu=9.70ms elapsed=27.35s tid=0x00007efe18197000 nid=0x4800 runnable  
"VM Periodic Task Thread" os_prio=0 cpu=23.86ms elapsed=27.30s tid=0x00007efe18336800 nid=0x4809 waiting on condition  

JNI global refs: 17, weak refs: 0
wilkinsona commented 3 years ago

It looks like Lettuce has started a non-daemon thread that's still running:

"lettuce-timer-3-1" #24 prio=5 os_prio=0 cpu=14.68ms elapsed=14.14s tid=0x00007efe19b83800 nid=0x482b sleeping [0x00007efd914a9000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(java.base@11.0.2/Native Method)
    at io.netty.util.HashedWheelTimer$Worker.waitForNextTick(HashedWheelTimer.java:567)
    at io.netty.util.HashedWheelTimer$Worker.run(HashedWheelTimer.java:466)
    at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
    at java.lang.Thread.run(java.base@11.0.2/Thread.java:834)

It's not clear from what you've shared thus far why that would be the case. If you'd like us to spend some more time investigating, I think we'll need the minimal sample that @scottfrederick requested above.

eduardobarrena-tc commented 3 years ago

Hi all, here I am attaching a project example with minimum artifacts in order to reproduce the error. I looks an autoconfiguration issue, in my project to get it work I had to disable several autonfigurations and configure things manually. However some module inherit those maven artifacts and do not end properly. It would be nice to have an annotation like @DisableAutoconfiguration (The oposiite to @EnableAutoConfiguration as a friendly alternative to property spring.autoconfigure.exclude). However, it is not so cool that the presence of some artifacts break things :S

https://github.com/eduardobarrena-tc/demo-spring-boot-bug-4.2.2

ed@pc: $ java -version
openjdk version "11.0.2" 2019-01-15
OpenJDK Runtime Environment 18.9 (build 11.0.2+9)
OpenJDK 64-Bit Server VM 18.9 (build 11.0.2+9, mixed mode)
ed@pc: $ uname -a
Linux pc 4.19.0-6-amd64 #1 SMP Debian 4.19.67-2 (2019-08-28) x86_64 GNU/Linux
philwebb commented 3 years ago

Thanks for the sample project. It looks like this might be a Lettuce issue. Adding <lettuce.version>6.0.1.RELEASE</lettuce.version> to the POM also fixes the issue.

I'm going to dig a bit more to see if I can find out what's changed.

philwebb commented 3 years ago

This commit https://github.com/lettuce-io/lettuce-core/commit/dabd8b2d846b2034041d31f3d7ad9034383669af fixed https://github.com/lettuce-io/lettuce-core/issues/1489 and now starts the timer eagerly where as before it was lazy. The timer thread isn't a daemon thread so the app continues to run.

@mp911de Is this a Lettuce issue? Should the DefaultClientResources class create a HashedWheelTimer with a daemon thread rather than a regular one?

mp911de commented 3 years ago

Lettuce creates a non-daemon thread for the timer, similar to the default Executors.defaultThreadFactory(). The mentioned change is a fix to eagerly initialize the thread to avoid blocking calls in the context of non-blocking workloads.

ClientResources.shutdown() stops the timer so I assumed that proper disposal of the bean would also stop the timer thread.

philwebb commented 3 years ago

We're in a bit of a catch 22. The shutdown() method does get called, but only when the ApplicationContext is closed. That won't happen if non-daemon threads are still active.

@eduardobarrena-tc you can change your main method to this to force the context to close:

public static void main(String[] args) {
    SpringApplication.run(DemoApplication.class, args).close();
}
eduardobarrena-tc commented 3 years ago

Thanks for that simple and cool solution @philwebb, but just to notice lot of people on internet are using as a recipe the code I have provided.

A suggestion: A thing that could be cool to have in the framework would be to have an @DisableAutoconfiguration to disable all auto-configuration classes. I know that I can exclude them, but in that case I have to enumerate them all, which if I do that a code might work today but not in the future if a new auto-configuration is added or changed in the framework (as in this case). If having that annotation is not feasible, another option might be using an asterisk like this "spring.autoconfigure.exclude=org.springframework.boot.autoconfigure.*"; which I tried but it did not work.

Thank you all !!

mp911de commented 3 years ago

I wasn't able to find the reason why HashedWheelTimer defaults to non-daemon threads so I cannot say what such a change would break (or not break). As workaround, Boot could provide its own timer constructed with a daemon thread which would align well with its the application lifecycle assumptions.

Generally speaking, that issues is likely to impact all netty-based integrations that create a timer instance (e.g. the Cassandra driver).

Would it make sense to provide a Boot-specific ThreadFactory for components that should not prevent the application from shutdown?

In Lettuce we could introduce such methods so config classes don't need to work out driver internals.

philwebb commented 3 years ago

@eduardobarrena-tc You can already use Spring Boot without auto-configuration, if you look at @SpringBootApplication you'll see it's actually:

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

If you don't want auto-configuration you can annotate your main class with:

@SpringBootConfiguration
@ComponentScan

You can also exclude the spring-boot-autoconfigure jar if you want to be totally sure that no auto-configuration functionality is used.

philwebb commented 3 years ago

Flagging for team-meeting to discuss a Boot-specific ThreadFactory.

wilkinsona commented 3 years ago

I'm not sure that something being a Spring Boot application allows us to assume that Netty timer threads should be non-daemon . If Lettuce, for example, isn't in a position to make that assumption in general then I don't think it being used in a Boot app changes the situation. As long as the JVM exits once the context has been closed (either via an explicit call to close() or via the shutdown hook running due to SIGTERM) then I don't think we should do anything here.

philwebb commented 3 years ago

All things considered I think we're best off keeping the same threading setup as Lettuce. Explicitly calling .close() seems like the best solution for users.