21

I am working on my first real project in erlang, however, this code is simplified for brevity. I want to be able to load a newer version of a file into my project remotely while it's running. I've read about using a behavior like gen_server or gen_fsm which has this for free. While that might achieve the result, I want to use this to learn how to do it, not just get it done. I've read the docs about code replacement, and LYSE's bit about Hot Code Loving, among other things, but I haven't been able to find anything that works for what I'm doing, so here is the basic idea.

-module(reloading).

-export([loop/0]).

loop() ->
    receive
        upgrade ->
            ?MODULE:loop();
        hello ->
            io:format("This is a test~n"),
            loop();
        _ ->
            loop()
    end.

I am simply looping with the idea that I can send the message upgrade and it will load a newer version of the code.

$ erl
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.9.1  (abort with ^G)
1> c(reloading).
{ok,reloading}
2> Loop = spawn(reloading, loop, []).
<0.39.0>
3> Loop ! hello.
This is a test
hello

At this point I change 10 line to io:format("I have changed this!~n"),

4> Loop ! upgrade.
upgrade
5> Loop ! hello.  
This is a test
hello

I am expecting this hello call to print I have changed this! not This is a test. I know I can simply call c(reloading). and have this work the way expected, but I'm looking to send the actual project a message rather than manually updating the code. So where is my disconnect? What am I doing wrong, that I should be doing in order to hot load this code? As mentioned before, I am looking for a non-OTP solution for the sake of education.

joneshf
  • 2,296
  • 1
  • 20
  • 27

4 Answers4

25

For the sake of having an explicit answer, I am posting this.

Using @rvirding's suggestion of using the code module, I've modified it to look like this:

-module(reloading).

-export([loop/0]).

loop() ->
    receive
        upgrade ->
            code:purge(?MODULE),
            compile:file(?MODULE),
            code:load_file(?MODULE),
            ?MODULE:loop();
        hello ->
            io:format("This is a test~n"),
            loop();
        _ ->
            loop()
    end.

First code:purge the old ?MODULE, then compile:file the new file, and finally, code:load_file the new ?MODULE. This works as I originally intended.

$ erl
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.9.1  (abort with ^G)
1> Loop = spawn(reloading, loop, []).
<0.34.0>
2> Loop ! hello.
This is a test
hello

Change line to io:format("I have changed this!~n"),

3> Loop ! upgrade.                   
upgrade
4> Loop ! hello.  
I have changed this!
hello
joneshf
  • 2,296
  • 1
  • 20
  • 27
  • 6
    Generally this explicit handling of the new module (compile,purge,load) is not done in the actual server. You have an environment around which handles the code and then when the new version has been loaded you send the `upgrade` message to the server. This is also the only way if you have many servers which use the same code. OTP works like this. – rvirding Aug 15 '12 at 15:42
19

While erlang can handle two versions of a module and calling a function with mod:func(...) will always call the latest version of a module (if the function is exported) you still have to load the new version of the module into Erlang system. You can't expect it automagically detect that you happen to have a new version of the module somewhere, find it, compile it and load it.

N.B. compiling and loading are two separate things. So c(mod). both compiles and loads the module, while l(mod). just loads the object code (.beam file) of the already compiled module. The Erlang compiler is called from the module compile and it just compiles and generates a .beam file while the code loading is handled by the module code.

rvirding
  • 20,848
  • 2
  • 37
  • 56
  • Okay, cool, that makes sense. So how do I load the new version of the module without using the shell? – joneshf Aug 15 '12 at 12:59
  • 2
    @joneshf It's all done through the module `code` with `code:load_file(Mod)`. This is what both `c/1` and `l/1` in the shell use. For more docs see http://www.erlang.org/doc/man/code.html – rvirding Aug 15 '12 at 13:12
1

In addition to the above I'd like to notice that some tools that reload code automatically for you exist.

You should have a look at sync or active projects.

Lol4t0
  • 12,444
  • 4
  • 29
  • 65
  • Thanks. I was not aware of those projects. Without sounding snarky/ungrateful, this doesn't really provide answers to the questions posed. This answer might make more sense as a comment. – joneshf Sep 24 '15 at 20:56
  • 1
    @joneshf, I know, but I'm afraid it will not get enough attention as a comment while actually people searching for (automatic) hot code replacement will be more satisfied with those project than hand made solutions – Lol4t0 Sep 24 '15 at 21:18
  • 1
    Then maybe the problem is the title of the question is not specific enough. It could be changed to "Manual hot code replacement in Erlang", or something similar. – joneshf Sep 25 '15 at 13:56
1

Compile *.beam locally, then send it to your server and reload it as mentioned in man pages:

http://erlang.org/documentation/doc-1/reference_manual/code_loading.html#id86381

-module(m).
-export([loop/0]).

loop() ->
    receive
        code_switch ->
            m:loop();
        Msg ->
            ...
            loop()
    end.
xxxe
  • 11
  • 1