@@ -102,18 +102,53 @@ In the post-assembly step:
102102
103103 - A ` bin/castle ` command is added, providing the commands that manage releases.
104104 The standard ` bin/<release> ` launcher that Mix generates is left untouched.
105- - The generated ` env.sh ` is extended with a hook. On the ** first** start of a
105+ - A ` bin/start ` is added, and it does nothing at all. ` release_handler `
106+ composes ` $ROOT/bin/start <data file> ` and installs it as ` heart ` 's temporary
107+ reboot command while preparing an emulator restart, and ` heart ` really does
108+ run it. A Castle release is restarted by its supervisor rather than by
109+ ` heart ` , so the one correct thing for that script to do is exit 0.
110+ - The generated ` env.sh ` is extended with a hook, and everything in it runs
111+ only for the commands that start the system. On the ** first** start of a
106112 deployment it creates ` releases/RELEASES ` , which is what lets the system
107113 manage its own releases — a short-lived VM, once, and only while that file
108114 is absent. The release root has to be writable for it to succeed; if it is
109115 not, the start still proceeds, with a warning, and ` bin/castle unpack ` and
110116 ` bin/castle install ` will later refuse — each reading the running system's
111117 own release records as it acts — rather than upgrade a system that cannot
112- record what it is running. Every start after the first does nothing at all.
113- The
114- hook is also where the provisional version marker left by a relup that
115- restarts the emulator will be consumed. Any ` env.sh ` the project supplies
116- through ` rel/env.sh.eex ` is preserved, and runs first.
118+ record what it is running.
119+
120+ Every start also runs OTP's ` heart ` , deliberately configured to do nothing:
121+ ` HEART_NO_KILL ` , no ` HEART_COMMAND ` , a beat timeout at heart's documented
122+ maximum, and the inert ` bin/start ` above. It is there for one reason —
123+ ` release_handler ` calls ` heart:set_cmd/1 ` while preparing an emulator
124+ restart, and that raises where no ` heart ` process exists.
125+
126+ If your deployment already asks for ` -heart ` — in ` rel/vm.args.eex ` , in
127+ ` ELIXIR_ERL_OPTIONS ` , in one of ` ERL_AFLAGS ` , ` ERL_FLAGS ` and ` ERL_ZFLAGS ` ,
128+ or in ` ERL_OTP<major>_FLAGS ` — that is fine, and nothing is added beside it:
129+ two of the flag make ` init:get_argument(heart) ` answer ` {ok, [[], []]} ` ,
130+ which heart's own startup check has no clause for, so the boot would hang
131+ having printed nothing. The hook settles it by asking ` erl ` what argument
132+ list it would build, so quoting and escaping in those values are read the way
133+ ` erl ` reads them, and all six places a flag can come from are covered at
134+ once. It asks on every start, which costs one short-lived ` erl ` that exits
135+ without booting anything; commands that do not start the system — ` eval ` ,
136+ ` rpc ` , ` remote ` — ask nothing.
137+
138+ Those three are ** assigned** , and ` HEART_COMMAND ` is ** unset** , rather than
139+ defaulted — so a deployment that already has any of them in its environment
140+ still gets a heart that does nothing. There is no opting out of that while
141+ this hook is in use: your supervisor owning the restart is what the rest of
142+ it depends on. A start that displaces one of your settings says so on
143+ standard error, naming what it displaced, rather than failing the boot over a
144+ configuration conflict or losing the setting silently. A deployment that sets
145+ none of them says nothing at all.
146+
147+ And a start that follows such a restart selects the version that was
148+ installed. See * Upgrades that restart the emulator* below.
149+
150+ Any ` env.sh ` the project supplies through ` rel/env.sh.eex ` is preserved, and
151+ runs first.
117152 - The generated _ name.rel_ is copied into the ` releases ` folder as _ name-vsn.rel_ ,
118153 which is where ` release_handler ` looks for it when unpacking a tarball.
119154 - Any checked ` relup ` is written into the version path of the release.
@@ -142,7 +177,8 @@ Moving the running system from one version to the next is done through `bin/cast
142177# Unpack myapp-0.1.1.tar.gz, which you have placed in myapp/releases.
143178> myapp/bin/castle unpack 0.1.1
144179
145- # Make 0.1.1 the version that is running now, without restarting the VM.
180+ # Make 0.1.1 the version that is running now. Whether the VM is restarted is a
181+ # property of the relup rather than of this command.
146182> myapp/bin/castle install 0.1.1
147183
148184# Make it the version that runs on restart too. With no version given, this
@@ -159,9 +195,38 @@ record OTP made up out of its boot script. Each asks the system itself, as it
159195acts, rather than trusting an answer given earlier; the refusal names the remedy,
160196which is a restart.
161197
162- Version selection on restart needs nothing from ` Forecastle ` : OTP's
163- ` release_handler ` records the committed version in ` releases/start_erl.data ` ,
164- which is exactly where the standard launcher reads it from.
198+ Version selection on restart needs nothing from ` Forecastle ` once a version has
199+ been committed: OTP's ` release_handler ` records the committed version in
200+ ` releases/start_erl.data ` , which is exactly where the standard launcher reads it
201+ from.
202+
203+ ### Upgrades that restart the emulator
204+
205+ ` bin/castle install ` is the same command whichever kind of transition the relup
206+ describes, and it exits 0 only once the version it installed is the one running —
207+ across a reboot, if there is one. What differs is what has to be in place around
208+ it.
209+
210+ ** Your supervisor owns the restart.** ` release_handler ` calls ` init:reboot() ` ,
211+ the operating system process exits, and nothing inside the release starts it
212+ again: ` bin/start ` is inert and ` HEART_COMMAND ` is unset — unset by the hook on
213+ every start, even where the environment supplies one — because two things
214+ starting one service is worse than the problem being solved. Run the release
215+ under systemd, a Docker restart policy, Kubernetes or runit. A release started by
216+ hand from a shell will simply stay down until you start it again.
217+
218+ ** Until you commit, a restart takes you back.** ` release_handler ` writes the
219+ installed version to ` releases/new_start_erl.data ` and leaves
220+ ` releases/start_erl.data ` naming the version that is still permanent — only
221+ ` bin/castle commit ` writes that file. So a provisional release that crashes
222+ before it is committed is followed by an ordinary start of the version you were
223+ on, with nobody intervening. ` bin/<release> version ` reports that version too,
224+ because what it prints is the version * to be booted* ; ask the running system if
225+ you want to know what is running.
226+
227+ ** Only the one-stage ` restart_emulator ` is supported.** ` mix forecastle.relup `
228+ never generates the two-stage ` restart_new_emulator ` and refuses it wherever it
229+ finds one; see below.
165230
166231## The Appup Compiler
167232
@@ -290,20 +355,13 @@ own is missing. A transition it judged hot and `systools` then could not generat
290355is a failure, so that the default never quietly ships something other than the
291356upgrade it decided on.
292357
293- > ** ` auto ` currently refuses a restart transition.** Castle can install a relup
294- > that restarts the emulator but cannot yet complete the transition (see below),
295- > so rather than write an upgrade plan that is known not to install, ` auto ` exits
296- > non-zero and names the edge that forced the restart and why. It names it before
297- > generating anything: an edge classification found to need a restart decides the
298- > run on its own, so a ` systools ` error from the transitions that were still going
299- > to be hot cannot be reported in its place. A ` restart_emulator ` an appup asked
300- > for by name is refused just as much - the same transition arrived at another
301- > way - but only becomes visible once there is a script to look at, so it is
302- > reported after generation. A relup with both kinds in it therefore names the
303- > classified edge, and the appup's own restart is reported by the run that follows
304- > once that edge is gone. This is temporary; when the refusal is lifted the run
305- > proceeds and both kinds are named in one announcement. ` --restart ` is the
306- > deliberate override for anyone who wants the relup anyway.
358+ The announcement names every edge that will restart and why — both the ones
359+ classification chose and any ` restart_emulator ` an appup asked for by name, in one
360+ message, since they are the same transition arrived at two ways. It also says
361+ what that means for reading the install back: ` install_release/1 ` replies
362+ ` {ok, Vsn, Descr} ` for such a transition, indistinguishably from a completed hot
363+ upgrade, and the emulator then reboots. ` --hot ` and ` --restart ` are the ways to
364+ insist on something else.
307365
308366** ` --hot ` ** requires a genuine hot upgrade of every transition, and exits
309367non-zero, having written nothing, if one cannot be: a missing appup entry, an
@@ -341,12 +399,9 @@ its own whenever the ERTS version differs between two releases, so a default
341399that simply generated a relup would ship the two-stage transition without
342400anybody having chosen it.
343401
344- Note that a restart transition can be * generated* but not yet * performed* .
345- ` release_handler ` calls ` heart:set_cmd/1 ` while preparing the reboot, which fails
346- where there is no ` heart ` process, so the install fails before anything reboots;
347- and the reboot would come back up on whichever version
348- ` releases/start_erl.data ` names, which nothing writes until the release is
349- committed. Until [ castle #14 ] ( https://github.com/ausimian/castle/issues/14 ) and
350- [ #10 ] ( https://github.com/ausimian/forecastle/issues/10 ) land, treat a restart
351- relup as something to generate and inspect rather than to deploy - and that is
352- why ` auto ` refuses to produce one, while ` --restart ` still will.
402+ Performing a one-stage restart transition takes two things the release now
403+ carries: a ` heart ` process, because ` release_handler ` calls ` heart:set_cmd/1 `
404+ while preparing the reboot, and something to select the installed version on the
405+ way back up, because the reboot would otherwise come back on whichever version
406+ ` releases/start_erl.data ` names. Both are in the ` env.sh ` hook; see
407+ * Upgrades that restart the emulator* above for what your supervisor has to do.
0 commit comments