Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a digraph to a different process and node?

Tags:

erlang

I have created a digraph term on process A and I want to pass this digraph to a process on another node. Whenever I use this digraph on the other process I am getting errors such as:

** {badarg,
   [{ets,insert,[598105,{"EPqzYxiM9UV0pplPTRg8vX28h",[]}],[]},
    {digraph,do_add_vertex,2,[{file,"digraph.erl"},{line,377}]},

Becasuse a digraph is based on ETS, it appears that this is quite more complicated, making a digraph pretty much standalone on the process it was created. I have found this entry that reveals a similar problem : ETS on a different process

I know I can create the digraph in a server an then connect to it through otp messages, but I cannot do that in my architecture. All nodes can communicate using a specific approach designed to pass the state along as Terms.

It appears to me that having digraphs sent accross different nodes that cannot directly communicate with each other is not possible. Overall, it appears that a digraph cannot be directly serialized. I am thinking that I can "unwind" the digraph as a list of vertices and edges and then transmit and recreate it on the other process (not efficient, performing or elegant). Any thoughts on a better way to serialize it ? Is there a way to serialize the digraph state out of the ETS store ?

Any thoughts ?

like image 296
gextra Avatar asked Mar 21 '13 16:03

gextra


2 Answers

You can serialize/deserialize a digraph like this;

serialize({digraph, V, E, N, B}) ->
    {ets:tab2list(V),
     ets:tab2list(E),
     ets:tab2list(N),
     B}.

deserialize({VL, EL, NL, B}) ->       
    DG = {digraph, V, E, N, B} = case B of 
       true -> digraph:new();
       false -> digraph:new([acyclic])
    end,
    ets:delete_all_objects(V)
    ets:delete_all_objects(L)
    ets:delete_all_objects(N)
    ets:insert(V, VL)
    ets:insert(E, EL)
    ets:insert(N, NL)
    DG.

And this is the code I used to test this;

passer() ->
    G = digraph:new(),
    V1 = digraph:add_vertex(G),
    V2 = digraph:add_vertex(G),
    V3 = digraph:add_vertex(G),
    digraph:add_edge(G, V1, V2, "edge1"),
    digraph:add_edge(G, V1, V3, "edge2"),
    Pid = spawn(fun receiver/0),
    Pid ! serialize(G).

receiver() ->
     receive
         SG = {_VL, _EL, _NL, _B} ->
            G = deserialize(SG),
            io:format("Edges: ~p~n", [digraph:edges(G)]),
            io:format("Edges: ~p~n", [digraph:vertices(G)])
     end.

Pretty ugly solution but works. I think this is the only way pass digraph between nodes since ets tables cannot be shared between nodes.

Edit: remove unnecessary loops

like image 172
cashmere Avatar answered Nov 06 '22 02:11

cashmere


I have a solution, but it relies on the structure of the variable returned by digrapgh:new(), so I am not sure that it will be compatible with future version.

D = digraph:new(),
...
%some code modifying D
...
{digraph,Vertices,Edges,Neighbours,Cyclic} = D, % get the table Id of the 3 tables containing D values
% It should be preferable to use the record definition of the digraph module
%-record(digraph, {vtab = notable :: ets:tab(),
%         etab = notable :: ets:tab(),
%         ntab = notable :: ets:tab(),
%             cyclic = true  :: boolean()}).
LV = ets:tab2list(Vertices),
LE = ets:tab2list(Edges),
LN = ets:tab2list(Neighbours),
...
% then serialize and send all this variables to the target node, ideally in one single tuple like 
% {my_digraph_data,LV,LE,LN,Cyclic} or using refs to avoid the mix of messages, 
% and on reception on the remote node:

receive
    {my_digraph_data,LV,LE,LN,Cyclic} ->
        Dcopy = digrapgh:new(Cyclic),
        {digraph,Vertices,Edges,Neighbours,_Cyclic} = Dcopy,
        ets:insert(Vertices,LV),
        ets:insert(Edges,LE),
        ets:insert(Neighbours,LN),
        Dcopy;
...

and that's it.

Note: If you are testing this in the same shell, then make sure to change the name of Vertices, Edges, and Neighbours in the spawned process' receive expression otherwise it will fail with badmatch (as they have already been bound when matching D).

like image 3
Pascal Avatar answered Nov 06 '22 02:11

Pascal