mount
mount copied to clipboard
A reset function which will reset all the states even if there are errors.
Situation I ran into:
This is interesting.
I defined a var with a defstate
but made a mistake the first time around.
;; not the same mistake but it will help elaborate what I’m trying to explain
(def conn1-atom (atom nil))
(defstate conn
:start (reset! conn1-atom :started)
:stop (reset! :started conn1-atom)) ;; there is a mistake in :stop.
(mount/start) ;; this works fine. (mount/stop) ;; produces the error.
ClassCastException clojure.lang.Keyword cannot be cast to clojure.lang.IAtom clojure.core/reset! (core.clj:2273)
;; trying to re-define conn
but keep running into the same error.
(defstate conn
:start (reset! conn1-atom :started)
:stop (reset! conn1-atom :stopped))
A function to reset all the states/connections even if they produce error would be nice.
This was mentioned on Slack's mount channel by @tolitius .
"it might make sense to potentially have a some kind of (mount/reset)
that could take params: i.e. (mount/reset :ignore-errors true)
. which, if well documented, could help in situations like this one."
To summarize the discussion with @tolitius:
-
mount/reset
makes sense as a dev-time tool. Thereset
would stop all started states unconditionally and clear the internal Mount state.
The only open question is how to deal with states which fail to stop cleanly:
- we don't want to change the semantics of
stop
which stops the STOP operation on the first exception - one option is for
reset
to return a set of Vars which failed to stop cleanly so that e.g. the dangling connection can still be forced closed
After thinking a bit more a separate API such as reset
is unnecessary, a call to (mount/stop)
should handle this use case.
Pushed changes to 0.1.12-SNAPSHOT
. A call to (mount/stop)
on failure to stop a state will report it and mark the state as :stopped
. This would allow to:
- stop all the subsequent states
- manually handle states that could not stop in REPL
- redefine states "stop" functions in the same REPL session if needed
(mount/stop)
will return all states that were successfully stopped.
boot.user=> (defstate a :start 42 :stop (throw (RuntimeException. "BOOM")))
#'boot.user/a
boot.user=> (defstate b :start 1 :stop 2)
#'boot.user/b
boot.user=> (mount/start)
{:started ["#'boot.user/a" "#'boot.user/b"]}
boot.user=> a
42
boot.user=> b
1
boot.user=> (mount/stop)
#error {
:cause "BOOM"
:via
[{:type clojure.lang.ExceptionInfo
:message "could not stop [#'boot.user/a] due to"
...}]}
{:stopped ["#'boot.user/b"]}
Since a
state could not be stopped, it still points to its original value after the "stop" function was called:
boot.user=> a
42
boot.user=> b
#object[mount.core.NotStartedState 0x2156abe2 "'#'boot.user/b' is not started (to start all the states call mount/start)"]
However from mount's perspective a
is :stopped
and can be started / redefined again. This allows a manual "force stop" of a
in REPL.
We can now redefine a
's stop function to "fix" the exception:
boot.user=> (defstate a :start 42 :stop 34)
#'boot.user/a
boot.user=> (mount/start)
{:started ["#'boot.user/a" "#'boot.user/b"]}
boot.user=> a
42
boot.user=> b
1
boot.user=> (mount/stop)
{:stopped ["#'boot.user/b" "#'boot.user/a"]}
boot.user=> a
#object[mount.core.NotStartedState 0xa04c91c "'#'boot.user/a' is not started (to start all the states call mount/start)"]
boot.user=> b
#object[mount.core.NotStartedState 0x56b0c866 "'#'boot.user/b' is not started (to start all the states call mount/start)"]