Search code examples
kotlinspring-mvckotlin-coroutinescoroutinecoroutinescope

What scope to use for launching a task in Kotlin from a controller?


My Kotlin App is using Spring to expose an API that should execute a long task in the background. We need to return

@PutMapping("/refresh")
fun refresh() = {
    GlobalScope.launch(IO) { refreshDataFromCache() }
    return ResponseEntity.accepted().body("""{"result": "accepted"}""")
}

IntelliJ complains that using the GlobalScope is an anti-pattern. However, I cannot just add suspend to the controller function or it won't work. Neither I can use runBlocking or the method will need to wait until finishing producing a REST timeout

What should I add as the scope of this function to use structured concurrency?


Solution

  • You probably want to inject some scope other than GlobalScope into your resource. If your framework already provides some sort of "application scope" that will be cancelled when your application shuts down, that seems appropriate.

    Failing that -- a coroutine scope is just a wrapper around a Job (well, and other coroutine context items). So you can create a new Job during app startup, and simply CoroutineScope(job) to make your application coroutine scope. Then just do job.cancel() during app shutdown and that will ensure any coroutines launched in that scope will be cancelled.

    Edited to add my solution:

    private val jobScope = CoroutineScope(IO)
    
    @PutMapping("/refresh")
    fun refresh() = {
        jobScope.launch(IO) { refreshDataFromCache() }
        return ResponseEntity.accepted().body("""{"result": "accepted"}""")
    }
    
    @PreDestroy
    fun stopTasks() {
        jobScope.cancel("Closing app")
    }