https://kotlinlang.org logo
Title
b

bbaldino

05/14/2020, 3:43 PM
I've got a ktor server handling requests on something like
/user/{userId}
, how can I make sure requests pertaining to a specific
userId
are handled serially, but requests for different `userId`s can be handled in parallel? I don't want to use a single-threaded dispatcher per
userId
, I'd like to have a single shared pool that handles all the requests, just enforcing that requests for a given
userId
are handled in order. (I think this is more of a general coroutine question with ktor as an example than a ktor-specific question, but let me know if I'm wrong)
Basically: I'd like to use a single dispatcher, but allow serialization of coroutine task processing on some scope (userId here), is this possible without using a coroutine contexted single-threaded dispatcher per scope and doing
withContext
?
z

Zach Klippenstein (he/him) [MOD]

05/14/2020, 4:52 PM
You don’t need any special dispatchers for this. You almost never need a custom dispatcher. One way to solve this is using actors: have an actor for each
userId
, store their channels in a map by the
userId
.
b

bbaldino

05/14/2020, 4:53 PM
Thanks Zach...Wasn't thinking a custom dispatcher, just one per user ID...so is having a channel per user ID the right way to accomplish this?
I really love being able to do
withContext
because of how neat it is, but my understanding is that the only way to accomplish the serialization per userId is to have a context per user ID, but then also each context would need it's own dispatcher instance (backed by a single-threaded exectuor) to ensure things were handled in serially
z

Zach Klippenstein (he/him) [MOD]

05/14/2020, 4:54 PM
Sorry, s/custom/dedicated/
👍 1
Threads are expensive, both in terms of memory and CPU. That is one of the main motivations for adding coroutines to the language in the first place. Making new threads for stuff like this largely defeats the point of using coroutines.
b

bbaldino

05/14/2020, 4:56 PM
Exactly why I want to avoid it 🙂
z

Zach Klippenstein (he/him) [MOD]

05/14/2020, 4:57 PM
There are many ways to solve this, actors is just one of them.
b

bbaldino

05/14/2020, 4:57 PM
I wanted just a single shared pool, just to be able to serialize the processing per user ID...I know the 'official' actor stuff is deprecated, but I the Channel solution you mentioned is basically an implementation of that, right?
z

Zach Klippenstein (he/him) [MOD]

05/14/2020, 4:58 PM
Yea, pretty much. I meant “actor” in the abstract sense, so you can use the
actor
builder but that’s basically just a
Channel
+ a
launch
, nothing special really.
b

bbaldino

05/14/2020, 4:59 PM
Will the channel-style make it more difficult to return the 'response'? I'm sure it can be done by passing a lambda in the task or something, but is there a nice idiom around getting the result of a task posted to a Channel?
z

Zach Klippenstein (he/him) [MOD]

05/14/2020, 5:02 PM
Depending on how “pure” your actors are, there are a number of ways to model responses (google “actor model”). The simplest way with channels is to just include a
CompletableDeferred
in the message to the actor for the actor to send the response on.
Another way would be to keep a map of `Mutex`es by user ID, and lock the mutex for a user before processing the request. That won’t necessarily preserve that requests are handled in the same order they arrived, but you might not care about ordering and this is slightly cheaper than the actor solution and probably even less code.
b

bbaldino

05/14/2020, 5:03 PM
I thought mutexes are generally a no-no with coroutines, or does kotlin have a coroutine-friendly mutex?
z

Zach Klippenstein (he/him) [MOD]

05/14/2020, 5:04 PM
Yes, the coroutines library has
Mutex
and
Semaphore
.
b

bbaldino

05/14/2020, 5:05 PM
Yeah I think your idea about passing the deferred is what occurred to me. But if there's a coroutine-friendly mutex then that could be even better--why wouldn't I be able to preserve order with that, though?
z

Zach Klippenstein (he/him) [MOD]

05/14/2020, 5:07 PM
Well, I believe the standard Mutex will actually preserve first-in first-out order of
lock
calls, so you actually would preserve order. But I would verify that first.
b

bbaldino

05/14/2020, 5:07 PM
Ok
Thanks a lot for the pointers, Zach! I'll dig into this
👍 1
u

ursus

05/18/2020, 7:48 AM
Semaphore