Spring Retry not working with Kotlin Async Coroutine - spring

Note: the below function test is being called via a launch coroutine.
The following code works:
#Retryable(value=[RetryException::class])
suspend fun test() {
throw RetryException("blah")
}
However, the moment I add an async call, retry stops working:
#Retryable(value=[RetryException::class])
suspend fun test() {
val deferred = supervisorScope {
async { library.method() }
}
deferred.await()
throw RetryException("blah")
}
What could be wrong?

Related

Writing JUnit tests for restTemplate.delete with Observable throwing OnErrorNotImplementedException

I have two methods in my Spring Boot(version 1.3.3) project as follows;
public Observable<Void> methodA() {
return this.methodB().map(s -> {
methodC.subscribe( aVoid ->
// some code
);
// some code
});
}
private Observable<Void> methodC() {
return Observable<~>create(sub -> {
restTemplate.delete(//some code);
sub.onNext(null);
sub.onCompleted();
}).doOnNext(//some code)
.doOnError(//some code);
}
I am using rxjava version 1.3.8.
I am trying to write Junit tests for methodC where restTemplate.delete(//some code); throws a HttpClientErrorException. For that, mocked the restTemplate.delete(//some code); as follows;
#Test
public void test() {
// some code
Mockito.doThrow(new HttpClientErrorException(HttpStatus.NOT_FOUND)).when(restTemplate)
.delete(//some code);
// some code
final Observable<Void> result = methodA();
final TestSubscriber<Void> subscriber = new TestSubscriber();
result.subscribe(subscriber);
subscriber.assertNotCompleted();
subscriber.assertError(HttpClientErrorException.class);
}
The mock is working as expected but it throws rx.exceptions.OnErrorNotImplementedException: 404 NOT_FOUND and fails the test.
but when I run the code, it is working as expected.
What am I missing here? How can I mock restTemplate.delete(// some code); to throw HttpClientErrorException and assert it?
Thanks in advance!

How to run a background task in Spring using coroutines in a reactive application while properly inheriting the reactor context

I would like to perform an async task in Spring (similar to #Async) using coroutines.
I've tried the following:
#PostMapping("/async")
suspend fun post(): ResponseEntity<Unit> {
logger.info { "Received request" }
val context = coroutineContext
logger.info { "coroutineContext=${context}" }
CoroutineScope(Dispatchers.Default + coroutineContext).launch {
delay(5000)
logger.info { "io + context, 5000ms" }
}
CoroutineScope(Dispatchers.IO + coroutineContext).launch {
delay(5000)
logger.info { "default + context, 5000ms" }
}
logger.info { "Returning response" }
return ResponseEntity.accepted()
.build()
}
Spring seems to be waiting for the coroutine to finish before returning the response.
I would also like to inherit the context so I can inherit trace information while inside the coroutine (I am using sleuth).

getting started with kotlin and SpringBootApplication to run some suspend fun

Trying to run this repo with some suspend functions. Can someone please give some hints?
Let say we have one
suspend fun log(){
mLog.subscribeAlways<GroupMessageEvent> { event ->
if (event.message.content.contains("Error")) {
print("****")
} else if (event.message.content.contains("Warning")) {
print("Warning")
}
}
mLog.Listen()
}
How can we trigger this log from main
open class Application {
companion object {
#JvmStatic fun main(args: Array<String>) {
SpringApplication.run(Application::class.java, *args)
}
}
}
What have try, it can run without error, but it didn't work as expected,
call the log function from Controller class
class Controller {
#Value("\${spring.datasource.url}")
private var dbUrl: String? = null
#Autowired
lateinit private var dataSource: DataSource
#RequestMapping("/")
internal suspend fun index(): String {
mLog()
return "index"
}
Suspend functions should be called from a coroutine. There are several coroutine builder functions: launch, async, runBlocking.
Using these functions you can start a coroutine, for example:
runBlocking {
// call suspend methods
}
Coroutines launched with launch and async coroutine builders in a context of some CoroutineScope. They don't block the current thread. There are more info in the docs.
runBlocking blocks the current thread interruptibly until its completion.
Using launch coroutine builder you will not block the current thread if calling suspend function in it:
fun index(): String {
GlobalScope.launch {
log()
}
"index"
}
Note: in this case function index returns before log is executed. GlobalScope is discouraged to use. Application code usually should use an application-defined CoroutineScope. How to define a CoroutineScope is described in the docs and here and here.
If your intention is to block the current thread until suspend function completes use runBlocking coroutine builder:
fun index(): String = runBlocking {
log()
"index"
}
Some useful links: GlobalScope.launch vs runBlocking, Update UI async call with coroutines, Suspend function 'callGetApi' should be called only from a coroutine or another suspend function.

Unexplained `kotlinx.coroutines.JobCancellationException: Job was cancelled` with kotlin coroutines and spring WebClient?

I have a bit of code written using kotlin coroutines
suspend fun getData(request: Request): Response {
return try {
webClient.post()
.uri(path)
.body(BodyInserters.fromObject(request))
.retrieve()
.awaitExchange()
.awaitBody<Response>()
} catch (e: Exception) {
logger.error("failed", e)
throw Exception("failed", e)
}
}
I am calling this bit of code like
val response = withContext(Dispatchers.IO) {
client.getData(request)
}
In my logs i see this exception happening from time to time
kotlinx.coroutines.JobCancellationException: Job was cancelled but this does not allow me to find what actually went wrong. I assume one of the suspending extension functions (awaitExchange or awaitBody) failed but i am not sure of that.

How to launch corutines in spring applications

I have an app that consumes some messages from a message Queue and processes them. The processing is implemented as suspending functions and there is a service that will publish the events to a Channel<Event>, I have another service that will basically do:
for (event in channel) {
eventProcessor.process(event)
}
The problem is that this is also a suspending function, and I am really not sure what's the proper way to launch it within the context of Spring.
My initial solution was to do the following:
#Bean
fun myProcessor(eventProcessor: EventProcessor, channel: Channel<Event>): Job {
GlobalScope.launch {
eventProcessor.startProcessing(channel)
}
}
But it seems somehow hacky, and I am not sure what's the proper way to do it.
Launching anything on a GlobalScope is a really bad idea. You loose all advantages of structured concurrency this way.
Instead, make your EventProcessor implement CoroutineScope.
This will force you to specify coroutineContext, so you can use Dispatchers.Default:
override val coroutineContext = Dispatchers.Default
So, the full example will look something like this:
#SpringBootApplication
class SpringKotlinCoroutinesApplication {
#Bean
fun myProcessor(eventProcessor: EventProcessor, channel: Channel<Event>): Job {
return eventProcessor.startProcessing(channel)
}
#Bean
fun p() = EventProcessor()
#Bean
fun c() = Channel<Event>()
}
fun main(args: Array<String>) {
runApplication<SpringKotlinCoroutinesApplication>(*args)
}
class Event
class EventProcessor : CoroutineScope {
override val coroutineContext = Dispatchers.Default
fun startProcessing(channel: Channel<Event>) = launch {
for (e in channel) {
println(e)
}
}
}

Resources