How do I calculate the time in which the process completes the request

Hello there,

First of all thank you for looking at my question, I’m new to Erlang and I’m studying by books and some question I found online. It was easy at first and now I can’t even understand some questions. Even if I do understand I can’t implement the topics I learned to the questions. There’s multiple questions to figure out If possible Can you please help me with this.

1 - Create a server that manages a list of available frequencies(say PROCESS [1,2,3,4,5,6,7,8,9,10]. The client process should be able to request any available frequency and release it. When the client releases the frequency, the server would calculate the amount (5 Rs/Sec) based upon the time the frequency was used by the client and would send the bill to the client.

Enhance the above question so that if a frequency allocated process dies without returning the frequency, the server retrieves the frequency by itself.

I really do appreciate your help even if you can’t solve. I have been trying this for 3 whole months and I’m honestly so down and hopeless. Thank you so much.

1 Like

Can you think about this question without thinking about any programming language at all, least of all Erlang?

It obviously doesn’t matter that the resources being managed are “frequencies” or that they are identified by integers.

Think about protocols.
Someone → starts the frequency master

A customer → manager “I am so-and-so. Frequency please.”

when a frequency is available, manager
transfers it from the “available” collection
to the “allocated” collection, tagged with a
timestamp.
Manager → customer “Here is your frequency.”
Customer → manager “I am so-and-so and I do not need (frequency) any more.”
Manager looks for (frequency, timestamp) in the “allocated collection”. If it’s not there,
manager → customer “you didn’t have it”.
If it is there, add the frequency to the “available”
collection, compute (now - timestamp) * price and
manager → customer “you owe Rs (xxx)”.

Someone → manager, drop dead!

This much is pretty basic.

There are at least two ways you could approach this. You could use basic Erlang, or you could use one of the existing behaviours. At this stage, I suspect you want to use basic Erlang.

so you are going to have something like

manager(Available, Allocated) →
receive
{stop} → ok
; {allocate,Requestor} when Available /= [] →
… stuff happens …
Requestor ! {grant,Frequency},
manager(Available1, Allocated1)
; {release,Requestor,Frequency} →
case … test if Frequency allocated to Requestor …

of true →
… stuff happens …
Requestor ! {bill,Amount},
manager{Available1, Allocated1)
; false →
Requestor ! {liar},
manager(Available, Allocated)
end
; _ → manager(Available, Allocated)
end.

Does this help? Get the crash-free case going,
then come back with questions about ‘link’ and
‘monitor’.

Traditionally, one would have used a list for
the frequencies, or a gb_set, or something, and
a list of {Frequency,Requestor,Timestamp} tuples for the allocated collection. These days one would probably reach for the (relatively) new built-in
map type.

@nzok First of all thank you so much, and can you please write it in a code, I can’t seem to put that into words. I am extremely new to this language and even if i try to do the code I am getting errors.

1 Like

I modify @nzok’s code

%% in fact, gen_server will be better
init() ->
    State = load_data_from_somewhere(),
    manager(State).

manager(State) ->
    receive
        {stop} -> ok;
        {allocate, Requestor, Frequency} ->
            %% check client request and server state
            %% of course you should do more and better, this is just an example
            case check_allocate_request_and_modify_state(Requestor, Frequency, State) of
                {ok, State1} ->
                    %% tell client you can use it
                    Requestor ! {grant, Frequency},
                    manager(State1);
                {error, Reason} ->
                    %% tell client you can't use it and why
                    Requestor ! {error, Reason},
                    manager(State)
            end;
        {release, Requestor, Frequency} ->
            %% same logic as before
            case check_release_request_and_modify_state(Requestor, Frequency, State) of
                {ok, Cost, State1} ->
                    %% tell client bill
                    Requestor ! {bill, Cost},
                    manager(State1);
                {error, Reason} ->
                    Requestor ! {error, Reason},
                    manager(State)
            end;
        UnknownMsg ->
            logger:error("unknown message: ~p", [UnknownMsg]),
            manager(State)
    end.

I think it’s easy for supervisor, you can stroe your data in ets(even database)
when process die, just restart and reload the data init/0

hope it can help you :slightly_smiling_face:

1 Like

Thanks @dominic Actually i can understand somewhat better. I tried running the code and got undefined for 3 things. It would be better If you can give me a sample input.

1 . Load_data_from_somewhere
2. case check_allocate…
3. case check_release

why is that? and I am working on processes topic not gen server. If it’s on gen server I can do that but the thing is that this is on processes. I’ll share the document if you can send a mail to me sanjuriosanju@gmail.com. Every one is welcome to text me.

-module(b).
-export([manager/1,init/0]).

init() ->
    State = {1,2,3,4,5,6,7,8,9,10},
    manager(State).

manager(State) ->
    receive
        {stop} -> ok;
        {allocate, Requestor, Frequency} ->
            %% check client request and server state
            %% of course you should do more and better, this is just an example
            case State(Requestor, Frequency, State) of
                {ok, State1} ->
                    %% tell client you can use it
                    Requestor ! {grant, Frequency},
                    manager(State1);
                {error, Reason} ->
                    %% tell client you can't use it and why
                    Requestor ! {error, Reason},
                    manager(State)
            end;
        {release, Requestor, Frequency} ->
            %% same logic as before
            case State(Requestor, Frequency, State) of
                {ok, Cost, State1} ->
                    %% tell client bill
                    Requestor ! {bill, Cost},
                    manager(State1);
                {error, Reason} ->
                    Requestor ! {error, Reason},
                    manager(State)
            end;
        UnknownMsg ->
            logger:error("unknown message: ~p", [UnknownMsg]),
            manager(State)
    end.

This is what I did after looking at the code

1 Like

these are function name,literally
meaning :rofl:
you can implement them
State is a variable store all the data

just normally i will work with gen_server in prod

Can you please help with the full code :sweat_smile: I am hopeless and stupid.

1 Like

OK, I can give you an example and you can play with it :slightly_smiling_face:
But just from learn, we will never write these code in produce!!!
maybe your demand is simple, but there is more thing we have to do in produce(supervisor, database, complex logic.etc)
So, again, just from learn!!!
play with test:run().

-module(test).

-compile([export_all, nowarn_export_all]).

run() ->
    %% using dets for example
    file:delete("example.dets"),
    %% this step should do in other place
    %% like `xxx_sup` when app start
    case dets:info(example, owner) of
        undefined ->
            dets:open_file(example, [{file, "example.dets"}]);
        _ ->
            skip
    end,
    spawn(fun() -> init() end),
    %% wait for start
    timer:sleep(100),
    Client = self(),

    %% normal operation
    example_server ! {allocate, Client, aaa, 1},
    example_server ! {allocate, Client, bbb, 1},
    example_server ! {allocate, Client, aaa, 1},
    timer:sleep(1000),
    example_server ! {release, Client, aaa, 1},
    example_server ! {release, Client, aaa, 1},
    %% ensure receive all msg
    timer:sleep(100),
    io:format("~p~n", [receive_server_msg([])]),

    %% allocate then server be killed
    example_server ! {allocate, Client, aaa, 1},
    %% ensure receive all msg
    timer:sleep(100),
    exit(whereis(example_server), kill),
    %% restart by somewhere(your sup)
    spawn(fun() -> init() end),
    %% wait for start
    timer:sleep(100),
    example_server ! {allocate, Client, bbb, 1},
    example_server ! {allocate, Client, aaa, 1},
    timer:sleep(1000),
    example_server ! {release, Client, aaa, 1},
    example_server ! {release, Client, aaa, 1},
    %% ensure receive all msg
    timer:sleep(100),
    io:format("~p~n", [receive_server_msg([])]),
    example_server ! {stop},

    dets:close(example).

receive_server_msg(List) ->
    receive
        Reply ->
            receive_server_msg([Reply|List])
        after 0 ->
            lists:reverse(List)
    end.
            

init() ->
    erlang:register(example_server, self()),
    State = other_data_you_need,
    manager(State).

manager(State) ->
    receive
        {stop} -> ok;
        {allocate, Requestor, Name, Frequency} ->
            %% check client request and server state
            %% of course you should do more and better, this is just an example
            case check_allocate_request_and_modify_state(Name, Frequency, State) of
                {ok, State1} ->
                    %% tell client you can use it
                    Requestor ! {grant, Frequency},
                    manager(State1);
                {error, Reason} ->
                    %% tell client you can't use it and why
                    Requestor ! {error, Reason},
                    manager(State)
            end;
        {release, Requestor, Name, Frequency} ->
            %% same logic as before
            case check_release_request_and_modify_state(Name, Frequency, State) of
                {ok, Cost, State1} ->
                    %% tell client bill
                    Requestor ! {bill, Cost},
                    manager(State1);
                {error, Reason} ->
                    Requestor ! {error, Reason},
                    manager(State)
            end;
        UnknownMsg ->
            logger:error("unknown message: ~p", [UnknownMsg]),
            manager(State)
    end.

check_allocate_request_and_modify_state(Name, Frequency, State) ->
    %% Is valid Frequency
    case lists:member(Frequency, [1,2,3,4,5]) of
        true ->
            %% Is someone using
            case dets:lookup(example, Frequency) of
                [] ->
                    dets:insert(example, {Frequency, Name, erlang:system_time(second)}),
                    {ok, State};
                [{_, Name, _}] ->
                    {error, using};
                _ ->
                    {error, other_using}
            end;
        _ ->
            {error, bad_request}
    end.

check_release_request_and_modify_state(Name, Frequency, State) ->
    case dets:lookup(example, Frequency) of
        [{_, Name, StartTime}] ->
            dets:delete(example, Frequency),
            Cost = (erlang:system_time(second) - StartTime),% * Price,
            {ok, Cost, State};
        _ ->
            {error, not_using}
    end.
1 Like