dany giguere
06/01/2025, 4:07 PM@get:UniqueEmail
val email: String,
but ConstraintValidator are not suspendable. So I have to run it blocking like :
@Component
class UniqueEmailValidator @Autowired constructor(
private val userService: UserService
) : ConstraintValidator<UniqueEmail, String> {
override fun isValid(value: String?, context: ConstraintValidatorContext): Boolean {
if (value == null) return true
// Run suspend function in a blocking way for validation
return runBlocking { userService.findByEmail(value) == null }
}
}
will that runBlocking somehow breaks (slows) all the coroutines afterward. I mean all the coroutine code that comes after that ? The repo is here: https://github.com/danygiguere/spring-boot-3-reactive-with-kotlin-coroutinesrkechols
06/01/2025, 11:56 PMsuspend
function that is called by the framework itself, and this function cannot be suspend
because of the framework
• The internal logic has something to it which involves just waiting (such as a database query) and so is declared as a suspend
function
Is that correct?
In this case I'm fairly certain that runBlocking
will use Dispatchers.Default
to run the suspending code within, which means everything ought to work as expected in those coroutines. You do of course have the current/parent thread which is sitting blocked, but it's not using any CPU so I don't think it's hurting anything unless it came from `Dispatcher.Default`'s threadpooldany giguere
06/02/2025, 12:09 AMdany giguere
06/02/2025, 12:13 AM@GetMapping("/demo/parallel")
suspend fun demoParallel(exchange: ServerWebExchange): String = coroutineScope {
val timeBefore = System.currentTimeMillis()
// The 2 calls below are executed in parallel (at the same time)
runBlocking {
executeFaked1000msCall()
executeFaked1000msCall()
}
val durationRequest1 = async{executeFaked1000msCall()}
val durationRequest2 = async{executeFaked1000msCall()}
val durationRequest3 = async{executeFaked1000msCall()}
val durationRequest4 = async{executeFaked1000msCall()}
durationRequest1.await()
durationRequest2.await()
durationRequest3.await()
durationRequest4.await()
val timeAfter = System.currentTimeMillis()
val duration = abs(timeBefore - timeAfter)
return@coroutineScope "Number of milliseconds to execute this function (containing two 1000ms parallel queries) : $duration ms"
}
The runBlocking calls ran in 2 secs. All other ran in parallel.... in 1 sec.rkechols
06/02/2025, 12:23 AMrunBlocking
shouldn't ever be used within a suspend
function, since it starves the thread and you could just do without it.
In your original scenario, would you mind printing the name of the thread that the validator function is running on? Before touching runBlocking
put in println("${Thread.currentThread().name}")
dany giguere
06/02/2025, 12:27 AM@Component
class UniqueEmailValidator @Autowired constructor(
private val userService: UserService
) : ConstraintValidator<UniqueEmail, String> {
override fun isValid(value: String?, context: ConstraintValidatorContext): Boolean {
if (value == null) return true
// Run suspend function in a blocking way for validation
println("****** * : ${Thread.currentThread().name}")
return runBlocking { userService.findByEmail(value) == null }
}
}
****** * : reactor-http-nio-3rkechols
06/02/2025, 12:29 AMdany giguere
06/02/2025, 12:29 AMrkechols
06/02/2025, 12:31 AMrunBlocking
is actually the right choice here. Its purpose is to go from a blocking context to a suspending context (and wait for completion), which is exactly what you have here.Artyom Gornostayev
06/02/2025, 7:29 PMdany giguere
06/02/2025, 11:59 PMArtyom Gornostayev
06/03/2025, 4:13 AMdany giguere
06/03/2025, 11:15 AMdany giguere
06/03/2025, 12:13 PMArtyom Gornostayev
06/03/2025, 2:17 PMrunBlocking
or so...
Think about a possibility to return some specific status code (409 or 422) for checks from service layer. ;)
In my practice I saw a lot of tries to solve such a problem, and none of them were successful.
If your application is not hello world
-like, you'll find a difficulties with DB transactions, integration with other systems and so on...dany giguere
06/03/2025, 4:16 PM