Mark Fisher
Mark Fisher

Reputation: 9886

Defining an agent lazily

I am creating an agent for writing changes back to a database (as discussed in the agent-based write-behind log in Clojure Programming).

This is working fine, but I am struggling to create the agent late. I don't want to create it as a def, as I don't want it created when my tests are running (I see the pool starting up when the tests load the forms even though I use with-redefs to set a test value).

The code I started with is (using c3p0 pooling):

(def dba (agent (pool/make-datasource-spec (u/load-config "db.edn"))))

I tried making the agent nil, and investigated how I could set it in the main of my application, when it's really needed. But there doesn't seem to be an equivalent reset! function as there is with an atom. And the following code also failed saying the agent wasn't in error so didn't need restarting:

(when (not @dba)
  (restart-agent dba (create-db-pool)))

So at the moment, I have an atom containing the agent, where I then do:

(def dba (atom nil))
;; ...
(defn init-db! []
  (when (not @dba)
    (log/info "Creating agent for pooled connection")
    (reset! dba (agent (create-db-pool))))

But the very fact I'm having to do @@dba to reference the contents of the agent (i.e. the pool) makes me think this is insane.

Is there a more obvious way of creating the pool agent lazily?

Upvotes: 3

Views: 55

Answers (1)

Arthur Ulfeldt
Arthur Ulfeldt

Reputation: 91617

delay is useful for cases like this. It causes the item to be created the first time it is read. so if your tests don't read it it will not be created.

user=> (def my-agent (delay (do (println "im making the agent now") (agent 0))))
#'user/my-agent
user=> 
user=> @my-agent
im making the agent now
#object[clojure.lang.Agent 0x2cd73ca1 {:status :ready, :val 0}]

Upvotes: 3

Related Questions