This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-03-30
Channels
- # aleph (2)
- # announcements (8)
- # babashka (12)
- # beginners (34)
- # calva (36)
- # cherry (3)
- # cider (1)
- # clj-kondo (11)
- # clj-otel (6)
- # cljdoc (31)
- # clojure (121)
- # clojure-conj (1)
- # clojure-czech (2)
- # clojure-europe (109)
- # clojure-nl (1)
- # clojure-norway (5)
- # clojure-uk (3)
- # cursive (3)
- # datahike (1)
- # datomic (9)
- # deps-new (6)
- # docker (5)
- # emacs (21)
- # fulcro (4)
- # hoplon (16)
- # introduce-yourself (2)
- # london-clojurians (5)
- # lsp (87)
- # malli (17)
- # missionary (1)
- # nbb (27)
- # off-topic (257)
- # pathom (4)
- # portal (42)
- # practicalli (1)
- # rdf (3)
- # releases (2)
- # shadow-cljs (49)
- # slack-help (3)
- # timbre (2)
Hey guys, I am using [clojurewerkz/machine_head] latest version to connect to an MQTT broker, everything works as expected, but then I get a really weird issue. In a regular deployment, this code works fine (by regular deployment I mean running the application):
(let [conns (into {} (mapv #(vector % (mh/connect mqtt-sink-address mqtt-sink-conn-optns)) (range threads)))] ....)
Although when I am running tests it complains about the arity of mh/connect, saying that it can't use 2 arguments, and it expects only 1. The weird part is that if I run the function call inside the debugger, I get the same error, but if I run it in a regular REPL, it works 🤯 Any ideas?Hmmmm how could that happen? I don't really see how that could occur
Using different aliases in different environments. Or relying on snapshot dependencies. Or something else.
Not the case here. I even checked if the dependencies weren't being overwritten by the test profile or dev. But I don't have anything in place for that
To be completely sure, you can compare (System/getProperty "java.class.path")
.
Also, something can potentially override the function at run time.
`/users/iceman/.m2/repository/clojurewerkz/machine_head/1.0.0/machine_head-1.0.0.jar` is in the classpath
that is what I am expecting
also (-> mh/connect class .getDeclaredMethods first .getParameterTypes alength)
returns 1 during runtime (debug) and 2 in the repl 🥲
Is there a chance your debugging runtime has some classes
on its classpath that has outdated compiled stuff? Or maybe it has something else on the classpath that overrides that particular function in that namespace.
forget it… I am just flabbergasted at my stupidity right now, sorry to have wasted your time
(with-redefs [mh/connect (fn [uri] uri)
nuff said 😄
Well, there you go. As I mentioned, "something can override the function at run time". :)
Yes you were right… Well, I guess it is not a very good idea do work when tired 😄
I'm having this weird issue that only occurs in the uberjar and not during development in the stripe-java library:
[{:type java.lang.NoSuchMethodError
:message "'com.google.gson.GsonBuilder com.google.gson.GsonBuilder.addReflectionAccessFilter(com.google.gson.ReflectionAccessFilter)'"
:at [com.stripe.net.ApiResource createGson "ApiResource.java" 41]}]
:trace
[[com.stripe.net.ApiResource createGson "ApiResource.java" 41]
[com.stripe.net.ApiResource <clinit> "ApiResource.java" 26]
[com.stripe.net.Webhook constructEvent "Webhook.java" 49]
[com.stripe.net.Webhook constructEvent "Webhook.java" 30]
I build with the following build.clj
(ns build
(:require [clojure.tools.build.api :as b]))
(def lib 'brian/api)
(def version (format "1.2.%s" (b/git-count-revs nil)))
(def class-dir "target/classes")
(def basis (b/create-basis {:project "deps.edn"}))
(def uber-file (format "target/uberjar/brian-api.jar"))
(defn clean [_]
(b/delete {:path "target"}))
(defn uber [_]
(clean nil)
(b/copy-dir {:src-dirs ["src/main" "resources"]
:target-dir class-dir})
(b/compile-clj {:basis basis
:src-dirs ["src/main"]
:class-dir class-dir})
(b/uber {:class-dir class-dir
:uber-file uber-file
:basis basis
:main 'brian.server-components.http-server}))
and just "clojure -T:build uber"I tried pinning com.google.code.gson/gson 2.9.0
because it's the version linked in the readme of stripe-java
it gets a different error:
[{:type java.lang.NoClassDefFoundError
:message "com/google/gson/ReflectionAccessFilter"
:at [com.stripe.net.Webhook constructEvent "Webhook.java" 49]}
{:type java.lang.ClassNotFoundException
:message "com.google.gson.ReflectionAccessFilter"
:at [jdk.internal.loader.BuiltinClassLoader loadClass nil -1]}]
this time the error happens both in dev and ubjerjar
the original error happens as well when pinning the version of gson that stripe-java expects based on what I see in deps tree
anyway I was just curious if anyone had an idea on how to resolve that kind of issue, for now I just ditched the whole stripe-java library since I was only using it to validate the signature. So if anyone finds this thread looking for an answer regarding this error I have not found one yet. But you can easily replace this lib if you use it to validate a signature with the following snippet: [buddy.core.codecs :as codecs] [buddy.core.mac :as mac]
(defn compute-hmac-sha256 [secret message]
(-> message
(mac/hash {:key secret :alg :hmac+sha256})
codecs/bytes->hex))
(defn valid-signature? [stripe-webhook-secret payload stripe-signature]
(let [{:strs [v1 t]} (apply hash-map
(mapcat #(str/split % #"=")
(str/split stripe-signature #",")))
signed-payload (str t "." payload)]
(= v1 (compute-hmac-sha256 stripe-webhook-secret signed-payload))))
(defn process-webhook-event
"May throw errors that should be caught by the http stack."
(beware you should also compare t to the current timestamp if you want to prevent replay attacks)
you should check and make sure the same version of google's gson library is used in the repl and in your uberjar
you might also check that you rare using the same java version when you try and the uberjar and at the repl
@U03K8V573EC I would start the jvm with -verbose:class
to see how it load classes, the order and where they are coming from (what jar and version) in both cases (specially the one that works and the one that fails). Also do a jar -tf target/uberjar/brian-api.jar
to check the class is there on the uberjar and go from there
Very strange things can happen (along these very lines) if 2+ jars on the classpath contribute distinct versions of "the same" library or portions thereof.
@U0HG4EHMH that might just be the explanation yeah, I'll run the experiment when I have more time
Any ideas what might be the cause of this?
:cause clojure.core$eval193$fn__194
:via
[{:type java.lang.ClassNotFoundException
:message clojure.core$eval193$fn__194
:at [jdk.internal.loader.BuiltinClassLoader loadClass BuiltinClassLoader.java 641]}]
:trace
I am serializing a clojure map that contains serializable instances of java objects (regular classes) it serializes fine, but when I try to deserialize, I get that error. Any ideas?the serialized data contains references to a class created at runtime by clojure (as a result of loading clojure code and compiling it to jvm bytecode)
that class is available through one of clojure's dynamicclassloaders, but is not available to the classloader that is being used while deserializing
is there a way to find out which class it is?
I have looked through the structure that gets saved to the file, and there's nothing unknown in there, that's why I don't get how there's a reference to something generated in runtime
and this has worked before, it stopped working recently
I mean is the same process serializing and deserializing, or different processes(either across time or space)?
it is the same jvm, let's say I serialize on termination
and deserialize on startup
this has worked before
same executable code
everything is the same, as I said, same jdk, same clojure, same executable
I run the executable, when I stop it, it saves, and when I run it again, it deserializes the data
or tries to
the class name clojure.core$eval193$fn__194 corresponds to an anonymous function eval'ed while the value of *ns*
(the current namespace) is clojure.core
that is kind of odd, since clojure is aot compiled, but you can generate something similar like:
% clj
Clojure 1.11.1
user=> (in-ns 'clojure.core)
#object[clojure.lang.Namespace 0x1e392345 "clojure.core"]
clojure.core=> (fn [])
#object[clojure.core$eval138$fn__139 0x7c9b78e3 "clojure.core$eval138$fn__139@7c9b78e3"]
clojure.core=> (class *1)
clojure.core$eval138$fn__139
clojure.core=>
%
makes sense
also depending on how you are launching your code, it is possible *ns*
just remains at its default value of clojure.core, in which cause any usuage of say clojure.core/eval would result in classes like that
how would one avoid that issue? or at least minimize it?
in certain circumstances it will generate strongly named classes (classes whose name will stay the same across different compilations) but that is the exception rather than the rule
I found the issue, and it is related with an anonymous function
FWIW, no version of Clojure that I can find has core$eval193
in it so that seems like something created "on the fly" once Clojure is up and running?
is there a way to let's say, name the anonymous function on the fly, so that it doesn't end up with a name like clojure.core$eval193$fn__194?
(fn somename [..] ..)
you mean?
but some part of it will be a number that will be effected by how many times nextId is called in the compiler
basically the whole point of anonymous function is it has no name, but it is compiled to a jvm class, which needs a name, so the compiler generates one as to avoid colliding with other generated names
I am dynamically loading a configuration from an edn file, that can contain anonymous functions or point to specific functions that are already defined. But some of these might end up in the final serialized file. So is there a way to receive an anonymous function but name it when evaluated?
if I used predefined functions, I can deserialize the file no issues
I was using nippy
but I still get the same issue, because it needs to use the Serializable interface
I found a possible solution:
(defn gen-fn
[n as b]
(let [n (symbol n)
as (vec (map symbol as))
fn-value (eval `(fn ~n ~as ~b))]
(intern *ns* n fn-value)))
the file is not read with eval
it is read with aero
but there are parts that need to be evaled
thanks for the input
as long as you are using eval you are going to get generated class names, and those generated class names are non-deterministic, so mixing them with java serialization will always be fragile, and you'll hit things like moving code around (changing the order of eval) might break deserialization, changing clojure versions might break it) and changing jvm versions might break it
I am aware of that, but all of that would break even if I wasn't using Java Serialization. Since there are no guarantees of same serialization/deserialization between jdk versions
if you bound *ns*
around the call to eval and maybe eval'ed deftypes instead of fns, you would have better control over the names which depending on how you create a name for each thing, might be less fragile
yeah, makes sense
thanks for all your help
Are there any map-like data structures that allow you to associate values with a given range of numeric keys?
E.g. if :foo
is associated with (2,35) and I call get
with 16.7 on my "range map", I get back :foo
but if I call get
with 1.2 I get nil
?
https://github.com/clojure/data.int-map
I guess I could just enumerate the key space using data.int-map
and round any decimal keys, but I'm also curious if there's a name for this kind of thing
So is the intention that keys are specified with ranges when inserted and with exact values when looked up?
https://github.com/dco-dev/interval-tree And as soon as I knew what to google, it is there! Thanks @U05092LD5
nice!
https://docs.oracle.com/javase/8/docs/api/java/util/NavigableMap.html would probably work. (Two implementing classes.) It keeps keys in a sorted order.
• If there are no gaps, you could just use the end of the interval with floorEntry()
.
• If there are known gaps, you could use a placeholder value.
• If there are no overlaps, you could store both ends and make sure your key is between them by using both floorEntry()
and higherEntry()
.
• If there are overlaps, this might be the wrong data structure, but I'm not sure how you'd want it to work.
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentSkipListMap.html, https://docs.oracle.com/javase/8/docs/api/java/util/TreeMap.html.
Hello, is there a way to use a http repo with tools.deps? I am asking because I have build environment where clojars is mirrored behind in a private repository which do not provide https.
Excellent ! I was really dreading starting discussing with my IT department about configuring https
Does anyone know why
clojure.core/*clojure-version*
is dynamic? Just out of curiosityLooking at the commit history, the name predates the warning issued when an earmuffed var is not marked dynamic. I think this might have been done to avoid a breaking change. But perhaps others here know more
(ie, wanting to preserve the name with earmuffs before that meant it was dynamic. and it was marked dynamic to not print a warning on startup each time)
Thank you! Yes, I had a similar suspicion about the earmuffs and being marked dynamic
What web server is everyone using for maximum performance nowadays? I was about to open a spike repo using https://github.com/AppsFlyer/donkey but it's recently been marked as unsupported.
we use jetty with ring on top. recently switched to
info.sunng/ring-jetty9-adapter {:mvn/version "0.18.5"}
which actually wraps jetty 11 despite its namehttps://github.com/ring-clojure/ring/issues/439 is an issue to update the “regular” ring lib off of jetty 9
We use the Sun Ng Jetty adapter in production and we're very happy with it. http-kit is another option but we found support for observability was very poor with New Relic which made us switch back to Jetty.
Food for thought: hazy memories led to this https://www.slideshare.net/metosin/naked-performance-with-clojure. It doesn't look like experimental repos they were playing with got any further, not that I'm complaining. Metosin has given me so much already.
I'm moving to a spicedb-based permission system, which has all sorts of design ripple effects. I wanted to reexamine my priors just in case there had been advancements in the basics while my attention was elsewhere
Well, do you need to maximize throughput or response time? Do you have any soft upper bounds for response time percentiles at different request rates?
Throughput, but (and this is a larger topic than the choice of http component), by removing permissions from the db schema, I am starting to treat the db like an object store, so the datomic-style client-side cache starts to make sense. I'm going to play with that idea next, but on a Jetty server 🙂
I've seen all big Clojure server options handle large loads, so I doubt any choice will have adverse effects.
Why do let
and family use a vector for bindings instead of a map?
duh, thanks!
so to handle that you’d have to either do some heavy analysis on dependency, or just use an ordered structure
(also, lisps traditionally use lists and Clojure (tastefully) uses a similar but distinct [
for binding pairs)
Yup, and did away with the distinction between let
and let*
entirely. Thanks again
I guess the key of the may would be a map or vector. map in that case seems to lose a bit of value
oh. the fatal flaw.
(let [x 1
x (+ x x)]
x)
2
that binding shadows x
. with a map, you’ve got duplicate keys. I suppose all of this can be solved if you make some new construct that is ordered, but that point you’re just talking about {
having different semantics and not being a map. You’re just asking for a new token.
And that gets super weird if you wanted to emit a let
from a macro. You’d have to somehow say “this is the special {
, not a map” when emitting a macro. Right now you just emit a vector.