Current section
Files
Jump to
Current section
Files
src/loki_xml_dist.erl
%%%-------------------------------------------------------------------
%%% @doc loki_xml_dist - Distributed chunk processing across nodes
%%% @end
%%%-------------------------------------------------------------------
-module(loki_xml_dist).
-export([
distribute/2,
parse_chunk_rpc/1
]).
%%%===================================================================
%%% API Functions
%%%===================================================================
%% @doc Distribute chunks across multiple Erlang nodes
-spec distribute([node()], [binary()]) -> [{ok, term()} | {error, term()}].
distribute(Nodes, Chunks) ->
%% Ensure nodes are available
ConnectedNodes = ensure_connected(Nodes),
case ConnectedNodes of
[] ->
%% Fallback to local processing
io:format("[loki_xml_dist] No nodes available, processing locally~n"),
process_local(Chunks);
_ ->
%% Distribute chunks round-robin across nodes
distribute_chunks(ConnectedNodes, Chunks)
end.
%% @doc RPC endpoint for parsing a chunk on remote node
-spec parse_chunk_rpc(binary()) -> {ok, [term()]} | {error, term()}.
parse_chunk_rpc(Chunk) ->
loki_xml_parser:parse(Chunk).
%%%===================================================================
%%% Internal Functions
%%%===================================================================
ensure_connected(Nodes) ->
lists:filter(fun(Node) ->
case net_adm:ping(Node) of
pong -> true;
pang ->
io:format("[loki_xml_dist] Node ~p not reachable~n", [Node]),
false
end
end, Nodes).
distribute_chunks(Nodes, Chunks) ->
%% Create node-chunk pairs using round-robin
NodeChunkPairs = assign_chunks_to_nodes(Nodes, Chunks),
%% Group chunks by node for batch processing
ChunksByNode = group_by_node(NodeChunkPairs),
%% Process on each node
Results = lists:flatmap(fun({Node, NodeChunks}) ->
process_on_node(Node, NodeChunks)
end, maps:to_list(ChunksByNode)),
Results.
assign_chunks_to_nodes(Nodes, Chunks) ->
NodeCount = length(Nodes),
lists:zip(Chunks, lists:flatten(lists:duplicate(length(Chunks) div NodeCount + 1, Nodes))).
group_by_node(NodeChunkPairs) ->
lists:foldl(fun({Chunk, Node}, Acc) ->
maps:update_with(Node, fun(Chunks) -> [Chunk | Chunks] end, [Chunk], Acc)
end, #{}, NodeChunkPairs).
process_on_node(Node, Chunks) ->
Parent = self(),
%% Spawn a process for each chunk on the remote node
Pids = lists:map(fun(Chunk) ->
spawn(fun() ->
Result = rpc:call(Node, ?MODULE, parse_chunk_rpc, [Chunk], 30000),
Parent ! {self(), Result}
end)
end, Chunks),
%% Collect results
collect_results(Pids, []).
collect_results([], Acc) ->
lists:reverse(Acc);
collect_results([Pid | Rest], Acc) ->
receive
{Pid, Result} ->
collect_results(Rest, [Result | Acc])
after 30000 ->
%% Timeout - mark as error
collect_results(Rest, [{error, {timeout, Pid}} | Acc])
end.
process_local(Chunks) ->
lists:map(fun(Chunk) ->
loki_xml_parser:parse(Chunk)
end, Chunks).