{"id":13504419,"url":"https://github.com/elixir-toniq/raft","last_synced_at":"2025-12-12T00:10:35.142Z","repository":{"id":57539320,"uuid":"120200744","full_name":"elixir-toniq/raft","owner":"elixir-toniq","description":"An Elixir implementation of the raft consensus protocol","archived":false,"fork":false,"pushed_at":"2019-10-13T17:45:42.000Z","size":188,"stargazers_count":430,"open_issues_count":8,"forks_count":29,"subscribers_count":26,"default_branch":"master","last_synced_at":"2025-03-30T13:09:59.237Z","etag":null,"topics":["distributed-systems","elixir","raft"],"latest_commit_sha":null,"homepage":"","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elixir-toniq.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":"2018-02-04T16:12:38.000Z","updated_at":"2025-03-26T05:47:35.000Z","dependencies_parsed_at":"2022-08-26T19:50:27.663Z","dependency_job_id":null,"html_url":"https://github.com/elixir-toniq/raft","commit_stats":null,"previous_names":["keathley/raft","toniqsystems/raft"],"tags_count":1,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-toniq%2Fraft","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-toniq%2Fraft/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-toniq%2Fraft/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-toniq%2Fraft/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elixir-toniq","download_url":"https://codeload.github.com/elixir-toniq/raft/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247500469,"owners_count":20948880,"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":["distributed-systems","elixir","raft"],"created_at":"2024-08-01T00:00:37.340Z","updated_at":"2025-12-12T00:10:35.099Z","avatar_url":"https://github.com/elixir-toniq.png","language":"Elixir","funding_links":[],"categories":["Components"],"sub_categories":["Vectors"],"readme":"# Raft\n\nRaft provides users with an api for building consistent (as defined by CAP),\ndistributed state machines. It does this using the raft leader election and\nconsensus protocol as described in the [original\npaper](https://raft.github.io/raft.pdf). Logs are persisted using rocksdb\nbut Raft provides a pluggable storage adapter for utilizing other storage\nengines.\n\n## Installation\n\n```elixir\ndef deps do\n  [\n    {:raft, \"~\u003e 0.2.1\"},\n  ]\nend\n```\n\n## Example\n\nLets build a distributed key value store. The first thing that we'll need\nis a state machine:\n\n```elixir\ndefmodule KVStore do\n  use Raft.StateMachine\n\n  @initial_state %{}\n\n  def write(name, key, value) do\n    Raft.write(name, {:set, key, value})\n  end\n\n  def read(name, key) do\n    Raft.read(name, {:get, key})\n  end\n\n  def init(_name) do\n    @initial_state\n  end\n\n  def handle_write({:set, key, value}, state) do\n    {{:ok, key, value}, put_in(state, [key], value)}\n  end\n\n  def handle_read({:get, key}, state) do\n    case get_in(state, [key]) do\n      nil -\u003e\n        {{:error, :key_not_found}, state}\n\n      value -\u003e\n        {{:ok, value}, state}\n    end\n  end\nend\n```\n\nYou can automatically start a peer as part of your supervision tree as shown below. As shown here,\nwhen the supervisor starts, a new peer will be started with the given name. You can provide additional\noptions and they will be used to customize the default config for the peer, for example, you can change\nthe data directory with the `:data_dir` option.\n\n```elixir\ndefmodule KVStoreSupervisor do\n  use Supervisor\n  \n  def start_link(args), do: Supervisor.start_link(__MODULE__, name: __MODULE__)\n  \n  def init(_args) do\n    children = [\n      {KVStore, [name: :\"kvstore_#{Node.self}\"]}\n    ]\n    Supervisor.init(children, strategy: :one_for_one)\n  end\nend\n```\n\nFor the rest of this example however, we will assume you are manually starting/configuring\npeers with `Raft.start_peer/2`, rather than starting them as part of your supervision tree.\n\nNow we can start our peers. Its important to note that each peer must be\ngiven a unique name within the cluster. In this example we'll create\nthree codes with shortnames `a`, `b`, and `c`. The Raft peers on these\nnodes are called `peer1`, `peer2`, and `peer3`.,\n\n```elixir\n$ iex --sname a -S mix\niex(a@mymachine)\u003e {:ok, _pid} = Raft.start_peer(KVStore, name: :peer1)\n\n$ iex --sname b -S mix\niex(b@mymachine)\u003e {:ok, _pid} = Raft.start_peer(KVStore, name: :peer2)\n\n$ iex --sname c -S mix\niex(c@mymachine)\u003e {:ok, _pid} = Raft.start_peer(KVStore, name: :peer3)\n```\n\nAt this point our peers are started but currently they're all in the\n\"follower\" state. In order to get them to communicate we need to define\na cluster configuration for them like so. This needs to be done on\nonly one of the nodes. In our case, we'll run it on node `a`.\n\n```elixir\niex(a@mymachine)\u003e Raft.set_configuration(:peer1,\n            ...\u003e [{ :peer1, :a@mymachine },\n            ...\u003e  { :peer2, :b@mymachine },\n            ...\u003e  { :peer3, :c@mymachine }]\n```\n\nNotice that we have to give both the peer name and the node name, even\nfor the local peer. That's because we store this configuration in the\nreplicated logs, and so they must make sense from all our nodes.\n\nOnce this command runs the peers will start an election and elect\na leader. You can see who the current leader is by running:\n\n```elixir\nleader = Raft.leader(:peer1)\n```\n\nOnce we have the leader we can read and write to our state machine:\n\n```elixir\n{:ok, :foo, :bar} = KVStore.write(leader, :foo, :bar)\n{:ok, :bar}       = KVStore.read(leader, :foo)\n{:error, :key_not_found} = KVStore.read(leader, :baz)\n```\n\nWe can now shutdown our leader and ensure that a new leader has been\nelected and our state is replicated across all of our peers:\n\n```elixir\niex(a@mymachine)\u003e Raft.stop(leader)\n```\n\nTry to use the old leader:\n\n```elixir\niex(a@mymachine)\u003e KVStore.read(leader, :foo)\n{ :error, { :redirect, { :peer3, :c@mymachine }}}\n```\n\nWe're told that the leader has changed.\n\n``` elixir\niex(b@mymachine)\u003e new_leader = Raft.leader(:peer2)\n# or\nnew_leader = { :peer3, :c@mymachine }\n```\n\nAnd use it:\n\n``` elixir\n{:ok, :bar} = KVStore.read(new_leader, :foo)\n```\n\nWe now have a consistent, replicated key-value store. If you want to read more\nabout the internals of the project or read up on the raft protocol please check out\nthe [hex docs](https://hexdocs.pm/raft).\n\n## Caution\n\nThis project is not quite ready for production use. If you would like to help\ntest out the implementation that would be greatly appreciated.\n\n## Contributing\n\nThe goal of this project is to provide the elixir community with\na standard way of building consistent systems. Pull requests and issues\nare very welcome. If you would like to get involved here's some of the\nimmediate needs.\n\n* [ ] - Configuration changes\n* [ ] - Automatic cluster joining\n* [ ] - Snapshotting\n* [ ] - Alternative storage engine using lmdb\n* [ ] - Jepsen testing\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-toniq%2Fraft","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felixir-toniq%2Fraft","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-toniq%2Fraft/lists"}