{"id":19691513,"url":"https://github.com/kbrw/supervisorring","last_synced_at":"2025-04-29T09:31:06.136Z","repository":{"id":11926278,"uuid":"14493944","full_name":"kbrw/supervisorring","owner":"kbrw","description":"otp/supervisor-like interface to supervise distributed processes","archived":false,"fork":false,"pushed_at":"2020-11-08T22:27:56.000Z","size":73,"stargazers_count":16,"open_issues_count":0,"forks_count":4,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-04-05T14:34:52.650Z","etag":null,"topics":["dht","elixir","otp","supervisor"],"latest_commit_sha":null,"homepage":"","language":"Elixir","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/kbrw.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":"2013-11-18T14:00:48.000Z","updated_at":"2023-09-01T10:53:28.000Z","dependencies_parsed_at":"2022-09-18T23:13:28.424Z","dependency_job_id":null,"html_url":"https://github.com/kbrw/supervisorring","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbrw%2Fsupervisorring","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbrw%2Fsupervisorring/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbrw%2Fsupervisorring/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kbrw%2Fsupervisorring/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kbrw","download_url":"https://codeload.github.com/kbrw/supervisorring/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251473212,"owners_count":21595023,"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":["dht","elixir","otp","supervisor"],"created_at":"2024-11-11T19:09:36.602Z","updated_at":"2025-04-29T09:31:05.818Z","avatar_url":"https://github.com/kbrw.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"supervisorring\n==============\n\nSupervisorring exposes the same module and behaviour as an erlang OTP supervisor but\ndistributed into several nodes monitored using (https://github.com/awetzel/nano_ring ):\n- each child is started on different up nodes according to the mapping of the\n  child id on a consistent hashing DHT\n- when the up node cluster change (NanoRing event), on each node, according to\n  the new DHT : moved in processes are started, moved out processes are killed,\n  and a state migration happened before the kill if possible\n- when child die repeatedly, a local supervisor is restarted to try a local\n  state restoration, if this local supervisor die repeatedly, then\n  all the supervisors on all nodes are killed , then let the parent\n  supervisor of the supervisorring supervisor handle the global state\n  restoration (like with a classical otp supervisor)\n\n## Usage ##\n\nThe `supervisorring` module and behaviour can be use nearly the same way as the\nOTP/erlang :supervisor module and behaviour with the following differences :\n- `terminate_child` only stops child locally, if the child is moved on another\n  node then a terminated child will be restarted.\n- children cannot be `transient` or `temporary`, they have to be `permanent`\n  for the reason above.\n- children are not necessary started in the given order.\n- the supervisor has to have locally registered name, so the `start_link/2`\n  does not exist and `start_link/3` needs {:local,NAME} as first parameter.\n- in addition to the supervisor `init/1` function, :supervisorring behaviour\n  needs you to implement a `migrate/3` function which will be called if\n  possible before to kill a child on a node where it doesnot belong anymore.\n- in order to allow the use of dynamic child management (`start_child /delete_child`), \n  you need to maintain a global list of children. To allow that, a new\n  `child_spec` can be used in `init/1` child list : `{:dyn_child_handler,YourModule}`.\n  `YourModule` has to implement the behaviour `:dyn_child_handler` :\n  - `add(::childspec)-\u003e:ok,del(::childid)-\u003e:ok` callbacks will be used\n     respectively after a successful `start_child` and `delete_child`\n  - `get_all()-\u003e[::childspec]` callback will be called during ring migration in\n     order to get the current global list of children to determine which one\n     has to be started locally.\n  - `match(::childid)-\u003eboolean` allows you to use multiple `:dyn_child_handler`\n     in childspecs and during addition and deletion, the correct handler will be\n     selected if the child id match according to this function.\n- in addition to supervisor function, supervisorring exposes two necessary new functions :\n  - `find(supname,childid)` allows you to find the node where a child belong. Indeed, \n     as a supervisor, a supervisorring does not give you a particular way to locate\n     your process among its children, you have to use your own mechanism to locate\n     your process : register, pid communication, etc... BUT if you just use\n     locally registered pid, you need to know the node where the child run for\n     instance if the registered name is also the child id in sup :\n     `:gen_server.call({ChildName,:supervisorring.find(ChildSup,ChildName)},:you_call)`\n  - the local ring may be unaware of the last ring update and\n    :supervisorring.find can give you a down node if this one just crashed. To\n    ensure that your code executes himself on the node where a given child\n    is currently running, you can use `exec(supname,childid,youfun)` \n- finally, the local supervisor is registered with the name given to the\n  supervisorring, so you can use for instance :\n  `:supervisorring.which_children(MySup)` to get all the children of the distributed supervisor and\n  `:supervisor.which_children(MySup)` to get the local children associate with this supervisor.\n\nThe `:dyn_child_handler` can maintain the child list globally with an external\ndatabase shared by every cluster nodes (network fs,mnesia,riak,etc.).\n\nExample : \n\n```elixir\ndefmodule MySup do\n  @behaviour :supervisorring\n  def migrate({_id,_type,_modules},old_server,new_server), do:\n    :gen_server.cast(new_server,{:set_state,:gen_server.call(old_server,:get_state)})\n  def init(_arg) do\n    {:ok,{{:one_for_one,2,3},[\n      {:dyn_child_handler,NetFSChildHandler},\n      {MySup.C1,{:gen_server,:start_link,[{:local,MySup.C1},GenericServer,nil,[]]},:permanent,2,:worker,[GenericServer]},\n      {MySup.C2,{:gen_server,:start_link,[{:local,MySup.C2},GenericServer,nil,[]]},:permanent,2,:worker,[GenericServer]}\n    ]}}\n  end\nend\n# if childs file is shared on every node with a shared fs :\ndefmodule NetFSChildHandler do\n  @behaviour :dyn_child_handler\n  def match(_), do: true\n  def get_all, do:\n    File.read!(\"childs\")|\u003ebinary_to_term\n  def add(childspec), do:\n    File.write!(\"childs\",File.read!(\"childs\") |\u003e binary_to_term |\u003e List.insert_at(0,childspec) |\u003e term_to_binary)\n  def del(childid), do:\n    File.write!(\"childs\",File.read!(\"childs\") |\u003e binary_to_term |\u003e List.keydelete(childid,0) |\u003e term_to_binary)\nend\n\n:supervisorring.start_link({:local,MySup},MySup,nil)\nc3 = {MySup.C3,{:gen_server,:start_link,[{:local,MySup.C3},GenericServer,nil,[]]},:permanent,2,:worker,[GenericServer]}\n:supervisorring.start_child(MySup,c3)\n:gen_server.call({MySup.C3,:supervisorring.find(MySup,MySup.C3)},:youcall)\n# get all childs\n:supervisorring.which_children(MySup)\n# get local childs\n:supervisor.which_children(MySup)\n```\n\n## How does it work ? ##\n\n    Supervisorring.App.Sup-\u003e .Events\n                          -\u003e .SuperSup -\u003e NodesListener\n    your_app -\u003e your_sup_tree -\u003e your_supervisorring -\u003e your_childs\n    actually starts the following supervision tree :\n    your_app -\u003e your_sup_tree -\u003e global_sup -\u003e local_sup(children=your_childs in dht(localnode)))\n                                            -\u003e child_manager -\u003e ring_event_handler\n\nThe application `SuperSup gen_server` listens nanoring events to\nupdate its consistant hashing dht, then notify all the local\nSupervisiorring Global Supervisor with a `gen_event`.\n\nThe `ChildManager gen_server` start or kill the local supervisor\nchildren according to the new DHT.\n\nThe local children are supervised by 2 parent supervisor, `global_sup -\u003e\nlocal_sup -\u003e children` so that :\n- if a child die it is restarted by `local_sup` as a classical supervised process\n- if the child die repeatedly (according to defined MaxR,MaxT), `local_sup` will die\n- if `local_sup` die, it is restarted by `global_sup` to \"test\" if\n  local children state restoration is sufficient to ensure the viability\n  of the children\n- if `local_sup` die, repeatedly, according to MaxR=2 MaxT=`LocalMaxT*2`,\n  then `global_sup` will die\n- `super_sup` monitors `global_sup` so that when `global_sup` exits for anormal\n   reason, it send `exit(global_sup_ref,kill)` to all nodes in order to kill the\n   supervisor \"globally\" (as the supervisor maintains the list of children\n   globally, the working child state may need a global children restart)\n\nThis way children supervised by `supervisorring` can be supervised\nglobally in a similar fashion as `local_supervision` locally.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkbrw%2Fsupervisorring","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkbrw%2Fsupervisorring","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkbrw%2Fsupervisorring/lists"}