Fork me on GitHub
#core-async
<
2020-05-15
>
Ben Sless08:05:46

Reading Hoare's paper, I notice core.async doesn't provide exactly the same semantics as the alternative command. What are your thoughts on this implementation as a general idea? I'm focusing here on emulating the semantics, not performance

cavan13:05:30

Hello, i came across a bug recently with core.async's timeout channel, In my web-server i create a timeout channel for each request and close it at the end of the request.. the problem is the timeout channel sometimes are created with the time-period already expired during high load... I know this has been reported before in the past (https://clojure.atlassian.net/browse/ASYNC-225) and the obvious explanation i saw was to not close the timeout channel and let the Garbage collection handle it for you. But that causes a LOT of memory usage.. so i'm not sure what is the best way ahead. Ideas will be much appreciated. Thanks guys in advance

hiredman15:05:37

You must not close timeout channels

8
hiredman15:05:45

It is best to consider timeout channels as a global resource. Your memory usage issues are likely because mutiple requests are getting the same timeout channel, and when you close a timeout channel for 1 request you are incorrectly also closing the timeout for other requests

hiredman15:05:15

So those other requests are prematurely cleaning up

cavan16:05:42

Thank you for replying, Yes i agree 100% that is what is happening, but as i mentioned i create a new timeout channel each time a http request is made AND on the server runs where i don't close channels explicitly, i just end up with a lot of CPU/ram usage(which is probably because i create many channels and the GC is slow to collect the ones that are obsolete, leaving behind a lot of stuff in memory), which goes away with closing channels... but closing channels brings woes of its own as you've mentioned.. As a hack i thought to replicate the timeout channel by creating a new channel, and doing a sleep on it for the same time as the timeout would be and then dropping something on the said channel to signify timeout is complete... but that doesn't look too pretty and i was hoping you guys had some better alternatives 🙂

ghadi16:05:40

closing a channel doesn't usually release resources anyways

ghadi16:05:11

I'm not clear on what the objective is

cavan17:05:27

sorry if i was unclear, But the gist of it is to be able to create a lot of new timeout channels for each http request.. (i find myself closing timeoutchannels at the end because if i don't, i see a significant increase in CPU use.).. But by closing timeout channels, i sometimes run into the problem where the new timeout channel i create is actually an older channel that is already exhausted.. I want each timeout channel to be a new resource so i'm not bitten by that behaviour... i searched around and see that this problem has been documented in the past https://clojure.atlassian.net/browse/ASYNC-225

noisesmith17:05:31

this is an intentional behavior of core.async - by reusing / duplicating them it reduces overhead, by closing them you prevent other people from getting their timeouts

noisesmith17:05:41

notice the issue tag on the issue you linked was literally edited from "bug" to "new feature"

cavan17:05:51

Yes i did notice it , and was hoping if someone experienced this issue before and had a workaround for it 🙂

noisesmith17:05:04

I'd take a closer look at what's using the CPU -- it could be that you were reducing load by dropping events on the floor, but that only adresses the symptom - maybe you need larger units of work between channel ops, maybe you need to move CPU intensive tasks out of go blocks into threads

noisesmith17:05:03

and IO usage out of go blocks as well

hiredman17:05:34

you may just have a bit of a space leak

❤️ 4
hiredman17:05:57

because the timeout is shared between requests it causes the callbacks on the channel to persist between requests

hiredman17:05:21

so introducing some level of indirection between the timeout and the callbacks may help

hiredman17:05:56

(defn my-timeout [ms] (async/go (async/<! (async/timeout ms))))

hiredman17:05:46

having high cpu usage without closing timeout channels sounds like a bug somewhere though

cavan17:05:19

I loved the idea about having the indirection, not sure how succesful it would be , but i'm definitely gonna try it , thanks! 😄

ghadi17:05:10

closing a channel is very different than closing an InputStream

ghadi17:05:32

there's no file descriptors or heavyweight things that are relinquished

dpsutton17:05:10

> new timeout channel i create is actually an older channel that is already exhausted.. I want each timeout channel to be a new resource so i'm not bitten by that behaviour. this sentence seems weird to me. an exhausted timeout channel. does this mean already elapsed?

cavan18:05:39

yes i meant elapsed, sorry for the confusion

hiredman17:05:27

what closing a channel tends to release is anyone waiting on the channel, which can be callbacks that close over just about anything

hiredman18:05:09

likely the timeout channel isn't closed (exchausted) because the timeout is elapsed in this scenario it is likely closed because some other request already closed it

cavan18:05:49

sorry for the confusion, by exhausted i mean elapsed. Since i create timeout channels for each request, at times i would come across newly created timeout channels that are already elapsed since i believe timeouts are shared in cljs.core.async