Pods

Eli Bierman edited this page Aug 25, 2015 · 67 revisions
Clone this wiki locally

Pods are only relevant to you if you want to extend Boot with your own tasks. If you're just getting started with Boot you probably don't need to worry about them.

Boot's primary purpose is to bootstrap Clojure applications. This involves bringing up the JVM, starting a Clojure runtime, and fetching and loading JAR dependencies from Maven repositories.

Automatic resolution of dependencies is great, but you can easily end up in dependency hell if you aren't careful. Boot provides a mechanism by which dependencies can be isolated from each other: we call this technique pods.

Overview

Pods are Clojure runtimes loaded from separate class loaders, built using the excellent shimdandy library. Pods are:

  • First-class – create anonymous pods, pass them to functions, etc.

  • Isolated – pods can be created with arbitrary dependencies and classpaths, and these do not affect the environment outside the pod.

  • Self-contained – pods are completely independent Clojure runtimes.

The primary avenue of attack when faced with dependency hell is isolation. Dependencies are separated into logically independent subsets that provide specific functionality. Pods are created and these dependencies loaded in them. Expressions can then be evaluated in these pods to do work or compute.

Note: pods are separate Clojure runtimes. Clojure objects created in one pod can't be used in another (each runtime has its own protocol implementations and class definitions which are not accessible from other runtimes).

Pods API

The pods API is defined in the boot.pod namespace. This namespace contains functions useful for managing dependencies and classpaths. It is available in any pod.

(make-pod env)
Creates a new pod with the given boot environment env and returns it.
(destroy-pod pod)

Prepares the pod so it can be collected as garbage by the JVM.

Note that this won't shut down threads running in the pod other than the Clojure agent pool, and running threads can prevent the pod from being reaped by the garbage collector.

(with-eval-in pod body*)

Evaluate the body expressions in the pod and return the result. The body expressions are templates.

The body and result must be able to be printed by pr-str and then read by read-string.

(with-call-in pod expr)

Evaluate the expr expression in the pod and return the result. The expr expression is a template. It must be a list whose first item is a namespace-qualified symbol (the function to call).

The expr and result must be able to be printed by pr-str and then read by read-string.

(.require pod ^String ns)
Does clojure.core/require of the namespace ns in the given pod.
(.invoke pod ^String f args*)

Invokes the function denoted by f (of the form "namespace/name") with the given args and returns the result.

The args and result must not be Clojure objects.

Pod Pool Service

Pods can take some time to warm up, so it's sometimes useful to maintain a pool of pods in reserve, ready for use.

(pod-pool env :size ... :init ... :destroy ...)
Creates a pool of pods such that there will always be size pods available. Returns a pool service object.
(pool-service)
Invoking a pool-service object with no arguments obtains a reference to the current active pod in the pool.
(pool-service :refresh)
Invoking a pool-service object with the :refresh argument destroys the current active pod and replaces it with another from the pool.
(pool-service :shutdown)
Invoking a pool-service object with the :shutdown argument destroys all pods in the pool and shuts down the service.

Creating Pods Based on Current Env

Pods are bootstrapped with the environment passed to the make-pod function. Usually this env will have different dependencies from the parent pod. However, most of the time you will want to pass the current Maven repo settings to the pod (eg. repositories, mirrors, etc.), and most of the work done in pods needs access to things on the classpath of the parent. Thus, in almost every case you want to do something like this:

(make-pod
  (update-in (get-env) [:dependencies]
             conj '[foo/bar "1.2.3"]))

rather than something like this:

(make-pod {:dependencies [[foo/bar "1.2.3"]]})