core-async

2019-11-28T00:02:39.173700Z

if you have an use of try/catch/finally in your real code you might try without that, there are some bugs in how the go macro compiles that which can lead to weirdness

2019-11-28T00:05:24.173900Z

I don't believe so

2019-11-28T00:06:01.174400Z

Oh. It does take place in an add-watch fn.

😬 1
2019-11-28T00:06:17.174700Z

will try to add that to the repro. code

2019-11-28T00:09:23.175500Z

I bet if you go through, and put printlns everywhere, you will find two printlns that happen in a different order then you expect

2019-11-28T00:11:33.176300Z

and it'll be something like the tap isn't happening until after all the go loops have published their values to a mult where no body is listening

2019-11-28T00:17:06.176600Z

hmmmmm

2019-11-28T00:18:22.177300Z

(def ch1 (a/chan 1))
(def m (a/mult ch1))

(def aa (atom nil))

(add-watch aa ::aa
           (fn [k r o n]
             (let [ch2 (a/tap m (a/chan 1))]
               (log/debug "watch changed")
               (a/go
                 (when-let [x (a/<! ch2)]
                   (println "GOT2" x)
                   ;;(a/close! ch2)
                   )))))

(doseq [x (range 10)]
  (a/put! ch1 x)
  ;;(a/go (a/>! ch1 x))
  )


(def but
  (d/div
   (d/button {:onclick #(a/put! ch1 (rand))} "put")
   (d/button {:onclick #(reset! aa 2)} "reset")))
So this is closer to the the real thing, and now the go loop never prints, with or without the close!

2019-11-28T00:19:35.178400Z

yeah

2019-11-28T00:19:47.178900Z

(i realize now the watch function is dumb, and i could stream the changes as another channel but should this work anyway?)

2019-11-28T00:20:02.179300Z

all the puts flow through the mult and go no where because there is no one listening

2019-11-28T00:21:02.180500Z

and then later you click the button, it resets the atom, which creates a tap, but by that point there is nothing there

2019-11-28T00:21:33.181300Z

by all the puts I mean the a/put! in the doseq

2019-11-28T00:21:39.181500Z

ah, i think that's still a case of a bad repro. sorry. The real thing creates the tap, then makes a request that should result in the puts, then does the go block

2019-11-28T00:22:19.181800Z

it should result in puts, but does it?

2019-11-28T00:22:46.182100Z

(def ch1 (a/chan 1))
(def m (a/mult ch1))

(def aa (atom nil))

(defn do-puts! []
  (doseq [x (range 10)]
    (a/put! ch1 x)
    ;;(a/go (a/>! ch1 x))
    ))

(add-watch aa ::aa
           (fn [k r o n]
             (let [ch2 (a/tap m (a/chan 1))]
               (do-puts!)
               (log/debug "watch changed")
               (a/go
                 (when-let [x (a/<! ch2)]
                   (println "GOT2" x)
                   ;;(a/close! ch2)
                   )))))




(def but
  (d/div
   (d/button {:onclick #(a/put! ch1 (rand))} "put")
   (d/button {:onclick #(reset! aa 2)} "reset")))

2019-11-28T00:23:39.182500Z

and now it's back to working with and without the close!

2019-11-28T00:24:01.183Z

(the go block print executes on reset of the atom)

2019-11-28T00:24:20.183300Z

you are getting at least one println out

2019-11-28T00:24:58.184100Z

and what is happening is the mult is getting clogged up because you never untap after your watch is done

2019-11-28T00:25:11.184500Z

the close is effectively an untap

2019-11-28T00:25:57.185500Z

in your real world case you can replace the a/close with a/untap, see that it makes it work, and confirm that this is correct

2019-11-28T00:26:08.185700Z

oh yeah, sure

2019-11-28T00:27:32.187100Z

at which means the whole watch things happens at least once, so you should see at least one println, and if you don't, you can start trying to figure out why

2019-11-28T00:29:38.187800Z

ok this is confusing. The untap works, but doesn't seem to actually untap anything judging from libral printlns

2019-11-28T00:29:57.188300Z

i mean the print happens with the untap, same as no close! or untap.

2019-11-28T00:30:27.188800Z

but the tapped chans are still receiving values after the untap

2019-11-28T00:31:03.189500Z

just for the sake of it, try untap-all

2019-11-28T00:32:26.190400Z

untap-all works as expected!

2019-11-28T00:32:33.190500Z

I suspect somewhere, somehow you do have a tap, where the channel being tapped to is not being consumed from

2019-11-28T00:32:46.190700Z

which will block the mult

2019-11-28T00:39:47.192500Z

i can't seem to find one at the moment (which doesn't mean it doesn't exist).

2019-11-28T00:40:10.193100Z

gotta run out for now, probably best to step away from this for a bit anyway, but thanks for all the help/patience!

2019-11-28T16:47:20.195600Z

I finally tracked this down! I was basically using code inspired by/equivalent to this: https://github.com/capitalone/cqrs-manager-for-distributed-reactive-services/blob/master/src/com/capitalone/commander/api.clj#L288-L292 This provides a way to get a copy of a single source chan, but with its events transformed (by piping through a chan with a transducer). The transformation chan that's actually tapped is closed over and thus unable to be closed/untapped

2019-11-28T16:51:09.196700Z

Out of curiosity, this code is probably inherently wrong/bad right? the transformation (ie (map command-map) transducer above) should be done before the mult is created right?

2019-11-28T16:53:16.198Z

Putting a sliding/dropping buffer on the int chan gets the job done also but it still doesn't allow for untapping

2019-11-28T17:02:56.202100Z

What is the ideal usage of mult anyway? I have a clojurescript app and have a single channel that serves as the point of entry for all messages from a server. I have to distribute those messages to various ui components. I've tended to approach this by making a mult wrapper for that channel at the top level, and creating helper functions of that mult such as the commander code above, and call these from the ui components. Is it better to just use the main channel instead and have each component create it's own mult off that if needed?

2019-11-28T17:07:39.203300Z

Actually I guess the mult per component wouldn't work because then nothing could take from the main incoming channel, multiple mults would race to take messages, etc

Jan K 2019-11-28T17:57:13.204Z

Sounds like pub/sub (which is implemented using mults) would be more suitable for that

2019-11-28T18:32:16.206100Z

if I understand the doc string correctly, a pub wont' globally get "stuck" on one held up sub on any topic, but rather each sub is it's own mult and can only hold up other sub'ed chans with the same topic right

Jan K 2019-11-28T18:49:29.208900Z

With the defaults a pub can actually get stuck globally if there's a slow subscriber, since the mults are attached to unbuffered channels. You could pass a buf-fn to the pub to avoid it (drop messages instead of blocking), or just make sure to subscribe to non-blocking channels.

2019-11-28T19:03:52.209700Z

ok gotcha