Hei. <@U2E974ELT> (apologies for tagging you direc...
# coroutines
s
Hei. @elizarov (apologies for tagging you directly) I wanted to get your rationale over
GlobalScope.launch { … }
I read this article https://elizarov.medium.com/the-reason-to-avoid-globalscope-835337445abc which makes sense, and I almost always use
withContext(AppropriateDispatcher) { … }
to offload heavy jobs. I have a ktor server that receives around 260 req/sec and logs request and responses while receiving and responding, along with actual response. This is done via an interceptor. Now I don’t care much about logging, so I was thinking to do something like
GlobalScope.launch(MDCContext()) { interceptor.request.log(...) }
so that logging is eventually done, but the interceptor can
proceed()
immediately. In fact, I was thinking to do this to pretty much every logging, Do you think it is a good way of doing the intention. What can be the consequences in regards to threads and CPU if a lot of logging suddenly starts offloading to GlobalScope ? Anyone can help me as well 🙂 Any and all feedback is much appreciated.
b
z
Note that
GlobalScope
doesn’t specify a dispatcher, which means that in most cases the
Default
dispatcher will end up getting used. So whatever that implies about thread behavior will apply to
GlobalScope
.
n
Just wondering how we can do fire and forget without waiting for result then without using GlobalScope.
u
Create your own scope singleton
Copy code
object LoggerScope = CoroutineScope(...+ SupervisorJob())
Or better inject it using DI
e
Using
GlobalScope.launch { log(...) }
is an exceptionally bad idea. First of all, you can get your back-to-back log statements misordered, vastly complicating future trouble-shooting that logging was supposed to help with in the first place. Now, you probably were trying to solve a problem of "logging being slow" by offloading it to the background, but instead you've created a situation where if your logging is indeed slow, then its slowness will manifest in your app running out of memory, making it way harder to figure out why is that your app is not working as good as indented.
The worst part of it, is that if you accidentally make a bug like trying to log something in an endless loop, it'll become way harder to spot. Instead of just slowing your app down (as the bug like this should), it will slowly eat your memory, resulting in all kind of weird effects throughout your app.
If you really want to offload logging into background, then use an actor pattern: create a channel with some decently sized buffer and launch a coroutine that reads from this channel sequentially and logs; send your stuff that needs to be logged in your app to that channel for your coroutine to log.
s
I actually I tried this in last night, and this is exactly what happened. Threads count that was 50 almost all the time slowly increased to to about 100, almost all the threads slowly transitioned to TIMED_WAITING, heap space started to grow slow, but after almost every thing chocked because of thread, increase in heap space was really slow. JVM bufer pool that was usually spiked in the similar fashion as thread count, and I saw massive loading and unloading of classes.
Thank you for all of your insights. This was really helpful. Clearly I was on the way to a disaster.
Do you think SharedFlows can be used as actor for this kind of pattern ?
l
yourScope.actor
would probably work best for your use case @Satyam Agarwal
s
Woh, I didn’t know about it. Will read up on it. Thank you so much
n
Do we have example/snippet somewhere using best practice for fire/forget.