[erlang-questions] how: mnesia: id/generator in a cluster

Ulf Wiger (TN/EAB) <>
Wed Nov 7 09:31:11 CET 2007


Dmitri Girenko wrote:
> Thanks Ladislav, dirty_update_counter is exactly what I've been looking
> for.
> 
> {node(), now()} is also a good idea, but I need a short and numeric ID
> to embed into URL.

It's a bit tricky if you want it really short, but still
unique (and also numeric).

I once wrote the following code, which I've never used in
any real product. The idea is that you have a counter per
erlang node in your cluster, and then assign each node an
index; the combination of node index and counter value
becomes your unique id (unique to your cluster, that is).

The new() operation is fast, as long as the counter stays
within the predefined bounds (32 bits), but then, a new
node index is checked out, which is a heavier operation.

A simple extension would be to remember which node ids
have been assigned to each node. That would allow you to
determine the source node of each unique id.

Note well that I never used it. See it as a sketch of one
possible solution.

BR,
Ulf W

-module(bbsOid).

-export([new/0,
          incr_vsn/1,
          add_branch/2,
          add_peer_node/1]).

-define(MAX_SEQ, 16#ffffFFFF)

new() ->
     MyNode = node(),
     NodeId = mnesia:read_table_property(schema, {bbs_node_id,MyNode}),
     case mnesia:dirty_update_counter(bbsOid, MyNode, 1) of
         NodeSeq when NodeSeq > ?MAX_SEQ ->
             NewNodeId  = set_new_node_id(MyNode),
             mnesia:dirty_write({bbsOid, MyNode, 1}),
             {NewNodeId, 1};
         NodeSeq ->
             {NodeId, NodeSeq}
     end.


add_peer_node(Node) ->
     mnesia_schema:schema_transaction(
       fun() ->
               case is_peer_node(Node) of
                   true ->
                       mnesia:abort({peer_node_exists, Node});
                   false ->
                       do_set_new_node_id(Node)
               end
       end).

set_new_node_id(Node) ->
     mnesia_schema:schema_transaction(
       fun() ->
               case is_peer_node(Node) of
                   true ->
                       do_set_new_node_id(Node);
                   false ->
                       mnesia:abort({not_a_peer_node, Node})
               end
       end).

do_set_new_node_id(Node) ->
     PeerNodes = read_property(bbs_peer_nodes, ordsets:new()),
     OldCtr = read_property(bbs_node_id_ctr, 0),
     NewCtr = OldCtr + 1,
     write_property({bbs_node_id_ctr, NewCtr}),
     write_property({{bbs_node_id, Node}, NewCtr}),
     write_property({bbs_peer_nodes,
                     ordsets:add_element(Node, PeerNodes)}),
     NewCtr.

read_property(P, Default) ->
     case mnesia_schema:do_read_table_property(
            schema, P) of
         undefined ->
             Default;
         Value ->
             Value
     end.

is_peer_node(Node) ->
     Peers = read_property(bbs_peer_nodes, ordsets:new()),
     ordsets:is_element(Node, Peers).

write_property(P, Value) ->
     mnesia_schema:do_write_table_property(schema, {P, Value}).




More information about the erlang-questions mailing list