clojure 2023-04-20

Hi all! I faced with an issue while moving our project api to reitit. Who knows how to solve issue with endpoints?

-> /connectors/validate-bucket/{type}/{path} 
-> /connectors/{a_id}/{c_id}/validate-bucket 
declared as:
["/connectors"
      [""
       {:post {...}}]
      ["/validate-bucket/{type}/{path}"
       {:get{:summary    "Validates something"
             :parameters {:path [:map [:type string?] [:path string?]]}
             :middleware [log-middleware]
             :handler    (fn [{:keys [path-params]}] (connectors/handle-validate-something (:type path-params) (:path path-params)))}}]
      ["/{a_id}"
       ["" {:get {...}]
       ["/{c_id}"
        ["" {:get    {...}]
        ["/validate-bucket" {:get {:summary    "Validates something else"
                                                   :parameters {:path [:map [:a_id string?] [:c_id pos-int?]]}
                                                   :middleware [log-middleware]
                                                   :handler    (fn [{:keys [path-params]}] (connectors/handle-validate-something-else (:a_id path-params) (:c_id path-params)))}}]]]]

I think https://cljdoc.org/d/metosin/reitit/0.6.0/doc/basics/route-conflicts describes your problem and a couple of ways to ignore conflicting routes

Thanks a lot! It helps )

Is it safe to include # in a var name, (def foo#bar 9)?

https://clojure.org/reference/reader#_symbols it is not officially supported. So even if it works now doesn't mean it will work forever.

Ok, best to avoid then. Thanks!

๐Ÿ‘ 1

Hi all, I have a Java interface:

@FunctionalInterface
public interface AsyncRunnable<R> {
  void run(AsyncExecution<R> execution) throws Exception;
}
and try to reify it like this:
....some code....

(reify AsyncRunnable
            (run [_# ^AsyncExecution execution]
              (~body execution)))

....more code....
and the error is: Mismatched return type: run, expected: void, had: java.lang.Object I need your help.

^void typehint in front of run should help

(reify AsyncRunnable
            (^void run [_# ^AsyncExecution execution]
              (~body execution)))

@delaguardo It helped, thank you. Itโ€™s interesting, because https://clojurians.slack.com/archives/C03S1KBA2/p1653670299984069 the approach was to remove typehint to make it work.

Alex Miller (Clojure team) 2023-04-20T11:57:22.590289Z

Iโ€™m surprised that that mattered or helped

Alex Miller (Clojure team) 2023-04-20T11:59:24.277879Z

I think the #_ there is removing not just the type hint but the whole arg so I suspect you are reifying the wrong signature?

it is _#

Alex Miller (Clojure team) 2023-04-20T12:00:18.816639Z

Oh nvm, I totally misread that

Hi guys! I have some web scraping code that does 25 requests sequentially. Results are then put into a larger map and are entirely independent from each other. Is there some easy way to get these to run concurrently? Code snippet:

{:monday (snipe "/mensa-erzbergerstrasse/montag.html")
 :tuesday (snipe "/mensa-erzbergerstrasse/dienstag.html")
 :wednesday (snipe "/mensa-erzbergerstrasse/mittwoch.html")
 :thursday (snipe "/mensa-erzbergerstrasse/donnerstag.html")
 :friday (snipe "/mensa-erzbergerstrasse/freitag.html")}
Full code https://github.com/GahliaDHBW/mensascrap2/blob/master/src/mensascrap2/core.clj pmap doubles execution time ๐Ÿ’€

The problem is the map produced in your code snippet is extremely hard to process any further. I'd prefer to pmap a named function to a seq of all endpoints, with that function doing all nescessary processing. So something like

(def data (pmap snipe endpoints))
with
(def endpoints (let [mensas #{"/mensa-erzbergerstrasse" "/mensa-schloss-gottesaue" "/cafeteria-moltkestrasse-30" "/mensa-moltke" "/mensa-am-adenauerring"}
                     days #{"/montag" "/dienstag" "/mittwoch" "/donnerstag" "/freitag"}
                     extensions #{".html"}
                     combinations (combo/cartesian-product mensas days extensions)]
                 (map (partial apply str) combinations)))
and
(defn snipe [endpoint]
  (->> endpoint
       (request)
       (s/select (s/class "aw-meal-category"))
       (map parse-metadata)))

this was just meant to show that i don't believe the "pmap double the execution time"

I'll continue to look into it, should work eventually

The easiest way is to use pmap. But if you want to have even a modicum of control over the parallelism, you should use something else. Plenty of discussions here with multiple references if you search for "pmap".

Have a look at https://github.com/clj-commons/manifold it has deferreds and streams for async programming with a pretty clean api.

I'd say Manifold is an overkill in this situation, assuming there are no other places in that code where it could be used.

pmap does seem to work for 2 endpoints at least, I'll check if it works more generally as well

When scaled up, it counterintuitively slows down the program by a factor of 2

@port19 you can use a client based on java.net.http and supply a fixed thread pool (e.g. of two).

user=> (import 'java.util.concurrent.Executors)
java.util.concurrent.Executors
user=> (def executor (Executors/newFixedThreadPool 2))
#'user/executor
user=> (require '[babashka.http-client :as http])
nil
user=> (def client (http/client {:executor 1}))

and then make requests with that client + :async true

(http/get "" {:client client :async true})

httpkit should also support supplying your own executor

clj-http-lite should work for that probably, I may look into it

how are you using pmap ? it's not the ideal solution because you don't control the thread pool (and probably don't care about the order) but it shouldn't double the execution time. I would not bother with non blocking for 25 requests, threads are good enough

like for you 5 requests:

dev> (time (do (into {} (map (fn [[k v]] [k (client/get (str "" v))])
                             {:monday "/mensa-erzbergerstrasse/montag.html"
                              :tuesday "/mensa-erzbergerstrasse/dienstag.html"
                              :wednesday "/mensa-erzbergerstrasse/mittwoch.html"
                              :thursday "/mensa-erzbergerstrasse/donnerstag.html"
                              :friday "/mensa-erzbergerstrasse/freitag.html"}))
               nil))
"Elapsed time: 383.66711 msecs"
;; => nil
dev> (time (do (into {} (pmap (fn [[k v]] [k (client/get (str "" v))])
                             {:monday "/mensa-erzbergerstrasse/montag.html"
                              :tuesday "/mensa-erzbergerstrasse/dienstag.html"
                              :wednesday "/mensa-erzbergerstrasse/mittwoch.html"
                              :thursday "/mensa-erzbergerstrasse/donnerstag.html"
                              :friday "/mensa-erzbergerstrasse/freitag.html"}))
               nil))
"Elapsed time: 80.316591 msecs"
;; => nil

I have a memory leak problem with (eval). Our server uses it for request processing, and I see that each minute JVM loads about 3.5k classes, which never get unloaded. This leads to a server breakdown when we run out of RAM. I'm wondering if there's a way to automatically unload classes spawned by (eval)? I know that one option is to try to rewrite the code without (eval), but that is a lot of work.

The issue was solved by adding JVM option -XX:MaxMetaspaceSize=512m. Turns out, code generated by (eval) doesn't have a GC root. So when we limit the metaspace, its GC is triggered as expected.

๐Ÿ‘ 1

> I know that one option is to try to rewrite the code without (eval), You could consider using #sci which doesn't have this problem

๐Ÿ‘€ 1

@shkertik say more about what you're doing, what is being evaled?

@ghadi we are creating some filtering functions with it. As I said, it could be rewritten without (eval). But rather than a big rewrite, a much better solution would be to add a couple lines that allow (eval) generated classes to be unloaded.

You can replace eval with:

(sci/eval-form (sci/init {}) the-form)
or
(sci/eval-string the-input-string)
or
(def ctx (sci/init {:namespaces {' {'your-fn your-fn}}}))
(sci/eval-form ctx the-form)
for keeping state over multiple evals. This should not generate any classes so nothing to unload. SCI is designed for "I want to use Clojure as a DSL within my app" type use cases. Let me know if you have more questions in #sci

before making changes, I'd try to show an example. Classes that are not referenced can be garbage collected

are you defining vars ? I'm curious if something like that could cleanup stuff:

(defn isolated-eval [form]
  (let [tmp-ns (gensym)]
    (create-ns tmp-ns)
    (try (binding [*ns* (the-ns tmp-ns)] (eval form))
         (finally (remove-ns tmp-ns)))))

or maybe it makes things worse

you're not using memoize by any chance? which could prevent garbage collection

From what I see, there are tons of instances of Clojure's DynamicClassLoader. Classes cannot be unloaded unless their class loader is garbage collected. So the question here is how can we dereference these loaders.

No memoize anywhere.

i'm surprised. From my understanding we are creating a new classloader everytime we eval, but classes generated by that classloader can still be garbage collected, so it's only a small memory leak. Otherwise I would just blow my repl's memory in a few hours no ?

If you have lots of requests with "eval"s, this may be interesting too:

user=> (time (dotimes [i 100000] (eval '(fn []))))
"Elapsed time: 17816.421042 msecs"
nil
user=> (time (dotimes [i 100000] (sci/eval-form (sci/init {}) '(fn []))))
"Elapsed time: 1021.765 msecs"
nil
Of course it depends on how often you will invoke that function. SCIs interpretation is slower than the compiled bytecode that Clj generates. So it all depends on your workload.

๐Ÿ‘ 2
๐Ÿ‘€ 1

@rolthiolliere Well, somehow our code generates thousands of classloader instances each minute. And it results in a slow metaspace usage creep, takes many hours to blow up. You can't really get this situation from typical REPL usage.

oh okay i misread, I though you were using eval once per inute and a single eval was generating 3.5k classes

paste some code, otherwise it's like playing 20 questions

people are throwing solutions at you without the specific problem being diagnosed

AFAIK, eval creates a new class loader, and those are not GC'ed in some cases. Have stumbled upon this before: https://stackoverflow.com/questions/71447267/clojure-memory-leaks-using-eval

๐Ÿ˜จ 1

I just published a library to Clojars for the first time and I have a question. How do I connect it to github? Currently it says N/A. I've noticed other libraries say things are pushed with a git tree but mine doesn't. I'm using deps-deploy for deployment.

It depends on how you published it. using deps.edn or lein?

deps.edn and deps-deploy

Thanks! I'll give that a shot

Another approach is to have a "template" pom.xml file with most of the data you need https://github.com/seancorfield/next-jdbc/blob/develop/template/pom.xml#L18-L22 and then in your build.clj specify :scm { :tag ... } and :src-pom https://github.com/seancorfield/next-jdbc/blob/develop/build.clj#L40-L48

๐Ÿ‘ 1

(I prefer that approach in my projects so that build.clj is simpler and I can have as much static detail in pom.xml as I want)

How do I get a version tag for a snapshot version?

So I tell the build task whether I'm building a release or a snapshot... And that's all done automatically in GitHub Actions...

For a snapshot workflow (after pushing/merging any commits to the main branch): https://github.com/seancorfield/next-jdbc/blob/develop/.github/workflows/test-and-snapshot.yml#L48

Yes, true, because I don't tag the repo for snapshots

(because every commit produces a release)

why do you even push a snapsnot release to clojars? git deps ftw ;)

Some of my users still use Leiningen ๐Ÿ˜›

(and Java compilation in some cases I guess)

(although we can solve that using prep-deps :))

@eoogbe You could put the SHA in :scm > :tag for snapshots... I just haven't bothered yet

Oh I just assumed snapshots were a normal part of pushing a prerelease version. If that's not supposed to be added, then I'll just jump to v0.1.0

SNAPSHOT isn't really the way to go. Just publish an -alpha0 release if it's really preview

or 0.1.0, fine too

SNAPSHOT builds are optional and not reproducible -- you'll get whatever is the latest snapshot JAR at the time of the request.

I do it just so folks can test the very latest versions from Clojars if they want (I checked with the Clojars maintainers first to ensure that pushing a SNAPSHOT for every commit was acceptable to them).

I push SNAPSHOTs on every commit for clj-kondo too and you can get a reproducible version from that, clojure-lsp uses this all the time

True, if you use the full date/time stamp version that each snapshot resolves to...

Sending clj-kondo/clj-kondo/2023.04.15-SNAPSHOT/clj-kondo-2023.04.15-20230418.173453-3.jar (361k)
    to 

It worked! tysm

๐Ÿ‘ 1
๐Ÿ‘๐Ÿป 1

(why) isn't there a way to destructure a map into a local namespace without having to type the keys?

(let [{:keys nil} {:a 5 :b 6}] a)
or something like that?

โœ… 1

Because it's better to be explicit about the local bindings you are introducing...

hmm. I think I can accept that.

(let [{:keys nil} {:if 1 :let 2 :do 3}] (if true (do (println "wat?") let))

(that's mostly a joke, but the point I'm making is that a hash map could inject arbitrary symbols and change the meaning of your code in unexpected ways)

another reason is that the compiler cannot know statically what is given dynamically

so if you would type:

(let [{:keys-all true} x]
  (assoc whatever-yo :foo :bar))
the compiler doesn't know what whatever-yo could be coming from some dynamically typed object

Heh, I remember reviewing some Python code where its author mutated locals(). It was impossible to make sense of, and the code wasn't working. The author claimed that there just wasn't any other way it could've been written without changing locals(). Needless to say, the author was wrong and the code without locals() ended up being drastically simpler.

so if you were going to have that it would be better (though not necessarily good) in static type land

in static typing this would be easier to implement, but I haven't encountered any languages that have this

Try :refer :all for a taste of how awful this quickly becomes ๐Ÿ˜…

๐Ÿ‘† 4

yeah i should follow the rules and say what is motivating this q

I have a multimethod and there's a couple bindings that appear at the top of every or almost every method implementation

trying to factor this out

you could do this using a macro (but even then I'd probably avoid it)

yeah that doesn't seem appropriate to me fwiw

and there's no way to access the dispatching value from within the method, right?

that seems like itwould be another bad idea

you could call the dispatch function on the this argument to get it?

Well, you can define the dispatch function seperately

just call it again

you can also just change the part that is the multimethod (dependent on who the consumers are)

so lets say you have a multimethod like this

(defmulti do-thing #(get-in % [:metadata :type]))

(defn f [x]
  (get-in x [:something :age]))
(defn g [x]
  (get-in x [:whatever :abc]))
(defmethod do-thing :apple
  [apple]
  (let [a (f apple)
        b (g apple)]))

(defmethod do-thing :pear
  [pear]
  (let [a (f pear)
        b (g pear)]))

if that let [a ... b ...] part is something you want extracted you can always just change the multimethod part to be further down

(defmulti do-thing*
          (fn [obj & _] 
            (get-in obj [:metadata :type])))

(defmethod do-thing* :apple
  [apple a b])

(defmethod do-thing* :pear
  [pear a b])

(defn do-thing 
  [fruit]
  (do-thing* fruit (f fruit) (g fruit)))

if that makes sense

really depends on why you are using a multimethod though

I see what you mean

and if you actually expect it to be extended

there are other options too

but without going insane, they are all just around moving where and how dispatch and extraction happen

I think the approach you describe suits my needs;thankss for taking the time @emccue