I created a project fresh out of the Spring Initializr by choosing Kotlin
, Gradle
, M7
and Web-reactive
.
I made a small project:
data class Person (val id: String)
@Component class PersonHandler(val template: ReactiveMongoTemplate)
{
init
{
println("Initializing")
val jim: Mono<Person> = template.save(Person("Jim"))
val john: Mono<Person> = template.save(Person("John"))
val jack: Mono<Person> = template.save(Person("Jack"))
launch(jim)
launch(john)
launch(jack)
println("Finished Initializing")
}
fun launch(mono: Mono<Person>)
{
mono.subscribe({println(it.id)}, {println("Error")}) // This works
// mono.block() This just hangs
}
}
I try to save three persons into the database. The save
method returns just a Mono
which needs to be executed. If I try to execute it by simply subscribing, everything works nice:
Initializing
Finished Initializing
2017-12-21 13:14:39.513 INFO 17278 --- [ Thread-13] org.mongodb.driver.connection : Opened connection [connectionId{localValue:3, serverValue:158}] to localhost:27017
2017-12-21 13:14:39.515 INFO 17278 --- [ Thread-12] org.mongodb.driver.connection : Opened connection [connectionId{localValue:4, serverValue:159}] to localhost:27017
2017-12-21 13:14:39.520 INFO 17278 --- [ Thread-14] org.mongodb.driver.connection : Opened connection [connectionId{localValue:5, serverValue:160}] to localhost:27017
Jim
Jack
John
However, when I use block
instead of subscribe
the application hangs:
Initializing
2017-12-21 13:16:47.200 INFO 17463 --- [ Thread-14] org.mongodb.driver.connection : Opened connection [connectionId{localValue:3, serverValue:163}] to localhost:27017
If I query the database manually, I see that Jim has been saved, but not Jack and John.
Is this a bug, or am I doing something wrong? I would like to have the guarantee that the users are in the database before the code goes any further so I would really like to use block
.
I am not sure if it is relevant, but I get a compiler warning
Accessing nonfinal property
template
in constructor
There is a minimal working example. It contains two branches. One is a workaround for the issue.
https://github.com/martin-drozdik/spring-mongo-bug-example
In the Reactive Streams API there are four main interfaces: Publisher — Emits events to subscribers based on the demands received from its subscribers. A publisher can serve multiple subscribers and it has only one method: subscribe. Subscriber — Receives events emitted by the Publisher.
As you know, Mono is an asynchronous call that executes in a non-blocking way.
The spring-boot-starter-data-mongodb-reactive is a Spring Boot starter for using MongoDB document-oriented database and Spring Data MongoDB Reactive. resources/application.properties. spring.main.banner-mode=off. In the application.
Reactive code does more work with fewer resources. Project Reactor and Spring WebFlux let developers take advantage of multi-core, next-generation processors—handling potentially massive numbers of concurrent connections. With reactive processing, you can satisfy more concurrent users with fewer microservice instances.
I think this might be a Spring Framework bug / usability issue.
First, let me underline the difference between subscribe
and block
:
subscribe
method kicks off the work and returns immediately. So you get no guarantee that the operation is done when other parts of your application run.block
is a blocking operation: it triggers the operation and waits for its completion.For initialisation work, composing operations and calling block once is probably the best choice:
val jim: Mono<Person> = template.save(Person("Jim"))
val john: Mono<Person> = template.save(Person("John"))
val jack: Mono<Person> = template.save(Person("Jack"))
jim.then(john).then(jack).block();
As you've stated, using block
hangs the application. I suspect this might be an Spring context initialisation issue - If I remember correctly, this process might assume a single thread in some parts and using a reactive pipeline there schedules work on many threads.
Could you create a minimal sample application (using just Java/Spring Boot/Spring Data Reactive Mongo) and report that on https://jira.spring.io?
I had a similar situation where by calling "reactiveMongoTemplate.save(model).block()" the application was hanging.
The issue was caused by the @PostConstruct in one of my classes designed to create my system users after the application initialization. I think somehow It was invoked before full Spring context initialization.
@Configuration
public class InitialDataPostLoader {
private Logger logger = LogManager.getLogger(this.getClass());
@PostConstruct
public void init() {
logger.info(String.format(MSG_SERVICE_JOB, "System Metadata initialization"));
createDefaultUsers();
}
By replacing @PostConstruct with ContextRefreshEvent listener the issues got resolved.
@Configuration
public class InitialDataPostLoader implements
ApplicationListener<ContextRefreshedEvent> {
private Logger logger = LogManager.getLogger(this.getClass());
@Override
public void onApplicationEvent(ContextRefreshedEvent arg0) {
logger.info(String.format(MSG_SERVICE_JOB, "System Metadata initialization"));
createDefaultUsers();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With