This page is not created by, affiliated with, or supported by Slack Technologies, Inc.
2022-10-20
Channels
- # announcements (10)
- # aws (4)
- # babashka (71)
- # beginners (30)
- # calva (61)
- # cherry (1)
- # cider (16)
- # clj-kondo (3)
- # clj-on-windows (4)
- # cljsrn (1)
- # clojure (28)
- # clojure-austin (2)
- # clojure-bay-area (1)
- # clojure-europe (45)
- # clojure-hungary (1)
- # clojure-nl (1)
- # clojure-norway (26)
- # clojure-sweden (14)
- # clojure-uk (11)
- # clojurescript (39)
- # core-async (3)
- # core-typed (11)
- # datomic (68)
- # fulcro (7)
- # keechma (1)
- # lsp (29)
- # malli (5)
- # off-topic (57)
- # other-languages (13)
- # pathom (4)
- # rdf (7)
- # reagent (7)
- # reitit (6)
- # releases (1)
- # schema (8)
- # shadow-cljs (86)
- # sql (22)
- # squint (1)
- # vim (8)
- # xtdb (12)
Hey team! Noob question:
(declare pool)
// ...
(defn start []
(def pool (connection/->pool HikariDataSource
(config/get-db-config)))
(.close (next-jdbc/get-connection pool)))
(defn stop []
(.close pool))
(defn restart []
(stop)
(start))
(next-jdbc/select pool ["SELECT * ..."])
I am trying to set up a next-jdbc with a connection pool. ^ This was my approach. I avoided component / mount; I don’t think we really need it yet.
Question for you: Is there some subtle bug I am missing, or is what I wrote fine?
I saw some people define pool
as a dynamic variable. I assume this is so they can redefine pool for transactions. But I don’t need this, as every function that talks to the db accepts conn
as an argument.Don't use def
inside defn
.
If you really want a global Var (which I think is a bad idea), you can do this without the weird def
-in`-defn` thing:
(def pool nil)
(defn start []
(alter-var-root #'pool (constantly (connection/->pool ..)))
(.close (next-jdbc/get-connection pool)))
I assume you're doing that last line to warm up the pool, at startup, rather than letting it do so lazily?(and the fact that the idiomatic way to modify pool
's value is so ugly should be a hint that global Vars are intentionally unpleasant to work with)
And then I have a global Var just for use with the REPL (and never reference it anywhere else).
Either like this https://github.com/seancorfield/usermanager-example/blob/develop/src/usermanager/main.clj#L227 or as a plain Var with alter-var-root
(per above).
alter-var-root
takes a function to apply. So constantly
makes this like "assignment" rather than an update.
For pretty much everything I write, I have more than just a "thing" that needs initializing (e.g., connection pool, web server, caching), so I use Component pretty much from the get-go.
So I have an app
Component, that contains the db
connection pool, the server
, caches
, etc, and I pass app
through the call chain (or just parts of it as needed).
Then my -main
builds the "production" Component and starts it and uses alter-var-root
to put it in the REPL global Var (in case I need to REPL into the process while it is running on QA/production). And I have RCFs as needed that have code to build, start, and stop the "dev" Component, that deliberately does not update the global Var so I avoid the temptation to write code that depends on the global while working in dev mode.
Okay, noting to play with component and take a longer gander at usermanager-example. You rock Sean!
Example from work:
(comment
(def app "Admin App" (component/start (build-system 8081)))
(require '[next.jdbc :as jdbc])
(def ds (-> app :database :pooled-db :datasource))
(jdbc/execute! ds ["select * from user where siteid = 45"])
(jdbc/execute! ds ["select * from skywriting_admin"])
(jdbc/execute! ds ["show index from loguserphoto"])
(jdbc/execute! ds ["describe loguseragent"])
(component/stop app))
And the -main
for that app looks like this:
(defn -main
"Start Jetty and run the Admin Server app."
[& [port]]
(let [port (or port (get (System/getenv) "PORT" 8081))
port (cond-> port (string? port) Integer/parseInt)
running-system (component/start (build-system port))]
(alter-var-root #'admin-app/sys (constantly running-system))
(alter-var-root #'admin-app/metrics-request-name
(constantly "Admin Requests"))
(start-telemetry (-> running-system :application :worldsingles-application)
"admin" "Admin Server"
`admin-web/probe)
;; wait for web server to shutdown
(-> running-system :web-server :shutdown deref)))
Note the admin-app
aliased ns containing the REPL global Vars.(I edited the RCF a bit so the ->
is a little different than it actually is in the -main
but in reality they are the same)
Our Application
Component contains a Database
Component that holds multiple connection pools as c.j.j-compatible db-specs -- hence the :datasource
selection to get a next.jdbc
-compatible datasource.
(since our app uses both libraries -- c.j.j in old code and next.jdbc
in new code)
And build-system
includes the actual "web application" function that runs everything.