diff --git a/README.md b/README.md index ddb5c04..09373b8 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,12 @@ _**Alan J. Perlis** from [Structure and Interpretation of Computer Programs](htt - [Start and Stop Order](#start-and-stop-order) - [Start and Stop Parts of Application](#start-and-stop-parts-of-application) - [Start an Application Without Certain States](#start-an-application-without-certain-states) +- [Stop an Application Except Certain States](#stop-an-application-except-certain-states) - [Swapping Alternate Implementations](#swapping-alternate-implementations) +- [Suspending and Resuming](#suspending-and-resuming) + - [Suspendable Lifecycle](#suspendable-lifecycle) + - [Plugging into (reset)](#plugging-into-reset) + - [Suspendable Example Application](#suspendable-example-application) - [Mount and Develop!](#mount-and-develop) - [Running New York Stock Exchange](#running-new-york-stock-exchange) - [Web and Uberjar](#web-and-uberjar) @@ -252,6 +257,116 @@ is run after `start-with`, it rolls back to an original "state of states", i.e. Here is an [example](test/check/start_with_test.clj) test that starts an app with mocking Datomic connection and nREPL. +## Stop an Application Except Certain States + +Calling `(mount/stop)` will stop all the application states. In case everything needs to be stopped _besides certain ones_, it can be done with `(mount/stop-except)`. + +Here is an example of restarting the application without bringing down `#'app.www/nyse-app`: + +```clojure +dev=> (mount/start) +14:34:10.813 [nREPL-worker-0] INFO mount.core - >> starting.. app-config +14:34:10.814 [nREPL-worker-0] INFO mount.core - >> starting.. conn +14:34:10.814 [nREPL-worker-0] INFO app.db - creating a connection to datomic: datomic:mem://mount +14:34:10.838 [nREPL-worker-0] INFO mount.core - >> starting.. nyse-app +14:34:10.843 [nREPL-worker-0] DEBUG o.e.j.u.component.AbstractLifeCycle - STARTED SelectChannelConnector@0.0.0.0:4242 +14:34:10.843 [nREPL-worker-0] DEBUG o.e.j.u.component.AbstractLifeCycle - STARTED org.eclipse.jetty.server.Server@194f37af +14:34:10.844 [nREPL-worker-0] INFO mount.core - >> starting.. nrepl +:started + +dev=> (mount/stop-except #'app.www/nyse-app) +14:34:47.766 [nREPL-worker-0] INFO mount.core - << stopping.. nrepl +14:34:47.766 [nREPL-worker-0] INFO mount.core - << stopping.. conn +14:34:47.766 [nREPL-worker-0] INFO app.db - disconnecting from datomic:mem://mount +14:34:47.766 [nREPL-worker-0] INFO mount.core - << stopping.. app-config +:stopped +dev=> + +dev=> (mount/start) +14:34:58.673 [nREPL-worker-0] INFO mount.core - >> starting.. app-config +14:34:58.674 [nREPL-worker-0] INFO app.config - loading config from test/resources/config.edn +14:34:58.674 [nREPL-worker-0] INFO mount.core - >> starting.. conn +14:34:58.674 [nREPL-worker-0] INFO app.db - creating a connection to datomic: datomic:mem://mount +14:34:58.693 [nREPL-worker-0] INFO mount.core - >> starting.. nrepl +:started +``` + +Notice that the `nyse-app` is not started the second time (hence no more accidental `java.net.BindException: Address already in use`). It is already up and running. + +## Suspending and Resuming + +Besides starting and stopping states can also be suspended and resumed. While this is not needed most of the time, it does comes really handy _when_ this need is there. For example: + +* while working in REPL, you only want to truly restart a web server/queue listener/db connection _iff_ something changed, all other times `(mount/stop)` / `(mount/start)` or `(reset)` is called, these states should not be restarted. This might have to do with time to connect / bound ports / connection timeouts, etc.. + +* when taking an application out of rotation in a data center, and then phasing it back in, it might be handy to still keep it _up_, but suspend all the client / novelty facing components in between. + +and some other use cases. + +### Suspendable Lifecycle + +In additiong to `start` / `stop` functions, a state can also have `resume` and, if needed, `suspend` ones: + +```clojure +(defstate web-server :start (start-server ...) + :resume (resume-server ...) + :stop (stop-server ...)) + +``` + +`suspend` function is optional. Combining this with [(mount/stop-except)](#stop-an-application-except-certain-states), can result in an interesting restart behavior where everything is restared, but this `web-server` is _resumed_ instead (in this case `#'app.www/nyse-app` is an example of the above `web-server`): + +```clojure +dev=> (mount/stop-except #'app.www/nyse-app) +14:44:33.991 [nREPL-worker-1] INFO mount.core - << stopping.. nrepl +14:44:33.992 [nREPL-worker-1] INFO mount.core - << stopping.. conn +14:44:33.992 [nREPL-worker-1] INFO app.db - disconnecting from datomic:mem://mount +14:44:33.992 [nREPL-worker-1] INFO mount.core - << stopping.. app-config +:stopped +dev=> + +dev=> (mount/suspend) +14:44:52.467 [nREPL-worker-1] INFO mount.core - >> suspending.. nyse-app +:suspended +dev=> + +dev=> (mount/start) +14:45:00.297 [nREPL-worker-1] INFO mount.core - >> starting.. app-config +14:45:00.297 [nREPL-worker-1] INFO mount.core - >> starting.. conn +14:45:00.298 [nREPL-worker-1] INFO app.db - creating a connection to datomic: datomic:mem://mount +14:45:00.315 [nREPL-worker-1] INFO mount.core - >> resuming.. nyse-app +14:45:00.316 [nREPL-worker-1] INFO mount.core - >> starting.. nrepl +:started +``` + +Notice `>> resuming.. nyse-app`, which in [this case](https://github.com/tolitius/mount/blob/suspendable/test/app/www.clj#L32) just recreates Datomic schema vs. doing that _and_ starting the actual web server. + +### Plugging into (reset) + +In case `tools.namespace` is used, this lifecycle can be easily hooked up with `dev.clj`: + +```clojure +(defn start [] + (mount/start)) + +(defn stop [] + (mount/suspend) + (mount/stop-except #'app.www/nyse-app)) + +(defn reset [] + (stop) + (tn/refresh :after 'dev/start)) +``` + +### Suspendable Example Application + +An [example application](https://github.com/tolitius/mount/tree/suspendable/test/app) with a suspendable web server and `dev.clj` lives in the `suspendable` branch. You can clone mount and try it out: + +``` +$ git checkout suspendable +Switched to branch 'suspendable' +``` + ## Mount and Develop! `mount` comes with an example [app](https://github.com/tolitius/mount/tree/master/test/app)