When trying to devise a simple example of evaluating an .appup script, I did the following:
- In `testapp`, vsn 1, I had one child in the `testapp_sup` supervisor
- The child, `testapp_p1` was spawn_link:ed as a plain process, to force a brutal purge
- In vsn 2, the supervisor childspec list was empty
When letting `setup` generate an .appup script, the app upgrade crashed as the upgraded supervisor kept the old childspec and tried to restart the `testapp_p1` process, even though the module had been purged.
In `supervisor:code_change/3`, the new `StartSpec` is taken from `CB:init/1`, and the old start spec is processed to copy the child pid into the new child spec. But if the `Id` isn’t found in the new spec, the old child spec is kept as-is.
Why would that be? It seems counter-intuitive to me.
Also, due to this, it’s not obvious to me how to generate an .appup script that will work.
For reference, here’s the auto-generated script:
[{load_object_code,{testapp,"2",[testapp_app,testapp_sup]}},
point_of_no_return,
{suspend,[testapp_app,testapp_sup]},
{load,{testapp_app,brutal_purge,brutal_purge}},
{load,{testapp_sup,brutal_purge,brutal_purge}},
{code_change,up,[{testapp_app,setup},{testapp_sup,setup}]},
{resume,[testapp_app,testapp_sup]},
{remove,{testapp_p1,brutal_purge,brutal_purge}},
{purge,[testapp_p1]}]
Here is a bit of trace to illustrate what happens:
(<0.96.0>) call supervisor:code_change(253393648690427985072663628403827426949,{state,{local,testapp_sup},
one_for_one,
{[testapp_p1],
#{testapp_p1 =>
{child,<0.97.0>,testapp_p1,
{testapp_p1,start_link,[]},
permanent,false,5000,worker,
[testapp_p1]}}},
undefined,5,10,[],0,never,testapp_sup,[]},setup)
(<0.96.0>) call supervisor:set_flags({one_for_one,5,10},{state,{local,testapp_sup},
one_for_one,
{[testapp_p1],
#{testapp_p1 =>
{child,<0.97.0>,testapp_p1,
{testapp_p1,start_link,[]},
permanent,false,5000,worker,
[testapp_p1]}}},
undefined,5,10,[],0,never,testapp_sup,[]})
...
(<0.96.0>) call supervisor:update_childspec({state,{local,testapp_sup},
one_for_one,
{[testapp_p1],
#{testapp_p1 =>
{child,<0.97.0>,testapp_p1,
{testapp_p1,start_link,[]},
permanent,false,5000,worker,
[testapp_p1]}}},
undefined,5,10,[],0,never,testapp_sup,[]},[])
(<0.96.0>) returned from supervisor:update_childspec/2 -> {ok,
{state,
{local,
testapp_sup},
one_for_one,
{[testapp_p1],
#{testapp_p1 =>
{child,
<0.97.0>,
testapp_p1,
{testapp_p1,
start_link,
[]},
permanent,
false,5000,
worker,
[testapp_p1]}}},
undefined,5,10,[],
0,never,
testapp_sup,[]}}
(<0.96.0>) returned from supervisor:code_change/3 -> {ok,
{state,
{local,testapp_sup},
one_for_one,
{[testapp_p1],
#{testapp_p1 =>
{child,<0.97.0>,
testapp_p1,
{testapp_p1,
start_link,[]},
permanent,false,
5000,worker,
[testapp_p1]}}},
undefined,5,10,[],0,
never,testapp_sup,[]}}
(<0.96.0>) call supervisor:handle_info({'EXIT',<0.97.0>,killed},{state,{local,testapp_sup},
one_for_one,
{[testapp_p1],
#{testapp_p1 =>
{child,<0.97.0>,testapp_p1,
{testapp_p1,start_link,[]},
permanent,false,5000,worker,
[testapp_p1]}}},
undefined,5,10,[],0,never,testapp_sup,[]})
(<0.96.0>) call supervisor:restart_child(<0.97.0>,killed,{state,{local,testapp_sup},
one_for_one,
{[testapp_p1],
#{testapp_p1 =>
{child,<0.97.0>,testapp_p1,
{testapp_p1,start_link,[]},
permanent,false,5000,worker,
[testapp_p1]}}},
undefined,5,10,[],0,never,testapp_sup,[]})
{ok,[]}
The `'EXIT’` signal arrives from the `testapp_p1` process being killed due to the brutal purge. Since the old childspec survived the code_change, the supervisor tries to restart it.
BR,
Ulf