The Best Way to Manage Resource Pools in Clojure

I have a web service endpoint that uses a mutable resource from a Java library. This web service endpoint can receive multiple requests at the same time. (endpoint implemented using Ring / Compojure). Creating these resources is expensive, so re-creating them for every web service call is really inefficient.

What I want to do is create a pool this resource, which I populate when the web service starts. Then, every time an endpoint is called, it takes a resource from the pool, uses it to process it, and then returns it to the pool and waits for the next call.

I am wondering what would be the best way to do this in Clojure? Is there a Clojure pool library that can help me?

I naively tried to implement this using a vector in an atom, where each element of the vector is this resource. However, he quickly realized that this could not work like that.

+7
clojure pool
source share
4 answers

This is based on Timothy Pratley's> idea of ​​using links:

 (def pool (ref ['a 'b 'c])) (defn take' [pool] (dosync (let [[h & t] @pool] (ref-set pool (vec t)) h))) (defn put [pool x] (dosync (alter pool conj x) nil)) (take' pool) ;; => 'a (put pool 'a) ;; => nil (take' pool) ;; => 'a (take' pool) ;; => 'b (take' pool) ;; => 'c 

This may not be the best way to attack this. But I like the simplicity of this.

+3
source share

To implement the pool, you need to solve two problems:

  • Concurrency. Use locking https://clojuredocs.org/clojure.core/locking or ref instead of atom. Requests can be simultaneous, so you need to be careful that two consumers cannot get the same resource.

  • Release of resources. Consider using a template such as (with opening ...), that is, a macro that expands to try-finally, where the resource goes back to the open pool when you leave the block area.

You may want to indicate the status “error”, as well as “available” or “in use”, where the resource may need to be freed and recreated.

+2
source share

Take a look at this kul / pool . It uses the Apache Commons Pool. Hope this will be helpful.

+2
source share

You can also use atom:

 (def pool (atom ['c 'b 'a])) (defn take' [pool] (loop [] (let [p @pool] (if (compare-and-set! pool p (pop p)) (peek p) (recur))))) (defn put [pool x] (swap! pool conj x) nil) (take' pool) ;; => 'a (put pool 'a) ;; => nil (take' pool) ;; => 'a (take' pool) ;; => 'b (take' pool) ;; => 'c 
0
source share

All Articles