This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2023-01-04
Channels
- # announcements (8)
- # babashka (78)
- # beginners (15)
- # calva (6)
- # cider (12)
- # clerk (1)
- # clojure (46)
- # clojure-dev (13)
- # clojure-europe (15)
- # clojure-norway (5)
- # clojure-portugal (1)
- # clojure-uk (1)
- # clojurescript (23)
- # clr (29)
- # conjure (4)
- # core-async (10)
- # cryogen (1)
- # data-science (8)
- # hoplon (4)
- # hyperfiddle (11)
- # introduce-yourself (3)
- # jobs (6)
- # kaocha (12)
- # lsp (11)
- # malli (8)
- # membrane (11)
- # releases (1)
- # shadow-cljs (20)
- # spacemacs (47)
- # tools-deps (1)
how do you do REPL driven development with Integrant? we use mount currently and we can just (mount/start) and then use the vars in the REPL. In integrant, you have to pass the system into the function, so how do you pass in the system? Do you have to spin up a new system for each REPL query, or use a global system var? In which case that feels just like using mount?
And you can store systems however you like, and then use the right ones in the right context.
Ah, those namespaces aren't built into Integrant itself, they're in a sister project that Integrant's README mentions: https://github.com/weavejester/integrant-repl
Yeah, so the idea is that during dev you have a system saved somewhere easily accessible by the repl
Here's a fragment from a project I'm currently working on that shows how I do it:
(def config
{::conn {:schema chat-schema}
::clients {}
::jetty9/server {:host "0.0.0.0"
:port 3000
:join? false
:clients (ig/ref ::clients)
:conn (ig/ref ::conn)
:handler #'web/handler}})
(defonce ^:dynamic *system* nil)
(defn system [] *system*)
(defn start
([config]
(alter-var-root #'*system* (fn [s] (if-not s (ig/init config) s))))
([] (start config)))
(defn stop []
(alter-var-root #'*system* (fn [s] (when s (ig/halt! s) nil))))
(defn restart
([config] (stop) (start config))
([] (restart config)))
(comment
(start)
(stop)
(restart)
(system)
)
With the optional config arg you can have multiple configs in your project and call restart on any of them.And if you want to use certain elements of your system in the REPL (e.g. the conn from above), you can do something like this in your REPL:
(let [conn (::conn (system))]
@conn)
(let [clients (::clients (system))]
clients)
(let [conn (::conn (system))]
(d/entity @conn [:username "C"]))
Also, if you var quote your top level handler (such as the web handler from above) you don't need to bounce the system if you are working on handler logic.
{:env/variables {:version (app-version)
:k_revision (System/getenv "K_REVISION")}
:db/postgresql {:uri (System/getenv "postgresql")}
:data-providers/binance {:auth {:api-key (System/getenv "binance_api_key")
:secret-key (System/getenv "binance_secret_key")}}
:logs/google-json-payload {:level (keyword (System/getenv "log_level"))
:is-loggable? (fn [{:keys [^String logger-name ^Level level]}]
(let [level-int (.intValue level)
log? (fn [{:keys [logger level]}]
(and (.startsWith logger-name logger)
(< level-int (google-json-payload/JUL-levels->int level))))]
(not
(some log?
[{:logger "org.postgresql" :level :config}
{:logger "jdk.event.security" :level :config}
{:logger "okhttp3.internal" :level :config}
{:logger "sun.net.www.protocol.http.HttpURLConnection" :level :config}]))))
:pretty? (nil? (System/getenv "K_REVISION"))}
}
I have a config which get values from env variables or from other place. The config is the same code. But values changes depends on environment.Is there a specific place to take memory leak questions? I have a long-running Clojure2D-based project. After about a day on an rPi it crashes with an OOM. Looking at the project on my desktkop, it looks like one of the send-off threads just keeps growing.
What tool have you used so far to monitor the process? Also, there's `-XX:+HeapDumpOnOutOfMemoryError` that you can use to create a heap dump on OOM and then investigate it separately in VisualVM or something similar
I have a task running every 10 seconds, and the send-off thread is active and growing on that schedule. So that seems to be the likely culprit.
(defn periodically [func millis]
(let [p (promise)]
(future
(while
(= (deref p millis :timeout) :timeout)
(func)))
#(deliver p :cancel)))
a good way to investigate memory leaks is to take multiple heap dumps over time and then focus on the objects that exist in consecutive dumps that you would have expected to get garbage collected instead. once you identify those you can work backwards to figure out what's hanging onto the references and why
Yeah either func or something accumulating in thread local on the future thread
I'm using visualvm right now, but it's just a bunch of Object or Byte. I don't see how to match those to the Clojure data structures I have.
MAT is slightly better at making sense of heap dumps https://www.eclipse.org/mat/
I tried MAT but still don't get it. I see a bunch of vars and agents, but still have no idea which one is which. Even worse, the reports don't work.
@U06BE1L6T which profile are you talking about?
I mean the heap dump. Maybe you can at least share a screenshot Ideally, try to create a minimal public reproducer - then it would be easier for people to help you
Sadly, not so much. You can try exploring the reports generated by MAT, they are interactive, or if you can, share the heap dump
If that's not possible, I can try to hop on a call tomorrow to help you analyze it if you'd like, but it's mostly heuristics, unless you want to start writing weird queries on the heap in some abomination language
I can't get MAT to generate any reports. It pops up a "save this index.html" dialogue, but I never get a report.