AndreyKo
AndreyKo

Reputation: 1481

Can I reload single changed Erlang module without stopping running application?

I have Erlang application which takes long time to start and even more to fully recompile. I make little changes which I'd like to test as fast as possible. I want to compile and load my changes without stopping the application. How to do it from Erlang console which I get to after starting the application? My source code is located in ./src directory and beam files are compiled to ./ebin directory and I want to do the same to the changed file. My application is started with

erl -pa ebin deps/*/ebin

Upvotes: 3

Views: 1004

Answers (3)

7stud
7stud

Reputation: 48599

You can do it by:

  1. Loading the new version of the module, e.g. c(module_name)

  2. Making a fully qualified call to a function in the new module. "Fully qualified" means:

     module_name:function_name()
    

    The module is only updated in the process where the fully qualified function call is made.

Here is an example:

a.erl:

-module(a).
-compile(export_all).

start() -> 
    %% Long startup time:
    timer:sleep(1000), 

    spawn(fun() -> loop() end).

loop() ->
    receive
        {a, Msg} -> 
            show1(Msg),
            loop();
        {b, Msg} -> 
            show2(Msg),
            loop();
        update_code -> a:loop()
    end.

show1(Arg) ->
    %%io:format("Hello ~w!~n", [Arg]).  %original code
    io:format("XXX ~w!~n", [Arg]).  %new code

show2(Arg) ->
    %%io:format("Goodbye ~w!~n", [Arg]).  %original code      
    io:format("YYY ~w!~n", [Arg]).  %new code

In the shell:

1> c(a).           
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}

2> Pid = a:start().
<0.72.0>

3> a:show1(jane).  %%Executed by shell process.
Hello jane!
ok

4> Pid ! {a, jane}. %% show1/1 executed in spawned process.
Hello jane!
{a,jane}

5> c(a).           
a.erl:2: Warning: export_all flag enabled - all functions will be exported
{ok,a}

6> a:show1(jane).  %%Module updated in shell process.
XXX jane!
ok

7> Pid ! {a, jane}.
Hello jane!     <---- STILL EXECUTING OLD MODULE
{a,jane}

8> Pid ! update_code.  %% Make fully qualified function call in spawned process.
update_code

9> Pid ! {a, jane}.  %% The whole module has been replaced by new code.
XXX jane!
{a,jane}

10> Pid ! {b, jane}.   
YYY jane!
{b,jane}

11> 

Upvotes: 5

odiferousmint
odiferousmint

Reputation: 56

Just to add to the other answers:

When you do a release, you may have two versions of your application and you may want to upgrade. Say, you first have lib/foo-1.0.0/ebin/foo_app.beam loaded. Then you install foo-2.0.0 via your preferred method, I use target_system:install("foo", "/tmp/erl-target"). Now I have /tmp/erl-target/lib/foo-2.0.0/.

I use code:which(foo_app) to see which one is loaded. To load it, in this case I would first start the Eshell as such:

$ /tmp/erl-target/bin/erl -boot /tmp/erl-target/releases/1/start

Afterwards I type:

1> application:ensure_all_started(foo).
{ok,[...,foo]}

To see which one is currently loaded, you type (in the same Eshell):

2> code:which(foo_app).                                  
"/tmp/erl-target/lib/foo-1.0.0/ebin/foo_app.beam"

You will need the foo.appup file in /tmp/erl-target/lib/foo-2.0.0/ebin/ with the following contents:

{"2.0.0",
 [{"1.0.0",[{load_module,foo_app}]}],
 [{"1.0.0",[{load_module,foo_app}]}]}.

To upgrade, you type:

3> release_handler:upgrade_app(foo, 'lib/foo-2.0.0').
{ok,[]}

If everything was successful, you will see the following:

4> code:which(foo_app).                                  
"/tmp/erl-target/lib/foo-2.0.0/ebin/foo_app.beam"

If I added a new function in foo-2.0.0 which I exported, it will become available, for example.

If you want to downgrade back to the previous version, you will have to have a similar foo.appup file in /tmp/erl-target/lib/foo-1.0.0/ebin/, but with the version numbers swapped.


Please correct me if I am wrong, I am new to Erlang. I did test it myself and it worked for me. This is just to upgrade your application within a release, this is not upgrading the release itself.

If something is unclear, let me know. I am still trying to work this out myself. :)

Upvotes: 3

legoscia
legoscia

Reputation: 41528

Compile the module as you usually do, and then type this into the erlang shell:

l(my_module).

This will load code from the new my_module.beam file and replace the my_module module.

The old version might not go away completely - see the section on Code Reloading in the Erlang reference manual for details.

Upvotes: 2

Related Questions