{"id":38886083,"url":"https://github.com/shaj13/raft","last_synced_at":"2026-01-17T14:47:03.726Z","repository":{"id":37651051,"uuid":"345303752","full_name":"shaj13/raft","owner":"shaj13","description":"raft is a golang library that provides a simple, clean, and idiomatic implementation of the Raft consensus protocol","archived":false,"fork":false,"pushed_at":"2025-05-05T17:51:21.000Z","size":180,"stargazers_count":133,"open_issues_count":6,"forks_count":18,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-05T18:33:45.988Z","etag":null,"topics":["consensus","go","golang","raft","raft-algorithm","raft-consensus-algorithm","raft-protocol","write-ahead-log"],"latest_commit_sha":null,"homepage":"","language":"Go","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/shaj13.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,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2021-03-07T09:18:47.000Z","updated_at":"2025-05-05T17:51:25.000Z","dependencies_parsed_at":"2024-04-23T12:10:20.926Z","dependency_job_id":"69c12e48-0f79-4755-88c2-4f525ac67704","html_url":"https://github.com/shaj13/raft","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/shaj13/raft","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shaj13%2Fraft","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shaj13%2Fraft/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shaj13%2Fraft/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shaj13%2Fraft/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shaj13","download_url":"https://codeload.github.com/shaj13/raft/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shaj13%2Fraft/sbom","scorecard":{"id":815278,"data":{"date":"2025-08-11","repo":{"name":"github.com/shaj13/raft","commit":"a5e55c36efbb0c2f01a2e978716ca32c59513cb2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.3,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":2,"reason":"Found 3/14 approved changesets -- score normalized to 2","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 12 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-23T14:09:09.134Z","repository_id":37651051,"created_at":"2025-08-23T14:09:09.134Z","updated_at":"2025-08-23T14:09:09.134Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28510905,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-17T13:38:16.342Z","status":"ssl_error","status_checked_at":"2026-01-17T13:37:44.060Z","response_time":85,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["consensus","go","golang","raft","raft-algorithm","raft-consensus-algorithm","raft-protocol","write-ahead-log"],"created_at":"2026-01-17T14:47:03.661Z","updated_at":"2026-01-17T14:47:03.717Z","avatar_url":"https://github.com/shaj13.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![GoDoc](https://godoc.org/github.com/shaj13/raft?status.svg)](https://pkg.go.dev/github.com/shaj13/raft)\n[![Go Report Card](https://goreportcard.com/badge/github.com/shaj13/raft)](https://goreportcard.com/report/github.com/shaj13/raft)\n[![Coverage Status](https://coveralls.io/repos/github/shaj13/raft/badge.svg?branch=master)](https://coveralls.io/github/shaj13/raft?branch=master)\n[![CircleCI](https://circleci.com/gh/shaj13/raft/tree/main.svg?style=svg)](https://circleci.com/gh/shaj13/raft/tree/main)\n\n## Overview \nRaft is a protocol with which a cluster of nodes can maintain a replicated state machine.\nThe state machine is kept in sync through the use of a replicated log.\nHowever, The details of the Raft protocol are outside the scope of this document,\nFor more details on Raft, see [In Search of an Understandable Consensus Algorithm](https://raft.github.io/raft.pdf)\n\n\n## Why another library? \nRaft algorithm comes in search of an understandable consensus algorithm, unfortunately, most of the go libraries out there required a deep knowledge of their implementation and APIs. \n\nThis raft library was born to align with the understandability raft principle and its sole purpose is to provide consensus with the minimalistic, simple, clean, and idiomatic API. \n\nEtcd Raft is the most widely used Raft library in production\nBut, it follows a minimalistic design philosophy by only implementing the core raft algorithm which leaves gaps and ambiguities.\n\nSo, instead of reinventing the wheel, this library uses etcd raft as its core.\n\nThat's how you can benefit from the power and stability of etcd raft, with an understandable API. indeed, it keeps your focus on building awesome software. \n\nFinally, the raft library aimed to be used internally but it is worth being exposed to the public.\n\n## Features\n\nThis raft implementation is a full feature implementation of Raft protocol. Features includes:\n\n- Mange Multi-Raft \n- Coalesced heartbeats to reduce the overhead of heartbeats when there are a large number of raft groups\n- Leader election\n- Log replication\n- Log compaction\n- Pre-Vote Protocol \n- Membership changes\n  - add member \n  - remove member\n  - update member \n  - promote member\n  - demote member\n- Leadership transfer extension\n- Efficient linearizable read-only queries served by both the leader and followers\n  - leader checks with quorum and bypasses Raft log before processing read-only queries\n  - followers asks leader to get a safe read index before processing read-only queries\n- More efficient lease-based linearizable read-only queries served by both the leader and followers\n  - leader bypasses Raft log and processing read-only queries locally\n  - followers asks leader to get a safe read index before processing read-only queries\n  - this approach relies on the clock of the all the machines in raft group\n- Snapshots \n  - automatic snapshots when the log store reaches a certain length \n  - API to force new snapshot \n- Read-Only Members\n  - learner member \n  - staging member\n- Segmented WAL to provide durability and ensure data integrity\n- Garbage collector to controls how many WAL and snapshot files are retained\n- Network transport to communicate with Raft on remote machines\n  - gRPC (recommended)\n  - http \n- gRPC chunked transfer encoding\n- Network [Pipelining](https://developer.mozilla.org/en-US/docs/Web/HTTP/Connection_management_in_HTTP_1.x#http_pipelining)\n- Restore cluster quorum and data \n  - force new cluster from the existing WALL and snapshot\n  - restore the cluster from snapshot\n- Optimistic pipelining to reduce log replication latency\n- Flow control for log replication\n- Batching Raft messages to reduce synchronized network I/O calls\n- Batching log entries to reduce disk synchronized I/O\n- Writing to leader's disk in parallel\n- Internal proposal redirection from followers to leader\n- Automatic stepping down when the leader loses quorum\n- Protection against unbounded log growth when quorum is lost\n\n## WAL's and snapshots\n\nThere are two sets of files on disk that provide persistent state for Raft.\nThere is a set of WAL (write-ahead log files). These store a series of log\nentries and Raft metadata, such as the current term, index, and committed index.\nWAL files are automatically rotated when they reach a certain size.\n\nTo avoid having to retain every entry in the history of the log, snapshots\nserialize a view of the state at a particular point in time. After a snapshot\ngets taken, logs that predate the snapshot are no longer necessary, because the\nsnapshot captures all the information that's needed from the log up to that\npoint. The number of old snapshots and WALs to retain is configurable.\n\nWALs mostly contain protobuf-serialized user data store\nmodifications. A log entry can contain a batch of creations, updates, and\ndeletions of objects from the user data store. Some log entries contain other kinds\nof metadata, like node additions or removals. Snapshots contain a complete dump\nof the store, as well as any metadata from the log entries that needs to be\npreserved. The saved metadata includes the Raft term and index, a list of nodes\nin the cluster, and a list of nodes that have been removed from the cluster.\n\n## Raft IDs\nThe library uses integers to identify Raft nodes. The Raft IDs may assigned dynamically \nwhen a node joins the Raft consensus group, or it can be defined manually by the user.\n\nIt's important to note that a Raft ID can't be reused after a node that was\nusing the ID leaves the consensus group. These Raft IDs of nodes that are no\nlonger part of the cluster are saved (persisted on disk) as part of the nodes\npool members to make sure they aren't reused. If a node with a removed Raft ID \ntries to use Raft RPCs, other nodes won't honor these requests.\n\nThe removed node's IDs are used to restrict these nodes from\ncommunicating, affecting the cluster state and avoid ambiguity.\n\n## Initializing a Raft cluster\nThe first member of a cluster assigns itself a random Raft ID unless it pre-defined.\nIt creates a new WAL with its own Raft identity stored in the metadata field.\nThe metadata field is the only part of the WAL that differs between nodes. By\nstoring information such as the local Raft ID, it's easy to restore this\nnode-specific information after a restart. In principle it could be stored in a\nseparate file, but embedding it inside the WAL is most convenient.\n\nThe node then starts the Raft state machine. From this point, it's a fully\nfunctional single-node Raft instance. Writes to the data store actually go\nthrough Raft, though this is a trivial case because reaching consensus doesn't\ninvolve communicating with any other nodes.\n\n## Joining a Raft cluster \nNew nodes can join an existing Raft consensus group by invoking the `Join` RPC\non any Raft member if proposal forwarding is enabled, Otherwise,\n`Join` RPC must be invoked on the leader. If successful, `Join` returns a\nRaft ID for the new node and a list of other members of the consensus group.\n\nOn the leader side, `Join` tries to append a configuration change entry to the\nRaft log, and waits until that entry becomes committed.\n\nA new node creates an empty Raft log with its own node information in the\nmetadata field. Then it starts the state machine. By running the Raft consensus\nprotocol, the leader will discover that the new node doesn't have any entries in\nits log, and will synchronize these entries to the new node through some\ncombination of sending snapshots and log entries. It can take a little while for\na new node to become a functional member of the consensus group, because it\nneeds to receive this data first.\n\nThe new node can join the cluster as a Voter, Learner, or Staging member.\n\n## Initializing a predefined Raft cluster\nThe library also provides a mechanism to initialize and boot the Raft cluster\nfrom a predefined members configuration. This is done by applying the same \nconfigurations to all members even for the later joining member. \n\nEach member of the cluster use its predefined id and creates a new \nWAL with its own Raft identity stored in the metadata field. The member\nnode then starts the Raft state machine.\n\nOnce all members nodes are started the election process will be initiated,\nand when all members agrees on the same leader the cluster becomes fully functional,\nWrites to the data store actually go through Raft and it considered complete after reaching a majority.\n\n## Usage \nThe primary object in raft is a Node. Either start a Node from scratch using `raft.WithInitCluster()`, `raft.WithJoin()` or start a Node from some initial state using `raft.WithRestart()`.\n\nTo start a three-node cluster from predefined configuration: \n\n**Node A** \n```go\nm1 := raft.RawMember{ID: 1, Address: \":8081\"}\nm2 := raft.RawMember{ID: 2, Address: \":8082\"}\nm3 := raft.RawMember{ID: 3, Address: \":8083\"}\nnode := raft.NewNode(\u003cFSM\u003e, \u003cTransport\u003e, \u003cOpts\u003e)\n// The first member should reference to the current effective member.\nnode.Start(raft.WithInitCluster(), raft.WithMembers(m1, m2, m3))\n```\n\n**Node B** \n```go\nm1 := raft.RawMember{ID: 1, Address: \":8081\"}\nm2 := raft.RawMember{ID: 2, Address: \":8082\"}\nm3 := raft.RawMember{ID: 3, Address: \":8083\"}\nnode := raft.NewNode(\u003cFSM\u003e, \u003cTransport\u003e, \u003cOpts\u003e)\n// The first member should reference to the current effective member.\nnode.Start(raft.WithInitCluster(), raft.WithMembers(m2, m1, m3))\n```\n\n**Node C** \n```go\nm1 := raft.RawMember{ID: 1, Address: \":8081\"}\nm2 := raft.RawMember{ID: 2, Address: \":8082\"}\nm3 := raft.RawMember{ID: 3, Address: \":8083\"}\nnode := raft.NewNode(\u003cFSM\u003e, \u003cTransport\u003e, \u003cOpts\u003e)\n// The first member should reference to the current effective member.\nnode.Start(raft.WithInitCluster(), raft.WithMembers(m3, m1, m2))\n```\n\n\nStart a single node cluster, like so:\n```go\nm := raft.RawMember{ID: 1, Address: \":8081\"}\nopt := raft.WithMembers(m)\n// or \nopt = raft.WithAddress(\":8081\")\nnode := raft.NewNode(\u003cFSM\u003e, \u003cTransport\u003e, \u003cOpts\u003e)\nnode.Start(raft.WithInitCluster(), opt)\n```\n\nTo allow a new node to join a cluster, like so:\n```go\nm := raft.RawMember{ID: 2, Address: \":8082\"}\nopt := raft.WithMembers(m)\n// or \nopt = raft.WithAddress(\":8082\")\nnode := raft.NewNode(\u003cFSM\u003e, \u003cTransport\u003e, \u003cOpts\u003e)\nnode.Start(raft.WithJoin(\":8081\", time.Second), opt)\n```\n\nTo restart a node from previous state:\n```go\nnode := raft.NewNode(\u003cFSM\u003e, \u003cTransport\u003e, \u003cOpts\u003e)\nnode.Start(raft.WithRestart())\n```\n\nTo force new cluster:\n```go\nnode := raft.NewNode(\u003cFSM\u003e, \u003cTransport\u003e, \u003cOpts\u003e)\n// This will use the latest wal and snapshot.\nnode.Start(raft.WithForceNewCluster())\n```\n\nTo restore from snapshot: \n```go \nnode := raft.NewNode(\u003cFSM\u003e, \u003cTransport\u003e, \u003cOpts\u003e)\nnode.Start(raft.WithRestore(\"\u003cpath to snapshot file\u003e\"))\n```\n\n## Examples and docs\n - More detailed development documentation can be found in [go docs](https://pkg.go.dev/github.com/shaj13/raft)\n - Fully working single and multiraft cluster example can be found in [Examples Folder](./_examples).\n\n## Contributing to this project\nWe welcome contributions. If you find any bugs, potential flaws and edge cases, improvements, new feature suggestions or discussions, please submit issues or pull requests.\n\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshaj13%2Fraft","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshaj13%2Fraft","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshaj13%2Fraft/lists"}