{"id":15056441,"url":"https://github.com/sile/evel","last_synced_at":"2025-04-10T04:10:34.084Z","repository":{"id":57497922,"uuid":"53948738","full_name":"sile/evel","owner":"sile","description":"An Eventual Leader Election Library for Erlang","archived":false,"fork":false,"pushed_at":"2020-09-24T11:34:04.000Z","size":755,"stargazers_count":36,"open_issues_count":0,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-24T05:26:13.860Z","etag":null,"topics":["erlang","eventually-consistent","leader-election"],"latest_commit_sha":null,"homepage":null,"language":"Erlang","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sile.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-15T13:55:51.000Z","updated_at":"2023-07-25T14:00:51.000Z","dependencies_parsed_at":"2022-08-28T19:41:32.269Z","dependency_job_id":null,"html_url":"https://github.com/sile/evel","commit_stats":null,"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sile%2Fevel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sile%2Fevel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sile%2Fevel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sile%2Fevel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sile","download_url":"https://codeload.github.com/sile/evel/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247818321,"owners_count":21001378,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["erlang","eventually-consistent","leader-election"],"created_at":"2024-09-24T21:51:29.870Z","updated_at":"2025-04-10T04:10:34.060Z","avatar_url":"https://github.com/sile.png","language":"Erlang","funding_links":[],"categories":[],"sub_categories":[],"readme":"evel\n=====\n\n[![hex.pm version](https://img.shields.io/hexpm/v/evel.svg)](https://hex.pm/packages/evel)\n[![Build Status](https://travis-ci.org/sile/evel.svg?branch=master)](https://travis-ci.org/sile/evel)\n[![Code Coverage](https://codecov.io/gh/sile/evel/branch/master/graph/badge.svg)](https://codecov.io/gh/sile/evel/branch/master)\n[![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)\n\n`evel` is a distributed leader election library which has eventual consistency.\n\nIf the nodes which have started `evel` application eventually agree with the same member list (usually distributed erlang ensures it),\neach election will elect a single leader who is recognized by all of them.\n\nIn the quiescent state (e.g., there are no joining nodes to the cluster),\nthe cost of an election is `O(M)` where `M` is the number of members (≈  nodes) who are interested in the election.\nIt is unaffected by the cluster size to which they belong.\n\n`evel` provides weaker consistency than [global](http://erlang.org/doc/man/global.html) which can be regarded as OTP's leader election module.\nBut it is more scalable and has higher availability (e.g., more tolarent to timing failures of some nodes in the same cluster).\n\nSee [Comparison with the global module](#comparison-with-the-global-module) for more details.\n\nBuild\n-----\n\nTo build the library for stand-alone usage:\n```sh\n$ git clone https://github.com/sile/evel.git\n$ cd evel\n$ ./rebar3 compile\n$ ./rebar3 shell\n$ \u003e evel:module_info().\n```\n\nIf you want to use from your application:\n```erlang\n%% In your 'rebar.config'\n\n%% Add following lines\n{deps,\n [\n   evel\n ]}.\n```\n\nExample\n-------\n\n```erlang\n$ ./rebar3 shell --sname master\n\n%%\n%% Starts 10 slave nodes on the local machine\n%%\n\u003e ok = evel_debug:slave_start_link_n(10).\n\u003e nodes().\n['1@localhost','2@localhost','3@localhost','4@localhost',\n'5@localhost','6@localhost','7@localhost','8@localhost',\n'9@localhost','10@localhost']\n\n%%\n%% Elects the leader from among following candidates\n%%\n\u003e Candidates = [spawn(timer, sleep, [infinity]) || _ \u003c- lists:seq(1, 100)].\n\u003e rpc:parallel_eval([{evel, elect, [foo, Candidate]} || Candidate \u003c- Candidates]).\n\n%%\n%% Finds the elected leader\n%%\n\u003e {ok, Leader} = evel:find_leader(foo).\n\u003e {Results0, []} = rpc:multicall(evel, find_leader, [foo]).\n\u003e lists:foreach(fun (R) -\u003e {ok, Leader} = R end, Results0). % All nodes agree with the single leader\n\n%%\n%% Dismisses the leader\n%%\n\u003e ok = evel:dismiss(Leader).\n\u003e error = evel:find_leader(foo).\n\u003e {Results1, []} = rpc:multicall(evel, find_leader, [foo]).\n\u003e lists:foreach(fun (R) -\u003e error = R end, Results1).\n```\n\nAPI\n---\n\nSee [EDoc Documents](https://hexdocs.pm/evel/)\n\nComparison with the global module\n---------------------------------\n\nThe following examples depict some differences between `evel` and `global`.\n\n```erlang\n$ ./rebar3 shell --sname master\n%%%\n%%% SETUP\n%%%\n\n%% Starts 100 slave nodes on the local machine\n\u003e ok = evel_debug:slave_start_n(50).\n\u003e nodes().\n['1@localhost','2@localhost','3@localhost','4@localhost',\n'5@localhost','6@localhost','7@localhost','8@localhost'|...]\n\n%% A auxiliary function\n\u003e RpcMultiApply = fun (F) -\u003e {Result, []} = rpc:multicall(erlang, apply, [F, []]), Result end.\n\n\n%%%\n%%% Consistency\n%%%\n\n%% [global]\n%% Every member always agree with a single leader.\n\u003e RpcMultiApply(fun () -\u003e global:register_name(foo, spawn(timer, sleep, [infinity])), global:whereis_name(foo) end).\n\u003e lists:usort(v(-1)).\n[\u003c0.617.0\u003e]\n\n%% [evel]\n%% At the time of conflict, multiple leaders may coexist temporarily.\n\u003e RpcMultiApply(fun () -\u003e evel:elect(foo, spawn(timer, sleep, [infinity])) end).\n\u003e lists:usort(v(-1)).\n[{\u003c10721.296.0\u003e,\u003c10721.298.0\u003e},\n {\u003c10720.298.0\u003e,\u003c10720.300.0\u003e},\n {\u003c10729.307.0\u003e,\u003c10729.309.0\u003e}]\n\n%% But every member eventually agree with a single leader.\n\u003e RpcMultiApply(fun () -\u003e evel:elect(foo, spawn(timer, sleep, [infinity])) end).\n\u003e lists:usort(v(-1)).\n[{\u003c10720.298.0\u003e,\u003c10720.300.0\u003e}]\n\n\n%%\n%% Efficiency (or Scalability)\n%%\n\n%% [global]\n%% All members participate the same election.\n\u003e timer:tc(fun () -\u003e length(RpcMultiApply(fun () -\u003e global:register_name(bar, spawn(timer, sleep, [infinity])) end)) end).\n{7573849,51} % 7.573 seconds\n\n%% Each member participate each election.\n\u003e timer:tc(fun () -\u003e length(RpcMultiApply(fun () -\u003e global:register_name(node(), spawn(timer, sleep, [infinity])) end)) end).\n{9992855,51} % 9.992 seconds\n\n%% [evel]\n%% All members participate the same election.\n\u003e timer:tc(fun () -\u003e length(RpcMultiApply(fun () -\u003e evel:elect(bar, spawn(timer, sleep, [infinity])) end)) end).\n{99444,51} % 0.099 seconds\n\n%% Each member participate each election.\n\u003e timer:tc(fun () -\u003e length(RpcMultiApply(fun () -\u003e evel:elect(node(), spawn(timer, sleep, [infinity])) end)) end).\n{324833,51} % 0.324 seconds\n\n\n%%\n%% Availability\n%%\n\n%% Suspends a slave node\n\u003e os:cmd(\"kill -19 ${A_SLAVE_OS_PID}\").\n\n%% global will block until the node is resumed.\n\u003e global:register_name(baz, self()). % Type `Ctrl+G =\u003e i =\u003e c` to break\n\n%% evel is unaffected by the suspension.\n\u003e evel:elect(baz, self()).\n{\u003c0.715.0\u003e,\u003c0.718.0\u003e}\n\n%% Resumes the slave node\n\u003e os:cmd(\"kill -18 ${A_SLAVE_OS_PID}\").\n\n\n%%\n%% Knowledge\n%%\n\n%% [global]\n%% Every member locally knowns the results of all elections.\n\u003e global:registered_names().\n['8@localhost','6@localhost','23@localhost','1@localhost',\n'16@localhost','43@localhost','14@localhost','42@localhost' | ...]\n\n%% [evel]\n%% Each member locally knows the results of elections in which are interested.\n\u003e evel:known_leaders().\n[{foo,{\u003c10720.298.0\u003e,\u003c10720.300.0\u003e}},\n {master@localhost,{\u003c0.665.0\u003e,\u003c0.667.0\u003e}},\n {bar,{\u003c10735.305.0\u003e,\u003c10735.307.0\u003e}}]\n\n%% If the node want to know the result of other election,\n%% it will fetch the result from some remote nodes.\n\u003e evel:find_leader('1@localhost').\n{ok,{\u003c10663.315.0\u003e,\u003c10663.317.0\u003e}}\n\n\u003e evel:known_leaders().\n[{'1@localhost',{\u003c10663.315.0\u003e,\u003c10663.317.0\u003e}},\n {foo,{\u003c10720.298.0\u003e,\u003c10720.300.0\u003e}},\n {master@localhost,{\u003c0.665.0\u003e,\u003c0.667.0\u003e}},\n {bar,{\u003c10735.305.0\u003e,\u003c10735.307.0\u003e}}]\n```\n\nLicense\n-------\n\nThis library is released under the MIT License.\nSee the [LICENSE](LICENSE) file for full license information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsile%2Fevel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsile%2Fevel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsile%2Fevel/lists"}