All Projects → sile → evel

sile / evel

Licence: MIT license
An Eventual Leader Election Library for Erlang

Programming Languages

erlang
1774 projects

Projects that are alternatives of or similar to evel

transaction-outbox
Reliable eventual consistency for Microservices
Stars: ✭ 59 (+68.57%)
Mutual labels:  eventually-consistent
sidecloq
Recurring / Periodic / Scheduled / Cron job extension for Sidekiq
Stars: ✭ 81 (+131.43%)
Mutual labels:  leader-election
pinboard
A threadsafe way to publish data, just stick it on the pinboard
Stars: ✭ 24 (-31.43%)
Mutual labels:  eventually-consistent
Atomix
A reactive Java framework for building fault-tolerant distributed systems
Stars: ✭ 2,182 (+6134.29%)
Mutual labels:  leader-election
ring-election
A node js library with a distributed leader/follower algorithm ready to be used
Stars: ✭ 92 (+162.86%)
Mutual labels:  leader-election
kube-leader-election
A crate to implement leader election for Kubernetes workloads in Rust.
Stars: ✭ 25 (-28.57%)
Mutual labels:  leader-election
Distributed-System-Algorithms-Implementation
Algorithms for implementation of Clock Synchronization, Consistency, Mutual Exclusion, Leader Election
Stars: ✭ 39 (+11.43%)
Mutual labels:  leader-election
Olric
Distributed cache and in-memory key/value data store. It can be used both as an embedded Go library and as a language-independent service.
Stars: ✭ 2,067 (+5805.71%)
Mutual labels:  eventually-consistent
sucredb
Distributed KV database with causality tracking
Stars: ✭ 51 (+45.71%)
Mutual labels:  eventually-consistent

evel

hex.pm version Build Status Code Coverage License: MIT

evel is a distributed leader election library which has eventual consistency.

If the nodes which have started evel application eventually agree with the same member list (usually distributed erlang ensures it), each election will elect a single leader who is recognized by all of them.

In the quiescent state (e.g., there are no joining nodes to the cluster), the cost of an election is O(M) where M is the number of members (≈ nodes) who are interested in the election. It is unaffected by the cluster size to which they belong.

evel provides weaker consistency than global which can be regarded as OTP's leader election module. But it is more scalable and has higher availability (e.g., more tolarent to timing failures of some nodes in the same cluster).

See Comparison with the global module for more details.

Build

To build the library for stand-alone usage:

$ git clone https://github.com/sile/evel.git
$ cd evel
$ ./rebar3 compile
$ ./rebar3 shell
$ > evel:module_info().

If you want to use from your application:

%% In your 'rebar.config'

%% Add following lines
{deps,
 [
   evel
 ]}.

Example

$ ./rebar3 shell --sname master

%%
%% Starts 10 slave nodes on the local machine
%%
> ok = evel_debug:slave_start_link_n(10).
> nodes().
['1@localhost','2@localhost','3@localhost','4@localhost',
'5@localhost','6@localhost','7@localhost','8@localhost',
'9@localhost','10@localhost']

%%
%% Elects the leader from among following candidates
%%
> Candidates = [spawn(timer, sleep, [infinity]) || _ <- lists:seq(1, 100)].
> rpc:parallel_eval([{evel, elect, [foo, Candidate]} || Candidate <- Candidates]).

%%
%% Finds the elected leader
%%
> {ok, Leader} = evel:find_leader(foo).
> {Results0, []} = rpc:multicall(evel, find_leader, [foo]).
> lists:foreach(fun (R) -> {ok, Leader} = R end, Results0). % All nodes agree with the single leader

%%
%% Dismisses the leader
%%
> ok = evel:dismiss(Leader).
> error = evel:find_leader(foo).
> {Results1, []} = rpc:multicall(evel, find_leader, [foo]).
> lists:foreach(fun (R) -> error = R end, Results1).

API

See EDoc Documents

Comparison with the global module

The following examples depict some differences between evel and global.

