This is a simple introduction to OTP in Elixir.
The sample is based heavily upon Introducing Elixir.
This will demonstrate how Elixir keeps a server alive across code recompilation, and restores a server after it has crashed.
This assumes that you have installed Elixir.
On a mac you can install this with (there are other solutions on other platforms):
brew install elixir
Start by cloning the repo:
Change directory into the drop_server folder.
Type
iex -S mix
This will start the application running inside the interactive Elixir REPL.
This application has a DropServer.Worker being monitored by a supervisor.
First you can calculate the velocity after falling a distance in meters:
(Type the bit after the iex> prompts):
iex(1)> DropServer.Worker.calculate_drop(40)
{“ok”, 28.0}
iex(2)> DropServer.Worker.calculate_drop(41)
{“ok”, 28.347839423843222}
iex(3)> DropServer.Worker.calculate_drop(42)
{“ok”, 28.691462144686877}
iex(4)> DropServer.Worker.how_many_calls
So far calculated 3 velocities.
:ok
You can even recompile the DropServer.Worker while it is running:
iex(5)> c(“lib/drop_server/drop_server.ex”)
warning: redefining module DropServer.Worker (current version loaded from _build/dev/lib/drop_server/ebin/Elixir.DropServer.Worker.beam)
lib/drop_server/drop_server.ex:1
warning: redefining module DropServer.Worker.State (current version loaded from _build/dev/lib/drop_server/ebin/Elixir.DropServer.Worker.State.beam)
lib/drop_server/drop_server.ex:4
[DropServer.Worker, DropServer.Worker.State]
This even maintains the state:
iex(6)> DropServer.Worker.how_many_calls
So far calculated 3 velocities.
:ok
Now if you give it some invalid data Elixir will do the classic Erlang thing and let it crash!
iex(7)> DropServer.Worker.calculate_drop(-1)
21:49:21.278 [error] GenServer DropServer.Worker terminating
** (ArithmeticError) bad argument in arithmetic expression
(stdlib) :math.sqrt(-19.6)
(drop_server) lib/drop_server/drop_server.ex:44: DropServer.Worker.fall_velocity/1
(drop_server) lib/drop_server/drop_server.ex:20: DropServer.Worker.handle_call/3
(stdlib) gen_server.erl:661: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:690: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
Last message (from #PID<0.133.0>): -1
State: %DropServer.Worker.State{count: 3}
Client #PID<0.133.0> is alive
(stdlib) gen.erl:169: :gen.do_call/4
(elixir) lib/gen_server.ex:921: GenServer.call/3
(stdlib) erl_eval.erl:680: :erl_eval.do_apply/6
(elixir) src/elixir.erl:265: :elixir.eval_forms/4
(iex) lib/iex/evaluator.ex:249: IEx.Evaluator.handle_eval/5
(iex) lib/iex/evaluator.ex:229: IEx.Evaluator.do_eval/3
(iex) lib/iex/evaluator.ex:207: IEx.Evaluator.eval/3
(iex) lib/iex/evaluator.ex:94: IEx.Evaluator.loop/1
** (exit) exited in: GenServer.call(DropServer.Worker, -1, 5000)
** (EXIT) an exception was raised:
** (ArithmeticError) bad argument in arithmetic expression
(stdlib) :math.sqrt(-19.6)
(drop_server) lib/drop_server/drop_server.ex:44: DropServer.Worker.fall_velocity/1
(drop_server) lib/drop_server/drop_server.ex:20: DropServer.Worker.handle_call/3
(stdlib) gen_server.erl:661: :gen_server.try_handle_call/4
(stdlib) gen_server.erl:690: :gen_server.handle_msg/6
(stdlib) proc_lib.erl:249: :proc_lib.init_p_do_apply/3
(elixir) lib/gen_server.ex:924: GenServer.call/3
However only the state is lost:
iex(7)> DropServer.Worker.how_many_calls
So far calculated 0 velocities.
:ok
iex(8)> DropServer.Worker.calculate_drop(10)
{“ok”, 14.0}
iex(9)> DropServer.Worker.how_many_calls
So far calculated 1 velocities.
:ok
The supervisor has restarted the server.
In a real service the state would have been kept in a distinct service possibly with some form of persistence.