Reloading code on a production ring-clojure server - clojure

What's the best way to push new code to a production ring server without restarting the whole JVM?
Currently I use wrap-reload in production, but this doesn't quite work for me because sometimes I want to run commands in the repl (doing database migrations for example) before ring starts handling requests with the new code. Also various blogs and tutorials out there say not to use wrap-reload in production, though I don't understand why not.
I have come up with the following solution, but I confess I don't have a deep understanding of what's going on under the hood. I was wondering if I could get a sanity check by someone who does. Does this technique seem reasonable?
The idea is to have a path (/admin/reload-clj) that causes all the clojure code to be reloaded.
(defonce ^:dynamic *jetty*)
(declare reload-clj)
(defn app [req]
...
(when (= (req :uri) "/admin/reload-clj") (reload-clj req))
...)
(defn start-jetty []
(let [j (run-jetty app {:port (http-port) :join? false :max-threads 16})]
(dosync (ref-set *jetty* j))
j))
(defn reload-clj [req]
(future
(log/info "Reloading clojure code...")
(require '(whrusrv admin main utils wdb) :reload-all)
(.stop #*jetty*)
(start-jetty)
(log/info "Clojure reload success!"))
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Reloading..."})
(defn -main [& args]
(start-jetty))

The code you have will work, though you should be aware that :reload-all only loads a namespace and that namespaces dependencies. It does not recursively load dependencies of those namespaces.
I should add that reloading in this way is strongly not reccomended in a production system.
Newly deployed code might have errors that aren't apparent until the system is restarted (e.g, they depend on a var that is still defined from the running system but whose declaration was removed). The system will work fine but then fail on restart.
Loading code can also have side effects which could screw up your production environment. Although its good style to avoid these, the only way to truly be sure something unexpected won't happen is to do a JVM restart.
The best way to do a zero-downtime deploy on the JVM is a rolling deploy using a load balancer.

I had an intranet web service in common lisp at a previous job, so I don't know if that qualifies as production. Also, being in CL my answer is neither ring nor clojure specific. Still, it's useful for reloading code.
What I did is to start a swank server (part of slime) on the production lisp instance and connect to it even remotely from my desktop. That way I could write new code, rewrite code, debug, reload, etc. Obviously you'll have to be extra carefull in a real production system.
You can do similarly in clojure, you even have alternatives to swank, like nrepl.
You'll have to take security into account as well and allow only local connections, or whatever works for you.

Related

How can I stop jetty server without restarting repl

Let's say I have a trivial boot-clj task that uses '[pandeiro.boot-http :refer [serve]]. If I make changes and need to restart the task it would complain that the port is already in use. Is there a way to kill jetty server without having to restart repl?
If you want only this many people use this pattern:
(defonce running-server (atom nil))
(defn stop-server [] (#running-server))
(defn go []
(reset! running-server
(org.httpkit.server/run-server
... your stuff here ...))
If this is a longer term project starting with something like the component framework solves this problem in a much more elegant way and allows for many dependent components (hence the name). This is a good place to get started and worth reading before starting your next project.
pandeiro.boot-http supports reloading of your handler so you don't have to restart the serve task. You need to configure it with :reload set to true:
On command line:
boot serve -H myapp.server/app -R wait
Or in your boot script:
(boot (serve :handler 'myapp.server/app :reload true) (wait))
For regular static files serve always returns the current version from the disk so there is no need for reloading for them.
If you really need to restart the task I guess you might be affected by issue in pandeiro.boot-http and might need to contact its maintainer.

How to do integration testing for clojure/ring/selenium/leiningen?

I have a clojure/ring web application which I want to test with clj-webdriver. Is there a simple way to run the ring webserver and then run the tests such that they target the ring instance?
I'm thinking of a usage something like:
$ lein with-ring test
I have two ideas so far:
write a custom higher order leiningen plugin. Seems too complicated. Does a similar plugin already exist?
write a Makefile rule which starts ring, runs lein test, find&kill the ring process - too complicated/hacky
Is there a simpler way?
If you're using clojure.test you can call use-fixtures to start/stop your server from within the tests, e.g.:
(use-fixtures
:once
(fn [f]
(let [server (ring.adapter.jetty/run-jetty
#'your-app
{:port 1234 :join? false})]
(try
(f)
(finally
(.stop server))))))
Just adjust start/stop logic to your preferred webserver (jetty in this example; http-kit would be very similar). If you replace :once with :each you'll even have a fresh server instance for each single test.

Sharing an atom between Clojure and Clojurescript?

Assume I have a Clojurescript namespace called main with a atom within it called state.
(ns main)
(atom state nil)
I compile my Clojurescript app, run it on a server, fire up the Clojurescript repl and then connect to my server using a browser. Everything works dandy.
In the Clojurescript repl, I can comfirm the usual
> (+ 1 1)
2
> (js/alert "Hey there") //shows an alert dialog with "Hey there" in the browser
nil
> main.state
(atom nil)
The Clojurescript repl is great for development. So, clearly I can get and (using swap! or reset!) set the value of a Clojurescript atom from a Clojure application. I was wondering if there was a way to have a connection between the value of an atom in my Clojurescript project and a running Clojure application. Perhaps the Clojurescript client connects to a specified port and sends the result to some Clojure server waiting on that port. Simply stated, I was wondering if it might be possible to have a running server application share the value of the state client atom.
Why, you might ask? Well, I was thinking it would be nice to write the value of the state atom to a actual file (state.clj) whenever state is modified in the running Clojurescript application. This way, I could always have a view of the current value of state. I could use something like emacs (global-auto-revert-mode t) to make sure that state.clj buffer was always fairly recent. This is a little like having a debugger.
Beyond that, my real desire is to then make it so that the running Clojure application would also periodically poll state.clj itself. When the server detects that I modified state.clj, it would accept the modification as the new value of the Clojurescript state atom. It would then do something like what the Clojurescript repl does, illustrated in the following pseudo code:
(send-to-client-for-evaluation
(compile-into-js
(reset!
main.state
the-read-string-value-of-the-content-of-state.clj)))
Basically, I want the ability for the server to have something akin to a shared atom between the client and the server. I want the value of state to be bidirectionally shared between the client and the server. Is any of this possible, or am I just dreaming?
Sure, just make requests to a rest API sending data in EDN format, and voila! If you want to avoid polling, consider running your Jetty or Tomcat behind an Nginx instance to make use of the Nginx HTTP Push Module. I have a somewhat cobwebby example here (this was pre-ClojureScript, so I wrote the client in plain JS). And there is also gifsockets.

How to hot reload a namespace on file save in Leiningen REPL

When using the leiningen REPL, is there a way to make a file or ns automatically reload in the repl on file save. Currently I reload the ns by typing the following in the repl - (use 'sample.ns :reload-all).
However can I have it reload automatically on file save ?
You can easily reuse the code from duct framework.
You will need only hawk files watcher.
Here is how it can be looks like:
(defn- clojure-file? [_ {:keys [file]}]
(re-matches #"[^.].*(\.clj|\.edn)$" (.getName file)))
(defn- auto-reset-handler [ctx event]
(binding [*ns* *ns*]
(clojure.tools.namespace.repl/refresh)
ctx))
(defn auto-reset
"Automatically reset the system when a Clojure or edn file is changed in
`src` or `resources`."
[]
(hawk.core/watch! [{:paths ["src/" "resources/" "dev/src/" "dev/resources/"]
:filter clojure-file?
:handler auto-reset-handler}]))
Clojure-Watch library does what you need. It observes a file and performs some action. In your case, an action would be to reload a namespace from that file. Also, it requires to write some initial code to launch the observer.
This way seems a bit complicated to me. Plain REPL launched directly from Lein is not effective way to develop. You better to use some Clojure-friendly editor like Emacs or Lightable.
Most major editors support custom hotkey bindings and have a Clojure plugin that allows you to connect to the active REPL over the network (via "nREPL"). Personally, I use vim and therefore use vim-fireplace for this purpose.
This means you can have a custom hotkey for reloading whatever file you're editing as you edit it. From there, it's typically trivial to add a custom on-save hook that does the reloading.

Where's run-server gone in compojure?

I used to be able to start a web server in compojure like this:
(run-server {:port 8080} "/*" (servlet my-app))
Does anyone know where this function has gone in the latest compojure? (0.6.2)
The docs say I'm supposed to run it from the command line and use some freaky auto-reloading thing, at which point I might as well be using python.
You're looking at some seriously out-dated documentation.
For jetty, use
(use 'ring.adapter.jetty)
(defn start-web []
(run-jetty (var my-site) {:port 8080 :join? false}))
Where my-site is your top-level handler function.
You can call that function anywhere, including from the REPL in SLIME. Recompiling/redefining my-site will work on a running server, so there's no need for auto-reloading if you're already using an interactive environment.
EDIT: compojure has been split into ring & clout, with compojure itself remaining as a small selection of higher-level abstractions on top. Most of the actual server stuff and design documentation is now in ring. See https://github.com/mmcgrana/ring/wiki