Closed spring-projects-issues closed 8 months ago
Juergen Hoeller commented
This is a tough one since it relates to the general challenges of parallel bean initialization. At this point, we generally don't support custom threads triggering (singleton) bean initialization while the main thread is waiting on them.
Brian Clozel commented
Apart from the general challenge, I wanted to point out that the driver for this issue is the following: being able to call initialization code during application startup using Mono
/ Flux
types. Maybe there is a way to solve that use case without tackling that hard issue.
Sébastien Deleuze commented
I faced the issue described by Brian Clozel in my reference Kotlin + Spring Reactive application https://github.com/mixitconf/mixit, and I could not find a satisfying solution, so I would like to know if we could improve that. I need to initialize data in my MongoDB instance with ReactiveMongoTemplate
and I need these data to be in the database before serving requests to users and before running my integration tests.
@Component
class DatabaseInitializer(private val userRepository: UserRepository, ...) {
@PostConstruct
fun init() {
userRepository.initData()
/...
}
}
@Repository
class UserRepository(private val template: ReactiveMongoTemplate, ...) {
private val logger = LoggerFactory.getLogger(this.javaClass)
fun initData() {
if (count().block() == 0L) {
val usersResource = ClassPathResource("data/users.json")
val users: List<User> = objectMapper.readValue(usersResource.inputStream)
users.forEach { save(it).block() }
logger.info("Users data initialization complete")
}
}
}
With Boot 2.0.2 it was working (by luck I think), but if I upgrade to Boot 2.0.6 or 2.1.1, it is blocking indefinitely at users.forEach
level. I tried to change this to chain reactive operators and use a single block()
but same issue. CommandLineRunner
is not usable here since it will not ensure the data are in the database before running the application.
I quote mp911de explanation:
So what happened there is, that we emit application events Emission is synchronous and while waiting on a
Mono.block()
to complete, events wait being dispatched. Dispatch is blocked by asynchronized
block in the application context (synchronized on the event dispatcher, it was, I think) Thesynchronized
block was entered by the bean initializer that is currently executingMono.block()
(that’s where the circle meets its start)
The only solution we have here is to use a blocking MongoTemplate
, but I have hard time requiring injecting a second bean with a different API and infrastructure just for data initialization.
I don't know if we could find a way to avoid this deadlock or support Mono<Void>
return type for this kind of Reactive use cases, but it seems to me we are missing something here. Any thoughts?
Dave Syer commented
I think CommandLineRunner
is a reasonable compromise - it certainly should work with integration tests, even if technically there is a window where the app is listening on port 8080 but not able to serve accurate data. If it doesn't work, then I consider that a bug as well.
Brian Clozel commented
Looking at Juergen Hoeller's comments in #21025, I don't think we'll have a reactive way of publishing/processing events - for the same reason, supporting Mono<Void>
is probably not an option?
Sébastien Deleuze commented
Dave Syer I was mistaken by https://github.com/spring-projects/spring-boot/issues/7656 where application and command line runners were initially moved after ready event, then it was rollbacked. I did some test with my MiXiT application, and I can confirm the data are created before my integration tests. That said using CommandLineRunner
breaks a test so I need to dig into it more in detail to understand if that can be used as a reliable replacement or not.
Also I still think that a lot of users will be spend lot of time stuck on the @PostConstruct
issue, and it seems we don't have an answer for that pretty common use case at Spring Framework level, so maybe worth to discuss for Spring 5.2?
Sébastien Deleuze commented
I confirm CommandLineRunner
is not a good fit as a general solution for this. In addition to being a Boot construct, in my application the web router which specifies @DependsOn("databaseInitializer")
needs the data to be created in the database to generate routes at startup based on these data. It was working with @PostConstruct
but it does not with CommandLineRunner
so that's another indication something like #19487 is needed.
@jhoeller - I am using spring 5.2.8 and still seeing this issue. Any updates on the fix?
Our project is trying to load the application cache using multiple parallel threads during @Postconstruct of the Cacheloader bean. If any of the threads has some issue while loading the cache we publish the issue as an events . A listener listens to those events and log the issue to the external system.
I am seeing deadlock while cache loader threads trying to publish the events using spring ApplicationEventpublisher.
With #25799 having been addressed years ago and #23501 revised for 6.2 now, there should not be any blocking for asynchronous event publication anymore. #25799 made it work fine for existing listeners, #23501 makes it work for listener beans that need to be initialized yet as well (unless there is a conflict among the dependencies created for that listener).
Mark Paluch opened SPR-16357 and commented
Publishing events can block the container initialization in conjunction with event publishing from a different thread.
This can happen if events are published from a bean constructor while its bean is instantiated and events get published in a different thread while the constructed bean awaits completion of event publishing.
Consider following code:
The code above publishes an event in an other thread than the constructing thread while awaiting completion before the constructor progresses.
It leads to a thread state like:
The example originates from reactive code in which a constructor is used to call initialization code using
.block()
for synchronization, see DATAMONGO-1841.Affects: 5.0 GA
Issue Links:
19487 Asynchronous initialization of beans during startup
13410 Parallel bean initialization during startup
0 votes, 8 watchers