{"id":19495768,"url":"https://github.com/zarusz/slimcluster","last_synced_at":"2025-10-12T08:14:57.778Z","repository":{"id":39644501,"uuid":"359147280","full_name":"zarusz/SlimCluster","owner":"zarusz","description":"Consensus (Raft) and Memberlist (SWIM) implementation for .NET micro-services","archived":false,"fork":false,"pushed_at":"2023-09-14T20:42:46.000Z","size":277,"stargazers_count":31,"open_issues_count":10,"forks_count":5,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-15T08:18:16.562Z","etag":null,"topics":["cluster","coordination","distributed","distributed-systems","dotnet","memberlist","membership","micro-services","raft","raft-algorithm","swim-protocol"],"latest_commit_sha":null,"homepage":"","language":"C#","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/zarusz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","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}},"created_at":"2021-04-18T13:14:18.000Z","updated_at":"2025-01-08T20:53:01.000Z","dependencies_parsed_at":"2024-11-10T21:38:56.856Z","dependency_job_id":null,"html_url":"https://github.com/zarusz/SlimCluster","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zarusz%2FSlimCluster","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zarusz%2FSlimCluster/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zarusz%2FSlimCluster/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zarusz%2FSlimCluster/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zarusz","download_url":"https://codeload.github.com/zarusz/SlimCluster/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250900405,"owners_count":21505048,"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":["cluster","coordination","distributed","distributed-systems","dotnet","memberlist","membership","micro-services","raft","raft-algorithm","swim-protocol"],"created_at":"2024-11-10T21:38:48.525Z","updated_at":"2025-10-12T08:14:52.727Z","avatar_url":"https://github.com/zarusz.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SlimCluster\n\nSlimCluster has the [Raft](https://raft.github.io/raft.pdf) distributed consensus algorithm implemented in .NET.\nAdditionally, it implements the [SWIM](https://www.cs.cornell.edu/projects/Quicksilver/public_pdfs/SWIM.pdf) cluster membership list (where nodes join and leave/die).\n\n- Membership list is required to maintain what micro-service instances (nodes) constitute a cluster.\n- Raft consensus helps propagate state across the micro-service instances and ensures there is a designated leader instance performing the coordination of work.\n\nThe library goal is to provide a common groundwork for coordination and consensus of your distributed micro-service instances.\nWith that, the developer can focus on the business problem at hand.\nThe library promises to have a friendly API and pluggable architecture.\n\nThe strategic aim for SlimCluster is to implement other algorithms to make distributed .NET micro-services easier and not require one to pull in a load of other 3rd party libraries or products.\n\n[![Gitter](https://badges.gitter.im/SlimCluster/community.svg)](https://gitter.im/SlimCluster/community?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge)\n[![GitHub license](https://img.shields.io/github/license/zarusz/SlimCluster)](https://github.com/zarusz/SlimCluster/blob/master/LICENSE)\n[![Build](https://github.com/zarusz/SlimCluster/actions/workflows/build.yml/badge.svg?branch=master)](https://github.com/zarusz/SlimCluster/actions/workflows/build.yml)\n\n## Roadmap\n\n\u003e This a relatively new project!\n\nThe path to a stable production release:\n\n- :white_check_mark: Step 1: Implement the SWIM membership over UDP + sample.\n- :white_check_mark: Step 2: Documentation on Raft consensus.\n- :white_check_mark: Step 3: Implement the Raft over TCP/UDP + sample.\n- :white_large_square: Step 4: Documentation on SWIM membership.\n- :white_large_square: Step 5: Other extensions and plugins.\n\n## Docs\n\n- [Introduction](docs/intro.md)\n\n## Packages\n\n| Name                                | Description                                | NuGet                                                                                                                                              |\n| ----------------------------------- | ------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `SlimCluster`                       | The core cluster interfaces                | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.svg)](https://www.nuget.org/packages/SlimCluster)                                             |\n| **Core abstractions**               |                                            |                                                                                                                                                    |\n| `SlimCluster.Membership`            | The membership core interfaces             | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.Membership.svg)](https://www.nuget.org/packages/SlimCluster.Membership)                       |\n| `SlimCluster.Serialization`         | The core message serialization interfaces  | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.Serialization.svg)](https://www.nuget.org/packages/SlimCluster.Serialization)                 |\n| `SlimCluster.Transport`             | The core transport interfaces              | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.Transport.svg)](https://www.nuget.org/packages/SlimCluster.Transport)                         |\n| `SlimCluster.Persistence`           | The core node state persistence interfaces | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.Persistence.svg)](https://www.nuget.org/packages/SlimCluster.Persistence)                     |\n| **Plugins**                         |                                            |                                                                                                                                                    |\n| `SlimCluster.Consensus.Raft`        | Raft consensus algorithm implementation    | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.Consensus.Raft.svg)](https://www.nuget.org/packages/SlimCluster.Consensus.Raft)               |\n| `SlimCluster.Membership.Swim`       | SWIM membership algorithm implementation   | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.Membership.Swim.svg)](https://www.nuget.org/packages/SlimCluster.Membership.Swim)             |\n| `SlimCluster.Serialization.Json`    | JSON message serialization plugin          | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.Serialization.Json.svg)](https://www.nuget.org/packages/SlimCluster.Serialization.Json)       |\n| `SlimCluster.Transport.Ip`          | IP protocol transport plugin               | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.Transport.Ip.svg)](https://www.nuget.org/packages/SlimCluster.Transport.Ip)                   |\n| `SlimCluster.Persistence.LocalFile` | Persists node state into a local JSON file | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.Persistence.LocalFile.svg)](https://www.nuget.org/packages/SlimCluster.Persistence.LocalFile) |\n| `SlimCluster.AspNetCore`            | ASP.NET request routing to Leader node     | [![NuGet](https://img.shields.io/nuget/v/SlimCluster.AspNetCore.svg)](https://www.nuget.org/packages/SlimCluster.AspNetCore)                       |\n\n## Samples\n\nCheck out the [Samples](src/Samples/) folder on how to get started.\n\n### Example usage\n\nSetup membership discovery using the SWIM algorithm and consensus using Raft algorithm:\n\n```cs\nbuilder.Services.AddSlimCluster(cfg =\u003e\n{\n    cfg.ClusterId = \"MyCluster\";\n    // This will use the machine name, in Kubernetes this will be the pod name\n    cfg.NodeId = Environment.MachineName;\n\n    // Transport will be over UDP/IP\n    cfg.AddIpTransport(opts =\u003e\n    {\n        opts.Port = builder.Configuration.GetValue\u003cint\u003e(\"UdpPort\");\n        opts.MulticastGroupAddress = builder.Configuration.GetValue\u003cstring\u003e(\"UdpMulticastGroupAddress\")!;\n    });\n\n    // Protocol messages (and logs/commands) will be serialized using JSON\n    cfg.AddJsonSerialization();\n\n    // Cluster state will saved into the local json file in between node restarts\n    cfg.AddPersistenceUsingLocalFile(\"cluster-state.json\");\n\n    // Setup Swim Cluster Membership\n    cfg.AddSwimMembership(opts =\u003e\n    {\n        opts.MembershipEventPiggybackCount = 2;\n    });\n\n    // Setup Raft Cluster Consensus\n    cfg.AddRaftConsensus(opts =\u003e\n    {\n        opts.NodeCount = 3;\n\n        // Use custom values or remove and use defaults\n        opts.LeaderTimeout = TimeSpan.FromSeconds(5);\n        opts.LeaderPingInterval = TimeSpan.FromSeconds(2);\n        opts.ElectionTimeoutMin = TimeSpan.FromSeconds(3);\n        opts.ElectionTimeoutMax = TimeSpan.FromSeconds(6);\n        // Can set a different log serializer, by default ISerializer is used (in our setup its JSON)\n        // opts.LogSerializerType = typeof(JsonSerializer);\n    });\n\n    cfg.AddAspNetCore(opts =\u003e\n    {\n        // Route all ASP.NET API requests for the Counter endpoint to the Leader node for handling\n        opts.DelegateRequestToLeader = r =\u003e r.Path.HasValue \u0026\u0026 r.Path.Value.Contains(\"/Counter\");\n    });\n});\n\n// Raft app specific implementation\nbuilder.Services.AddSingleton\u003cILogRepository, InMemoryLogRepository\u003e(); // For now, store the logs in memory only\nbuilder.Services.AddSingleton\u003cIStateMachine, CounterStateMachine\u003e(); // This is app specific machine that implements a distributed counter\nbuilder.Services.AddSingleton\u003cISerializationTypeAliasProvider, CommandSerializationTypeAliasProvider\u003e(); // App specific state/logs command types for the replicated state machine\n\n// Requires packages: SlimCluster.Membership.Swim, SlimCluster.Consensus.Raft, SlimCluster.Serialization.Json, SlimCluster.Transport.Ip, SlimCluster.Persistence.LocalFile, SlimCluster.AspNetCore\n```\n\nThen somewhere in the micro-service, the [`ICluster`](src/SlimCluster/ICluster.cs) can be used:\n\n```cs\n// Injected, this will be a singleton representing the cluster the service instances form.\nICluster cluster;\n\n// Gives the current leader\nINode? leader = cluster.LeaderNode;\n\n// Gives the node representing current node\nINode self = cluster.SelfNode;\n\n// Provides a snapshot collection of the current nodes discovered and alive/healthy forming the cluster\nIEnumerable\u003cINode\u003e nodes = cluster.Nodes;\n\n// Provides a snapshot collection of the current nodes discovered and alive/healthy forming the cluster excluding self\nIEnumerable\u003cINode\u003e otherNodes = cluster.OtherNodes;\n```\n\nThe [`IClusterMembership`](src/SlimCluster.Membership/IClusterMembership.cs) can be used to understand membership changes:\n\n```cs\n// Injected: IClusterMembership ClusterMembership\nClusterMembership.MemberJoined += (target, e) =\u003e\n{\n    Logger.LogInformation(\"The member {NodeId} joined\", e.Node.Id);\n    PrintActiveMembers();\n};\n\nClusterMembership.MemberLeft += (target, e) =\u003e\n{\n    Logger.LogInformation(\"The member {NodeId} left/faulted\", e.Node.Id);\n    PrintActiveMembers();\n};\n\nClusterMembership.MemberStatusChanged += (target, e) =\u003e\n{\n    if (e.Node.Status == SwimMemberStatus.Suspicious)\n    {\n        Logger.LogInformation(\"The node {NodeId} is suspicious. All active members are: {NodeList}\", e.Node.Id, string.Join(\", \", ClusterMembership.Members.Where(x =\u003e x.Node.Status == SwimMemberStatus.Active)));\n    }\n};\n```\n\n## Architecture\n\n- The service references SlimCluser NuGet packages and configures MSDI.\n- Nodes (service instances) are communicating over UDP/IP and exchange protocol messages (SWIM and Raft).\n- Cluster membership (nodes that form the cluster) is managed (SWIM).\n- Cluster leader is elected at the beginning and in the event of failure (Raft).\n- Logs (commands that chage state machine state) are replicated from leader to followers (Raft).\n- State Machine in each Node gets logs (commands) applied which have been replicated to majority of nodes (Raft).\n- Clients interact with the Cluster (state mutating operations are executed to Leader or Followers for reads) - depends on the use case.\n\n![SlimCluster architecture](docs/images/SlimCluster.jpg)\n\n## License\n\n[Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0)\n\n## Build\n\n```cmd\ncd src\ndotnet build\ndotnet pack --output ../dist\n```\n\nNuGet packaged end up in `dist` folder\n\n## Testing\n\nTo run tests you need to update the respective `appsettings.json` to match your cloud infrastructure or local infrastructure.\n\nRun all tests:\n\n```cmd\ndotnet test\n```\n\nRun all tests except integration tests which require local/cloud infrastructure:\n\n```cmd\ndotnet test --filter Category!=Integration\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzarusz%2Fslimcluster","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzarusz%2Fslimcluster","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzarusz%2Fslimcluster/lists"}