-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Low
-
Affects Version/s: 10.0.0
-
Component/s: Spring
-
1
-
Severity 3 - Minor
Issue Summary
In Bitbucket 10+, Spring was upgraded to 6.2 which introduced lenient bean creation mode, allowing non-Spring-managed background threads to create beans concurrently with the main bootstrap thread. This can cause a deadlock in Spring 6.2's DefaultSingletonBeanRegistry.getSingleton() method during startup if a background thread attempts to autowire Spring beans at the same time as the main bootstrap thread. Both threads end up permanently waiting on each other and startup never completes. This was not an issue prior to the Spring 6.2 upgrade as earlier versions used a single synchronized lock for all bean creation, making concurrent creation impossible and this deadlock scenario could not occur.
Steps to Reproduce
- Set up a two-node Bitbucket Data Center cluster
- Wait for Node 1 to fully start
- Open the ZDU cluster status page at /plugins/servlet/zdu on Node 1 in a browser – this polls /rest/zdu/cluster every ~5 seconds, triggering cluster operations that require Spring bean injection on Node 2
- Start Node 2
Expected Results
The application starts successfully.
Actual Results
The application on Node 2 hangs indefinitely. Thread dumps show spring-startup and one or more non-Spring-managed threads permanently blocked in DefaultSingletonBeanRegistry.getSingleton(), waiting on each other.
A race condition in Spring 6.2's deadlock detection logic means that when two threads end up waiting on each other's beans, this causes a deadlock, and results in both threads hanging permanently.
Workaround
Add -Dspring.locking.strict=true to the JVM arguments. This restores Spring's pre-6.2 style locking behaviour, preventing the deadlock.