$ ./rebar3 shell --sname master
%%%
%%% SETUP
%%%

%% Starts 100 slave nodes on the local machine
> ok = evel_debug:slave_start_n(50).
> nodes().
['1@localhost','2@localhost','3@localhost','4@localhost',
'5@localhost','6@localhost','7@localhost','8@localhost'|...]

%% A auxiliary function
> RpcMultiApply = fun (F) -> {Result, []} = rpc:multicall(erlang, apply, [F, []]), Result end.


%%%
%%% Consistency
%%%

%% [global]
%% Every member always agree with a single leader.
> RpcMultiApply(fun () -> global:register_name(foo, spawn(timer, sleep, [infinity])), global:whereis_name(foo) end).
> lists:usort(v(-1)).
[<0.617.0>]

%% [evel]
%% At the time of conflict, multiple leaders may coexist temporarily.
> RpcMultiApply(fun () -> evel:elect(foo, spawn(timer, sleep, [infinity])) end).
> lists:usort(v(-1)).
[{<10721.296.0>,<10721.298.0>},
 {<10720.298.0>,<10720.300.0>},
 {<10729.307.0>,<10729.309.0>}]

%% But every member eventually agree with a single leader.
> RpcMultiApply(fun () -> evel:elect(foo, spawn(timer, sleep, [infinity])) end).
> lists:usort(v(-1)).
[{<10720.298.0>,<10720.300.0>}]


%%
%% Efficiency (or Scalability)
%%

%% [global]
%% All members participate the same election.
> timer:tc(fun () -> length(RpcMultiApply(fun () -> global:register_name(bar, spawn(timer, sleep, [infinity])) end)) end).
{7573849,51} % 7.573 seconds

%% Each member participate each election.
> timer:tc(fun () -> length(RpcMultiApply(fun () -> global:register_name(node(), spawn(timer, sleep, [infinity])) end)) end).
{9992855,51} % 9.992 seconds

%% [evel]
%% All members participate the same election.
> timer:tc(fun () -> length(RpcMultiApply(fun () -> evel:elect(bar, spawn(timer, sleep, [infinity])) end)) end).
{99444,51} % 0.099 seconds

%% Each member participate each election.
> timer:tc(fun () -> length(RpcMultiApply(fun () -> evel:elect(node(), spawn(timer, sleep, [infinity])) end)) end).
{324833,51} % 0.324 seconds


%%
%% Availability
%%

%% Suspends a slave node
> os:cmd("kill -19 ${A_SLAVE_OS_PID}").

%% global will block until the node is resumed.
> global:register_name(baz, self()). % Type `Ctrl+G => i => c` to break

%% evel is unaffected by the suspension.
> evel:elect(baz, self()).
{<0.715.0>,<0.718.0>}

%% Resumes the slave node
> os:cmd("kill -18 ${A_SLAVE_OS_PID}").


%%
%% Knowledge
%%

%% [global]
%% Every member locally knowns the results of all elections.
> global:registered_names().
['8@localhost','6@localhost','23@localhost','1@localhost',
'16@localhost','43@localhost','14@localhost','42@localhost' | ...]

%% [evel]
%% Each member locally knows the results of elections in which are interested.
> evel:known_leaders().
[{foo,{<10720.298.0>,<10720.300.0>}},
 {master@localhost,{<0.665.0>,<0.667.0>}},
 {bar,{<10735.305.0>,<10735.307.0>}}]

%% If the node want to know the result of other election,
%% it will fetch the result from some remote nodes.
> evel:find_leader('1@localhost').
{ok,{<10663.315.0>,<10663.317.0>}}

> evel:known_leaders().
[{'1@localhost',{<10663.315.0>,<10663.317.0>}},
 {foo,{<10720.298.0>,<10720.300.0>}},
 {master@localhost,{<0.665.0>,<0.667.0>}},
 {bar,{<10735.305.0>,<10735.307.0>}}]

License

This library is released under the MIT License. See the LICENSE file for full license information.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].