Fork me on GitHub
#core-async
<
2018-09-14
>
michihuber14:09:11

Hi, I'm using core async for coordinating IO and have doubts about my architecture. My app offers collaborating editing using operational transforms on documents (think Google docs). One go-loop reads all messages being sent by clients over websocket. It keeps a map of document-id -> chan and forwards them. Each document-chan is consumed by a go-loop that receives the messages, updates the state of the document (transforming the message if necessary), persists the messages in the db (blocking!), and forwards them to the other clients editing the same document. Clients only have one message in flight at a time and latency is not a huge priority – I mainly want to avoid everything grinding to a halt. Would this setup work for a larger number of documents being edited? Alternatively, I could keep a fixed number N of go-loops, that each handle M/N documents. Or is there an entirely different solution?

noisesmith16:09:04

IO inside go blocks will break at medium scale, the fixed number of threads are low enough that you could lock all core.async up hard with less than ten documents

noisesmith16:09:16

this is the reason clojure.core.async/thread exists, it uses a thread in an expandable pool (as opposed to the fixed pool go blocks use) and returns a channel that you can properly park on (rather than blocking on IO)

hiredman16:09:13

because of core.async's back pressure it is very easy to get in to a situation where a slow consumer is blocking things for everyone else

michihuber17:09:51

so, instead of go-loop I use async/thread and loop and then things should work until I hit the thread limit, correct?

noisesmith17:09:55

you can call and park on thread inside go-loop

noisesmith17:09:22

and you are very unlikely to hit any thread limit if you do that (unless you mean your OS limit for usable threads)

michihuber17:09:30

ah, so I create a go-loop for each doc, but then do the message handling (with the blocking IO) inside a thread call?

noisesmith17:09:42

yeah - that's how I would do it at least

noisesmith17:09:59

(go-loop [... ...] (let [msg (<! doc-chan)] (when (some? msg) (let [foo (<! (thread ...))] ... (recur ...))))) - something like this

michihuber17:09:58

gotcha, that makes perfect sense to me... thanks, noisesmith!

noisesmith17:09:36

I treat (<! (thread ...)) as my placeholder for anything that might block, I rarely want to call some blocking operation inside a go block without doing a park on it immediately