[erlang-questions] Feedback on webmachine route
Damien Krotkine
damien@REDACTED
Fri Sep 25 18:03:46 CEST 2015
Hi,
I'm kinda new to Erlang, and I'm extending the ReST API of Riak, so that
a single request will make it fetch multiple keys and return their
values, concatenated.
It's spawning a process for each keys for fetching the values, then wait
to receive all the values to stich them together and return it to the
client.
I'm sure this code is horrible from the point of view of a seasoned
Erlang developer, and I welcome any feedback. Especially regarding speed
and robustness against faults.
Thanks,
dams
Link to code here: http://lpaste.net/6968618518325493760
Also pasted below:
-module(events_api).
%% webmachine resource exports
-export([
init/1,
content_types_provided/2,
service_available/2,
malformed_request/2,
to_textplain/2
]).
-include_lib("webmachine/include/webmachine.hrl").
-record(ctx, { bucket = undefined,
key = undefined
}).
init(_) ->
{ok, #ctx{}}.
content_types_provided(ReqData, Context) ->
{[{"plain/text", to_textplain}],
ReqData, Context}.
malformed_request(ReqData, Ctx) ->
{false, ReqData, Ctx}.
service_available(ReqData, Ctx) ->
{true, ReqData, Ctx}.
to_textplain(ReqData, Ctx) ->
Epoch = wrq:get_qs_value("epoch", "n/a", ReqData),
DC = wrq:get_qs_value("dc", ReqData),
Types = wrq:get_qs_value("types", "n/a", ReqData),
Personas = wrq:get_qs_value("personas", "n/a", ReqData),
error_logger:info_msg("epoch:~p DC:~p types:~p personas:~p",[ Epoch,
DC, Types, Personas ]),
{ok, Client} = riak:local_client(),
% fetch metadata, returns a list of data keys
DataKeys = get_datakeys(list_to_binary(Epoch), list_to_binary(DC),
Client),
error_logger:info_msg("DataKeys: ~p ",[ DataKeys ]),
% get the actual event blobs from the data keys
Blobs = get_values(Client, DataKeys),
error_logger:info_msg(" got ~p blobs",[ lists:flatlength(Blobs) ]),
% join blobs together, and return it
Body = binary_join(Blobs),
{[Body], ReqData, Ctx}.
% 1442926965
get_datakeys(Epoch, DC, Client) ->
Bucket = <<"epochs">>,
Key = <<Epoch/binary, "-", DC/binary>>,
Options = [ {r, 1}, {timeout, 1000} ],
error_logger:info_msg("fetching metadata bucket:~p key:~p ...",[
Bucket, Key ]),
{ ok, RiakObject } = riak_client:get(Bucket, Key, Options, Client),
Data = riak_object:get_value(RiakObject),
Partials = binary:split(Data, <<"|">>, [ global ]),
% data keys have the epoch prepended, let's do that
lists:map( fun(Partial) -> <<Epoch/binary, ":", Partial/binary>>
end, Partials).
% easy api
get_values(Client, DataKeys) ->
get_values(Client, DataKeys, [], []).
% main code: spawn a child that fetches, accumulate child pid
get_values(Client, [DataKey | Tail], Children, []) ->
Parent = self(),
Child = spawn_link(
fun() ->
Options = [ {r, 1}, {timeout, 1000} ],
Bucket = <<"events">>,
{ ok, RiakObject } = riak_client:get(Bucket,
DataKey, Options, Client),
Blob = riak_object:get_value(RiakObject),
Parent ! { self(), Blob
}
end ),
get_values(Client, Tail, [ Child | Children ], []).
% all keys fetches are spawn, wait and collect all the results
get_values(_Client, [], Children, Blobs) ->
receive {Child, Blob} ->
case lists:member(Child, Children) of
true -> get_values(undefined, [], lists:delete(Child,
Children), [Blob| Blobs]);
false -> erlang:error("Wrong signal: doesn't match
protocol")
end
end;
% all results collected, return the blobs.
get_values(_Client, [], [], Blobs) ->
Blobs;
binary_join([]) ->
<<>>;
binary_join([Part]) ->
Part;
binary_join([Head|Tail]) ->
lists:foldl(fun (Value, Acc) -> <<Acc/binary, Value/binary>> end,
Head, Tail).
More information about the erlang-questions
mailing list