Skip to content

Instantly share code, notes, and snippets.

@Xdeon
Created July 14, 2020 06:42
Show Gist options
  • Save Xdeon/9d5ba3a739deee0453637ceefb42c3e7 to your computer and use it in GitHub Desktop.
Save Xdeon/9d5ba3a739deee0453637ceefb42c3e7 to your computer and use it in GitHub Desktop.
frequency server scaling up with front-end router
-module(frequency_front).
-export([start/0, allocate/0, deallocate/1, stop/0]).
-export([init/0]).
%% API
allocate() ->
send_receive(allocate).
deallocate(Freq) ->
send_receive({deallocate, Freq}).
stop() ->
send_receive(stop).
send_receive(Info) ->
try frequency ! {request, self(), Info} of
_ ->
receive
{reply, Reply} -> Reply
after 500 ->
erlang:error(timeout)
end
catch error:badarg ->
erlang:error(serverdown)
end.
start() ->
register(frequency, spawn(?MODULE, init, [])).
%% Internal functions
init() ->
erlang:process_flag(trap_exit, true),
Servers = lists:map(fun({Id, Free}) ->
Pid = frequency_shard:start(Free),
link(Pid),
{Id, Pid} end, get_frequencies()),
%io:format("start servers: ~p~n", [Servers]),
loop(Servers).
get_frequencies() ->
[{freq1, [10,11,12,13,14,15]},
{freq2, [20,21,22,23,24,25]}].
loop(Servers) ->
receive
{request, _, _}=Req ->
case handle_request(Req, Servers) of
ok -> ok;
NewServers -> loop(NewServers)
end;
{'EXIT', Pid, _Reason} ->
loop(handle_exited(Pid, Servers))
end.
% round robin for allocate request
handle_request({request, Pid, allocate}, [{Id, S}|T]) ->
S ! {request, Pid, allocate},
T ++ [{Id, S}];
% dedicated routing for deallocate request
handle_request({request, Pid, {deallocate, Freq}}, Servers) ->
case freq_shard(Freq) of
{ok, Id} ->
S = proplists:get_value(Id, Servers),
S ! {request, Pid, {deallocate, Freq}};
{error, _}=Error ->
Pid ! {reply, Error}
end,
Servers;
% stop all servers and the front itself
handle_request({request, Pid, stop}, Servers) ->
terminate(Servers),
Pid ! {reply, stopped},
ok.
% restart crashed server
handle_exited(Pid, Servers) ->
case lists:keytake(Pid, 2, Servers) of
{value, {Id, Pid}, Rest} ->
New = frequency_shard:start(proplists:get_value(Id, get_frequencies())),
link(New),
[{Id, New}|Rest];
false ->
Servers
end.
% clean up for termination
terminate([]) ->
ok;
terminate([{_, S}|T]) ->
_ = frequency_shard:stop(S),
terminate(T).
% find appropriate server for given frequency
freq_shard(Freq) ->
case lists:filter(fun({_, Free}) ->
lists:member(Freq, Free) end, get_frequencies()) of
[{Id, _}] -> {ok, Id};
[] -> {error, illegal_frequency}
end.
%% Based on code from
%% Erlang Programming
%% Francecso Cesarini and Simon Thompson
%% O'Reilly, 2008
%% http://oreilly.com/catalog/9780596518189/
%% http://www.erlangprogramming.org/
%% (c) Francesco Cesarini and Simon Thompson
-module(frequency_shard).
-export([start/1, allocate/1, deallocate/2, stop/1]).
-export([init/1]).
%% These are the start functions used to create and
%% initialize the server.
start(Free) ->
spawn(?MODULE, init, [Free]).
init(Free) ->
Frequencies = {Free, []},
loop(Frequencies).
%% The Main Loop
loop(Frequencies) ->
receive
{request, Pid, allocate} ->
{NewFrequencies, Reply} = handle_allocate(Frequencies, Pid),
Pid ! {reply, Reply},
loop(NewFrequencies);
{request, Pid , {deallocate, Freq}} ->
NewFrequencies = handle_deallocate(Frequencies, Freq),
Pid ! {reply, ok},
loop(NewFrequencies);
{request, Pid, stop} ->
Pid ! {reply, stopped}
end.
%% Functional interface
allocate(Pid) ->
Pid ! {request, self(), allocate},
receive
{reply, Reply} -> Reply
end.
deallocate(Pid, Freq) ->
Pid ! {request, self(), {deallocate, Freq}},
receive
{reply, Reply} -> Reply
end.
stop(Pid) ->
Pid ! {request, self(), stop},
receive
{reply, Reply} -> Reply
end.
%% The Internal Help Functions used to allocate and
%% deallocate frequencies.
handle_allocate({[], Allocated}, _Pid) ->
{{[], Allocated}, {error, no_frequency}};
handle_allocate({[Freq|Free], Allocated}, Pid) ->
{{Free, [{Freq, Pid}|Allocated]}, {ok, Freq}}.
handle_deallocate({Free, Allocated}, Freq) ->
NewAllocated = lists:keydelete(Freq, 1, Allocated),
{[Freq|Free], NewAllocated}.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment