Fork me on GitHub
#babashka
<
2023-11-02
>
joakimen09:11:03

Heyo! I have a concurrency exception that only occurs while using babashka and not jvm Clojure when trying to query AWS-resources in parallel using https://github.com/grzm/awyeah-api. I have a bit of repro-code and a stack-trace, should I dump the stack trace in ๐Ÿงต as a start, or do you want an issue on GitHub?

borkdude09:11:55

Is it possible to make the smallest repro possible, preferably even without the awyeah-api lib?

borkdude09:11:55

If it's an issue with awyeah-api I suggest creating an issue there, but if you can demonstrate it's a bb issue, please make an issue with bb

joakimen10:11:58

That's the kicker; it wasn't obvious to me from the stack trace whether the exception originated from the library or not. It doesn't make it easier that it only fails for a few elements of the collection I'm processing either. Anyway, the reason I started in this channel was because I wasn't able to reproduce the error on JVM, so I thought maybe you could deduce something from the stack trace

borkdude10:11:06

Feel free to post the stacktrace (in a gist), perhaps it will ring a bell.

joakimen10:11:34

This only happens for a few elements of the collection. I have 47 queues, and when fetching attributes for each queue in parallel, like 1-10 of them fail with that error. On JVM, none of them produce that error

borkdude10:11:52

ok, this totally rings a bell, it's a reflection configuration issue

borkdude10:11:57

let me fix that right away

borkdude10:11:00

oh no it wasn't what I thought

borkdude10:11:58

I've seen a similar error in httpkit with native-image. this was an issue with SimpleDateFormat or something. I migrated that to java.time and the error went away, let's check if awyeah is using this old stuff

borkdude10:11:56

check this issue: https://github.com/http-kit/http-kit/issues/543 are you providing a virtual thread pool or something?

borkdude10:11:15

The error seems to happen around clojure.data.xml$parse so perhaps it's possible to make a repro using that

borkdude10:11:33

aaah maybe awyeah-api is using core.async?

borkdude10:11:22

yeah, gotcha. go blocks in core.async in bb are using virtual threads

borkdude10:11:47

let me try to repro this

borkdude11:11:16

I'm trying to repro it like this:

$ bb -e '(clojure.core.async/<!! (clojure.core.async/go (clojure.data.xml/parse (java.io.ByteArrayInputStream. (.getBytes "<a></a>")))))'
#xml/element{:tag :a}

borkdude11:11:48

Perhaps it would help you if executed a dummy XML read like the above in your program before doing anything with awyeah

borkdude11:11:02

A workaround for your problem would be I think:

