I have a database connection object which I’m using as a component, which I can then pass as an argument to database functions, e.g. (db/create conn-component my-thing)
. This works well.
The problem I’m running into is that whenever the connection is interrupted (database unreachable, network problem, etc.), using a database function will result in an exception.
In this situation, the old database connection object becomes useless and I need a new one. How can I use component to solve or avoid this problem?
don't use a database connection as a component
use a connection pool or something you can get a connection from
What you can do is implement a defrecord that participates in the pseudo-protocol that clojure.java.jdbc expects from its db-conn argument
E.g.:
(defrecord DatabasePool [db datasource]
component/Lifecycle
(start [this]
(log/info :msg "Starting database pool")
(let [{:keys [dbtype host port dbname user password]} db]
(when-not (= "postgresql" dbtype)
(throw (ex-info "Unsupported dbtype" {:db db
:dbtype dbtype})))
(let [options (assoc default-database-pool-options
:server-name host
:port-number port
:database-name dbname
:username user
:password password)]
(try
(let [datasource (with-retries* #(hikari/make-datasource options))]
(assoc this :datasource datasource))
(catch Exception e
(log/error :msg "Error creating database pool"
:exception e)
(throw (ex-info "Invalid database pool" {:db db} e)))))))
(stop [this]
(log/info :msg "Stopping database pool")
(when datasource
(hikari/close-datasource datasource))
(assoc this :datasource nil)))
the thing to do, independent of component, is use a connection pool