Fork me on GitHub
#clojure
<
2023-12-27
>
hifumi12303:12:07

What solutions do people here use for metrics? I tried looking for Prometheus clients the other day but was left unsatisfied with my options (either due to being 7+ years out of date, or simply not having features I would like). It made me look for alternatives to prometheus. One key takeaway is that I really don’t understand the state of metrics in the JVM world. Is there some simple JVM-targetted solution that I am missing? (e.g. I’ve seen mentions of New Relic many times in this Slack, but I’ve never really looked into it).

delaguardo09:12:53

https://micrometer.io/ this one is a good option as a generic metric library with options how to export numbers to various frontends like prometheus

practicalli-johnny13:12:21

Take a look in #C010TGGL02X

onetom09:12:41

I have a problem with calling a public static final method on java.util.Date.

(java.util.Date/getMillisOf #inst "2000")
Syntax error (IllegalArgumentException) compiling . at (xxx.clj:211:3).
No matching method getMillisOf found taking 1 args for class java.util.Date

onetom09:12:54

(-> (java.util.Date.) clojure.reflect/reflect :members
      (->> (filter #(-> % :name (= 'getMillisOf)))))
=>
(#clojure.reflect.Method{:name getMillisOf,
                         :return-type long,
                         :declaring-class java.util.Date,
                         :parameter-types [java.util.Date],
                         :exception-types [],
                         :flags #{:static :final}})

onetom09:12:42

am I misunderstanding that a public final method can be called like that?

p-himik09:12:17

getMillisOf is not public.

p-himik09:12:00

If you don't care that normalize can be called on that inst, you can just call its getTime member fn.

onetom09:12:06

ah, it's just static, sorry, thanks!

👍 1
thheller10:12:19

why not just (.getTime #inst "2000")?

🙏 1
onetom10:12:17

because I had no experience with the JDK before learning Clojure 10 yrs ago and even since then I'm trying avoid learning the historical, deprecated classes, so I'm primarily using the java.time package.

onetom10:12:12

also, i just looked at the JDK sources and getMillisOf was the method, which was returning the fastTime instance variable, so it looked like just what I needed.

thheller10:12:03

definitely better to use the java.time things, much more useful

thheller10:12:38

Instant has a toEpochMilli fn, and things are generally convertible to Instant (just might require adding a timezone in case you are working with LocalDateTime or so)

onetom10:12:21

i just wanted to check something using d/as-of and wanted to add a single millisecond to #inst "2000", so i thought i would learn how to do that using the java.util.Date API, but i should have just put up with the verbosity, do the round-trip to Instant and don't disturb people here 🙂

p-himik10:12:54

What's d/as-of?

onetom10:12:02

unfortunately Datomic is still using java.util.Date...

andy.fingerhut12:12:18

I am looking for something like bit-shift-left' , and "prime" versions of bit-shift-left, bit-and, bit-or, bit-xor, etc., that give correct answers not only for long and shorter integer args, but also for BigInt args. Is there a library that includes functions like that somewhere? I will double-check to see if https://github.com/clojure/algo.generic can be made to do that, although I suspect not without writing multimethods for that purpose yourself.

Felipe15:12:31

wasteful, but maybe just always convert to bigint? (defn bit-shift-left' [x n] (.shiftLeft (java.math.BigInteger/valueOf x) n))

andy.fingerhut16:12:20

Yeah, I'm starting to write a small collection of functions that I want, which use java.math.BigInteger operations, but also accept long or clojure.lang.BigInt operands, so there are a few more cases to handle.

👍 1
itaied18:12:48

hey all, trying to wrap my head around atom and ref. I'm not sure I fully understand the use cases for both, and why we couldn't just go with atom. As I understand, refs are used for mutating multiple sources of data atomically, which is exactly what an atom does. So instead of have 2 refs, we could have an atom containing both data inputs. For example, instead of:

(def account1 (ref 100))
(def account2 (ref 150))

(dosync
  (alter account1 - 50)
  (alter account2 + 50))
we could have
(swap! (atom {:account1 100 :account2 150})
         (fn [{:keys [account1 account2]}] {:account1 (- account1 50 )
                                            :account2 (+ account2 50 )}))

seancorfield18:12:13

ref is required when you need coordinated updates across two separate sources. Think: database transactions where you are updating multiple tables. They are very rarely used in Clojure because most "atomic" data can be handled by an atom.

itaied18:12:51

can you give me an actual use case for ref? because I think that by using a compound atom you can overcome the necessity of having multiple refs. I'm trying to understand why clojure introduce this different entity that has a different API. switching between the two is not an easy refactor task

p-himik18:12:38

If you combine multiple values under the same atom, you can't un-combine them.

jpmonettas19:12:42

I guess you can almost always track all your mutable state with a single reference like an atom, but this is not always the best idea. First from a design POV. If you have state that is mostly independent, smashing it together in one atom is weird, and not always possible, like when you have different modules of the project tracking it's own state. For this you can create multiple atoms, but if you also want to be able to modify them in a transaction some times, refs are the thing. And there is also the perf aspect. If you smash everything together in the same atom where most of the time sub-state will evolve independently, then multiple threads will be unnecessarily competing for changing the same atom, which could make your CAS functions be re-run a lot.

Alex Shulgin19:12:26

Refs implement STM, and I think could potentially be sped up at hardware level (modern CPUs have some crazy capabilities in that regard). Hardware accelerated or not, STM allows for more concurrency, which is a bit of a stretch — one can do an incredible amount of updates on a single atom as well.

andy.fingerhut21:12:44

Data split up across many ref's gives at least the possibility that many threads/CPU cores could achieve higher throughput with increased paralleism of processing, if the average "conflict rate" on indiivdual refs remains low. That is as compared to a single atom, where it is guaranteed that any two threads wanting to update its value at the same time will cause one of them to wait.

andy.fingerhut21:12:03

But there is definitely an additional burden on a developer to ensure that such parallel updates to distinct sets of ref's leads to no mangling of the consistentcy of the overall data being kept in them.

Nundrum21:12:55

I had to use ref recently to handle having one thread reading output from tcpdump and another thread pulling out that output for visualization purposes. Maybe someday I'll re-write it with core.async 😉

itaied05:12:58

thanks for all your replies. so I understand the use case of refs now better, and I want to change my question, what are the benefits of atom? when should I use atom over ref? If I want parallelism and transaction I should go for refs, but once again, refactoring an atom to ref may be expensive, so why not using a ref in the first place?

seancorfield05:12:38

I can only say that in nearly 13 years of production Clojure, and working with 140k lines of Clojure right now, I've never needed to use a ref. I use atom quite a lot. In the data and domain I work with, I don't tend to have in-memory coordinated mutation of multiple data sources. I have transactions for the relational database I work with because that sort of coordinated mutation across multiple tables crops up from time to time. I have in-memory coordinated mutation of individual data sources, but not for multiple data sources.

itaied05:12:34

I understand, but by design, why can't we transact on multiple atoms? why did clojure introduce a new state concept of ref just for this purpose?

seancorfield06:12:46

My understanding of the history is that Rich thought at the time that STM and refs would be much more widely useful than they've turned out to be in practice.

seancorfield06:12:09

From Rich's "History of Clojure" paper (my emphasis): 3.6.5 Summary. Taking on the design and implementation of an STM was a lot to add atop designing a programming language. In practice, the STM is rarely needed or used. It is quite common for Clojure programs to use only atoms for state, and even then only one or a handful of atoms in an entire program. But when a program needs coordinated state it really needs it, and without the STM I did not think Clojure would be fully practical.

seancorfield06:12:17

My advice is: user atoms and then if you have a situation where you discover you really need refs, refactor to use them. The reality is you'll probably never need them.

metal 1
oyakushev07:12:24

atom is just a wrapper over Java's AtomicReference , so it was already there, and is conceptually very simple. STM involves refs but also works correctly with agents, it is quite complicated and, like many have said above, almost never used. You can safely forget about the existence of ref s and you'll be fine.

metal 1