(alter-var-root #'clojure.core.async/go (constantly @#'clojure.core.async/thread))

borkdude11:11:20

this reverts using virtual threads for go blocks to normal threads

borkdude11:11:01

Ah it seems the native image 21.0.0.1 fixes this problem

borkdude11:11:13

let me upgrade so you can test the dev version

joakimen11:11:24

Sorry, was eating! I got this error using the Java Executors stuff with a fixed thread pool like this

(ns sqs.lib.repro
  (:require [com.grzm.awyeah.client.api :as aws])
  (:import [java.util.concurrent ExecutorService Executors Future]))

(defn jxmap
  ([f coll] (jxmap f coll 100))
  ([f coll concurrency]
   (let [executor  (Executors/newFixedThreadPool concurrency)
         tasks (mapv #(fn [] (f %)) coll)]
     (->> (.invokeAll ^ExecutorService executor tasks)
          (map #(.get ^Future %))))))
then something like
(def attributes (jxmap #(aws/invoke sqs-client {:op :GetQueueAttributes
                                                    :request {:QueueUrl %
                                                              :AttributeNames ["All"]}}) queues 100))
where queues is a vec of strings

borkdude11:11:00

yeah, it's related to bb's core.async implementation using vthreads

borkdude11:11:12

try this as a workaround:

(alter-var-root #'clojure.core.async/go (constantly @#'clojure.core.async/thread))

borkdude11:11:29

before actually requiring/loading awyeah

joakimen11:11:38

And that was used in the awyeah-lib, or?

borkdude11:11:58

you can use this:

BABASHKA_PRELOADS="(alter-var-root #'clojure.core.async/go (constantly @#'clojure.core.async/thread))"

borkdude11:11:21

I'll upgrade native image to 21.0.0.1 and then you can also try it without that patch

joakimen11:11:14

I tried it now by evaluating (alter-var-root #'clojure.core.async/go (constantly @#'clojure.core.async/thread)) , then evaluating the async code. I still get the same problem here, is there a difference between evaluating it in the repl, and providing the env var?

borkdude11:11:19

you should ensure to NOT load awyeah before you evaluate this

borkdude11:11:24

the env var is a safer bet

joakimen11:11:33

right ok, sec

joakimen11:11:31

That seems to have done the trick!

of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures

joakimen11:11:50

I wouldn't have caught that in a million years

joakimen11:11:32

Should this be an issue for awyeah then?

borkdude11:11:24

no, it's a bb issue, feel free to post an issue, I'm working on a fix (by upgrading graalvm). Also please post the workaround in the issue so people can find it later

borkdude11:11:37

which OS are you on?

borkdude11:11:03

alright, try this:

bash <(curl ) --dev-build --dir /tmp
and then run your program with /tmp/bb Make sure you unset the env variable. Use a new terminal just to be sure you haven't still set it.

joakimen11:11:25

Looking good :star-struck:

of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures
of 47 queues, there were 0 failures

joakimen11:11:40

(System/getenv "BABASHKA_PRELOADS") ;; => nil

joakimen11:11:43

What was the fix? So many branches. Saw the bump of graalvm here https://github.com/babashka/babashka/commits/21_0_0_1, but was that all you needed, and how did you know?

borkdude11:11:00

yes, that branch fixes it

borkdude11:11:28

I didn't know, but I suspected that the graalvm folks might have fixed this bug

joakimen11:11:45

That was a good bet

joakimen11:11:34

Seriously, amazing catch, thanks so much, I've had this problem for weeks and thought I was just getting increasingly bad at clojure

borkdude11:11:48

hmm, actually, this is weird. the version you installed wasn't the version from my branch but from the master branch. So I'm puzzled why it works for you

borkdude11:11:07

that release is from 3 days ago

joakimen11:11:07

I haven't upgraded homebrew-bb for those days, and just did it during lunchtime now. Will try again with the homebrew-version and see

borkdude11:11:41

no, this has nothing to do with homebrew

borkdude11:11:51

but the dev-builds releases only update from the master branch

borkdude11:11:21

anyway I can repro it locally using an example I posted in the issue and I can confirm that it works with 21.0.1 which I have installed locally

borkdude12:11:02

but it also mysteriously works with the build from 3 days ago

borkdude12:11:28

It could be the result of disabling some optimization

borkdude12:11:43

Anyway glad it works with the dev build

joakimen12:11:22

Yeah ๐Ÿ˜„ Can expect this to be part of v1.3.186 later then?

borkdude12:11:01

For sure

๐Ÿ™ 1
borkdude12:11:38

thanks for sponsoring :)

joakimen12:11:38

If anyone in this community deserves it, it's you โญ

โค๏ธ 2
๐Ÿ‘ 1
borkdude13:11:20

1.3.186 was just released

๐ŸŽŠ 1
elken13:11:36

Just seen that https://justine.lol/cosmo3/ has been released (TL;DR compilation toolchain for "actually portable executables"), wonder if it's worth hooking it up to graal, can you specify other compiler toolchains? Posting here because it would be cool to have a single bb that actually runs the same everywhere

borkdude13:11:23

thanks for sharing, I'll ask in the native-image channel on native-image if they have heard of this

๐Ÿ™Œ 1
JAtkins21:11:11

I don't think this is a babashka problem, but I feel it's likely that someone here will know the solution ๐Ÿ™‚. Why does curl/get ... {:as :stream} download into ram first?

(let [request (curl/get "myurl" {:as :stream})]
    (with-open [body (:body request)]
      (println (type body))
      (aws/invoke s3 {:op      :PutObject
                      :request {:Bucket        "..."
                                :Key           "..."
                                :ContentType   (get-in request [:headers "content-type"])
                                :ContentLength (Integer. (get-in request [:headers "content-length"] 0))
                                :Body          body}})))
This creates the network behavior in the attached screenshot. Is there any way to reduce the buffer of the input stream to prevent loading the whole file into ram? I suppose the only other option would be to download the file direct to disk, then move that to s3. i.e. stream network -> disk, disk -> s3, but that seems quite wasteful.

โœ… 1
borkdude22:11:17

What is aws here?

JAtkins22:11:32

cognitect aws api

borkdude22:11:37

is this in a JVM?

JAtkins22:11:07

oo, I totally forgot that bb.curl was primarily for bb, not jvm clj

borkdude22:11:08

AFAIK the stream should not be consumed if you don't read it

borkdude22:11:56

I think babashka.http-client is a better option if you're on the JVM

borkdude22:11:00

the interface is mostly the same

JAtkins22:11:10

sounds good, I'll give that a spin. thanks!

JAtkins22:11:44

that works a bit better, much appreciated!

๐Ÿ‘ 1