Fork me on GitHub

Hi all, I am wondering if >! from a "go block" into a channel is starvation free in Clojure. For example, if I have two producers and one consumer:

(let [c (chan)]
  ;; producer 1
  (go-loop [count 1] (>! c (str "p1: " count)) (recur (inc count))

  ;; producer 2
  (go-loop [count 1] (>! c (str "p2: " count)) (recur (inc count))

  ;; only consumer
  (go-loop [] (println (<! c)) (recur)))
I am wondering if it is possible one of the producer gets starved? Thanks!


Actually it seems put! has a FIFO order. I think we should be good :thinking_face:

Alex Miller (Clojure team)02:08:51

note that you're using an unbuffered channel (chan) so it can hold 0 items. The producers will basically both be trying to rendezvous with the consumer to transfer the value

Alex Miller (Clojure team)02:08:57

how that plays out with 2 producers is somewhat arbitrary and based on how notification happens at the java layer, there are no guarantees on the puts between the 2 producers

Alex Miller (Clojure team)03:08:29

so you might see two puts from the first, then some from the second, etc (but they should be in order for each producer)


I came across with this article and apparently Clojure will place blocked puts and takes in a FIFO queue, even for unbuffered channels :thinking_face:

Alex Miller (Clojure team)03:08:13

I think you're missing some of the details - that will happen with put! and take!, but you're using >! and <! which are parking ops and they will simply park (wait, without blocking a thread) until the op can complete with a corresponding take or put

πŸ‘ 3

Thanks Alex! I think it makes sense now πŸ™‚


Brains a little slow this morning. I have a map along these lines {#{1 2 3},"a",#{4,5,6},"b",…} and am thinking about f such that (f 2) => a , (f 6) => b and so on. Maybe this isn't the right structure? I have something ugly using filter but I feel like there's a simpler way (that isn't unrolling the sets into the map)


Why do you want to avoid unrolling the sets?


They are a bit larger than the example


(Yes I realise it would probably have taken less time than framing the question in the first place, but then I wouldn't learn anything :))


I guess I should be less lazy


:) "Larger than the example" should not affect anything because sets are already basically maps, so by wrapping multiple keys in sets you're not saving anything.


Well only typing time


Actually I do have another approach using (vec (concat (repeat x :a) (repeat y :b)))


but I guess I will just type it out


Ah, but you can just unroll the sets with code, no? No need to unroll them manually, so you can still save on typing.


Yeah, I agree with p-himik here, you can do it with code:

(defn unroll-keys [m]
  (into {}
        (mapcat (fn [[ks v]] (map #(vector % v) ks)))
Of course, this is UB when the keys aren't disjoint.

πŸ‘ 3

Thanks, I hadn't thought of unrolling with code. Good idea although I overcame my laziness in the meantime πŸ™‚

πŸ‘ 6

Hi I recall there was some discussion over clojure.cache flaws for instance, is it possible that cache-get-through may fail under some intensive load? endpoint should cache sql queries, but I see about 27M select statements in the db log for last hour (which means cache doesnt work) do you have any ideas maybe? thanks


What do you use for state management? An atom maybe?


AFAIK, no locks


in that case when there is a lot of load, meaning multiple thread trying to swap! the cache, one will succeed (eventually), but it will take several tries (queries)

πŸ‘ 3

So probably good to wrap it with a locking πŸ™‚


so it is kinda multiplies a load even

πŸ˜… 3

thanks a lot!

πŸ‘ 3

@U1V7K1YTZ The locking might not be necessary if you use delay (swap! C2 cache/through-cache :d (fn [] (delay (do-query))))

πŸ’₯ 3

yes, I think Chris mentioned something like this


Is it preferable to pass around a StringBuilder/cl-format rather than several nested calls to (str)?


it's the "tree falls in a forest" type thing- it makes sense if the scope of the builder is limited to a lexical block where the pattern of usage (append only) is safe, you probably don't want to try to use a StringBuilder that escapes scope


and yes, nested string calls are an antipattern (though apply str or string/join will do the stringbuilder stuff for you already)


I'm sure it rarely makes the difference performance wise, unless you're rolling your own serialization lib or something. I have a sick fascination with cl-format. Why should I write all the tedious imperative generation of intermediate representations when I have this (insane, but) perfect DSL to do it for me! parrot


the question is whether the string ops are your bottleneck (and for many domains that can truly be the case - eg. web servers with back end rendering, having a more efficient template tool makes a big difference)


and yes, cl-format is remarkable


cl-format makes me wonder if there's a library out there that implements LOOP using transients that could be used in hot loop situations


haha, LOOP is insane


I'm working on a lib that does low level packed contiguous data structures, that might be a cool feature if I implemented a "hold-my-beer" namespace

πŸ‘€ 3

the LOOP periodic table from Land of Lisp might be my favourite programming book figure of all time

πŸ†’ 3

LOOP is so audaciously un-apologetic about what it does

Eduardo Mata15:08:07

Does anyone know who to check for outdated dependencies using Boot?


inb4 "boot is an outdated dependency" πŸ˜„

πŸ˜‚ 9

@contact904 this looks like it wants to do what you need though I can't personally vouch for it


and in all seriousness, if you have the flexibility to do so, I'd consider a boot -> deps.edn switch an improvement / future proofing, not much is happening in the boot ecosystem lately


@contact904 I agree with @noisesmith on switching but if you must stay, that ability is built in to boot's show

πŸ’― 6

This oddly specific knowledge comes from having used the lib @noisesmith linked a few years ago haha:


@contact904 boot -d boot-deps ancient

βœ”οΈ 3
Eduardo Mata15:08:48

Thank you so much!


MacOS just updated, and now clj gives this error. All the more weird because clojure seems to work fine. clojure: error: Cannot execute clojure: No such file or directory

πŸ‘€ 3

What do you see if you check the path:


ls -al /usr/local/bin/clj


This is what I get: */usr/local/bin/clj*@ -> ../Cellar/clojure/


It is pointing to a homebrew location.


I typically use strace in these situations; may seem like overkill, but it’s typically very obvious which fopen() is causing the error


lrwxr-xr-x 1 dave.dixon admin 36 Aug 17 10:41 /usr/local/bin/clj -> ../Cellar/clojure/


Maybe just re-install Clojure?


MacOS screwed something up. Having a similar issue with sbt, works from the command line, but not from build scripts etc.


I did try a reinstall of the Clojure CLI tools, though without uninstall, same result.


maybe an issue with java path, or JAVA_HOME ?


The clojure command runs fine. It's just clj that fails.


Maybe something to do with readline ?


or permissions


If I put the full path to clojure in the rlwrap command used by clj, it works. Scratching my head as to what would have changed in MacOS to cause that.


Obviously something like it is no longer picking up the $PATH value from the shell environment.


@U066LQXPZ Are you aware that Catalina is using zsh is the default shell now instead of bash?


Yes. Though I'm using fish as my shell.


Can someone recommend an oauth2 client library for clojure?


@U016XBH746B I wrote this library a few years back, and used it in production with a few services. The use of Schema is probably no longer desirable and could be removed easily enough.


I would recommend avoiding coupling the OAuth 2.0 protocol with any HTTP client/server, which is the approach I took in this library. Doing so worked well and allowed us to use any Ring-compatible library with custom logging/telemetry.