{"id":29795458,"url":"https://github.com/rmqtt/rmqtt-raft","last_synced_at":"2025-12-25T10:35:25.144Z","repository":{"id":60921628,"uuid":"492444328","full_name":"rmqtt/rmqtt-raft","owner":"rmqtt","description":"A raft framework, for regular people","archived":false,"fork":false,"pushed_at":"2025-07-14T03:10:05.000Z","size":117,"stargazers_count":39,"open_issues_count":0,"forks_count":9,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-14T06:02:15.078Z","etag":null,"topics":["raft","riteraft","rust","tikv"],"latest_commit_sha":null,"homepage":"","language":"Rust","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/rmqtt.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,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-05-15T09:40:52.000Z","updated_at":"2025-07-14T03:07:42.000Z","dependencies_parsed_at":"2024-09-07T09:48:32.524Z","dependency_job_id":"f5bd441a-40de-4131-a99c-d642309b716e","html_url":"https://github.com/rmqtt/rmqtt-raft","commit_stats":{"total_commits":57,"total_committers":2,"mean_commits":28.5,"dds":0.01754385964912286,"last_synced_commit":"2f9a1041f0428fb0f501cbcfdba61ab3a195e2aa"},"previous_names":[],"tags_count":27,"template":false,"template_full_name":null,"purl":"pkg:github/rmqtt/rmqtt-raft","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmqtt%2Frmqtt-raft","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmqtt%2Frmqtt-raft/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmqtt%2Frmqtt-raft/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmqtt%2Frmqtt-raft/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rmqtt","download_url":"https://codeload.github.com/rmqtt/rmqtt-raft/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rmqtt%2Frmqtt-raft/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267459928,"owners_count":24090782,"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","status":"online","status_checked_at":"2025-07-28T02:00:09.689Z","response_time":68,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["raft","riteraft","rust","tikv"],"created_at":"2025-07-28T04:11:03.125Z","updated_at":"2025-12-25T10:35:25.085Z","avatar_url":"https://github.com/rmqtt.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# RmqttRaft - A raft framework, for regular people\n\n\u003ca href=\"https://github.com/rmqtt/rmqtt-raft/releases\"\u003e\u003cimg alt=\"GitHub Release\" src=\"https://img.shields.io/github/release/rmqtt/rmqtt-raft?color=brightgreen\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://crates.io/crates/rmqtt-raft\"\u003e\u003cimg alt=\"crates.io\" src=\"https://img.shields.io/crates/v/rmqtt-raft\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://docs.rs/rmqtt-raft\"\u003e\u003cimg alt=\"Documentation\" src=\"https://docs.rs/rmqtt-raft/badge.svg\" /\u003e\u003c/a\u003e\n\nThis is an attempt to create a layer on top of\n[tikv/raft-rs](https://github.com/tikv/raft-rs), that is easier to use and implement. This is not supposed to be the\nmost featureful raft, but instead a convenient interface to get started quickly, and have a working raft in no time.\n\nThe interface is strongly inspired by the one used by [canonical/raft](https://github.com/canonical/raft).\n\n## Usage\n\nAdd this to your `Cargo.toml`:\n\n```toml\n[dependencies]\nrmqtt-raft = \"0.4\"\n```\n\n## Getting started\n\nIn order to \"raft\" storage, we need to implement the `Storage` trait for it. Bellow is an example with `HashStore`,\nwhich is a thread-safe wrapper around an\n`HashMap`:\n\n```rust\n#[derive(Serialize, Deserialize)]\npub enum Message {\n    Insert { key: String, value: String },\n    Get { key: String },\n}\n\n#[derive(Clone)]\nstruct HashStore(Arc\u003cRwLock\u003cHashMap\u003cString, String\u003e\u003e\u003e);\n\nimpl HashStore {\n    fn new() -\u003e Self {\n        Self(Arc::new(RwLock::new(HashMap::new())))\n    }\n    fn get(\u0026self, key: \u0026str) -\u003e Option\u003cString\u003e {\n        self.0.read().unwrap().get(key).cloned()\n    }\n}\n\n#[async_trait]\nimpl Store for HashStore {\n    async fn apply(\u0026mut self, message: \u0026[u8]) -\u003e RaftResult\u003cVec\u003cu8\u003e\u003e {\n        let message: Message = deserialize(message).unwrap();\n        let message: Vec\u003cu8\u003e = match message {\n            Message::Insert { key, value } =\u003e {\n                let mut db = self.0.write().unwrap();\n                let v = serialize(\u0026value).unwrap();\n                db.insert(key, value);\n                v\n            }\n            _ =\u003e Vec::new(),\n        };\n        Ok(message)\n    }\n\n    async fn query(\u0026self, query: \u0026[u8]) -\u003e RaftResult\u003cVec\u003cu8\u003e\u003e {\n        let query: Message = deserialize(query).unwrap();\n        let data: Vec\u003cu8\u003e = match query {\n            Message::Get { key } =\u003e {\n                if let Some(val) = self.get(\u0026key) {\n                    serialize(\u0026val).unwrap()\n                } else {\n                    Vec::new()\n                }\n            }\n            _ =\u003e Vec::new(),\n        };\n        Ok(data)\n    }\n\n    async fn snapshot(\u0026self) -\u003e RaftResult\u003cVec\u003cu8\u003e\u003e {\n        Ok(serialize(\u0026self.0.read().unwrap().clone())?)\n    }\n\n    async fn restore(\u0026mut self, snapshot: \u0026[u8]) -\u003e RaftResult\u003c()\u003e {\n        let new: HashMap\u003cString, String\u003e = deserialize(snapshot).unwrap();\n        let mut db = self.0.write().unwrap();\n        let _ = std::mem::replace(\u0026mut *db, new);\n        Ok(())\n    }\n}\n\n```\n\nOnly 4 methods need to be implemented for the Store:\n\n- `Store::apply`: applies a commited entry to the store.\n- `Store::query`  query a entry from the store;\n- `Store::snapshot`: returns snapshot data for the store.\n- `Store::restore`: applies the snapshot passed as argument.\n\n### running the raft\n\n```rust\n#[tokio::main]\nasync fn main() -\u003e std::result::Result\u003c(), Box\u003cdyn std::error::Error\u003e\u003e {\n    let decorator = slog_term::TermDecorator::new().build();\n    let drain = slog_term::FullFormat::new(decorator).build().fuse();\n    let drain = slog_async::Async::new(drain).build().fuse();\n    let logger = slog::Logger::root(drain, slog_o!(\"version\" =\u003e env!(\"CARGO_PKG_VERSION\")));\n\n    // converts log to slog\n    #[allow(clippy::let_unit_value)]\n        let _log_guard = slog_stdlog::init().unwrap();\n\n    let options = Options::from_args();\n    let store = HashStore::new();\n    info!(logger, \"peer_addrs: {:?}\", options.peer_addrs);\n    let cfg = Config {\n        reuseaddr: true,\n        reuseport: true,\n        // grpc_message_size: 50 * 1024 * 1024,\n        ..Default::default()\n    };\n    let raft = Raft::new(\n        options.raft_laddr.clone(),\n        store.clone(),\n        logger.clone(),\n        cfg,\n    )?;\n    let leader_info = raft.find_leader_info(options.peer_addrs).await?;\n    info!(logger, \"leader_info: {:?}\", leader_info);\n\n    let mailbox = Arc::new(raft.mailbox());\n    let (raft_handle, mailbox) = match leader_info {\n        Some((leader_id, leader_addr)) =\u003e {\n            info!(logger, \"running in follower mode\");\n            let handle = tokio::spawn(raft.join(\n                options.id,\n                options.raft_laddr,\n                Some(leader_id),\n                leader_addr,\n            ));\n            (handle, mailbox)\n        }\n        None =\u003e {\n            info!(logger, \"running in leader mode\");\n            let handle = tokio::spawn(raft.lead(options.id));\n            (handle, mailbox)\n        }\n    };\n    \n    tokio::try_join!(raft_handle)?.0?;\n    Ok(())\n}\n```\n\nThe `mailbox` gives you a way to interact with the raft, for sending a message, or leaving the cluster for example.\n\n## Credit\n\nThis work is based on  [riteraft](https://github.com/ritelabs/riteraft), but more adjustments and improvements have been\nmade to the code .\n\n## License\n\nThis library is licensed under either of:\n\n* MIT license [LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT\n* Apache License 2.0 [LICENSE-APACHE](LICENSE-APACHE) or https://opensource.org/licenses/Apache-2.0\n\nat your option.\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frmqtt%2Frmqtt-raft","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frmqtt%2Frmqtt-raft","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frmqtt%2Frmqtt-raft/lists"}