Skip to content
/ raft Public

Package raft implements the Raft distributed consensus protocol based on hslam/rpc.

License

Notifications You must be signed in to change notification settings

hslam/raft

Folders and files

NameName
Last commit message
Last commit date

Latest commit

May 17, 2023
c1f2cb3 · May 17, 2023
Jan 30, 2021
Dec 9, 2019
Feb 6, 2021
Feb 11, 2021
Feb 11, 2021
Feb 2, 2021
Dec 23, 2020
Dec 24, 2020
Feb 16, 2021
Feb 16, 2021
Feb 17, 2021
Feb 5, 2021
Jan 2, 2021
Jan 4, 2021
May 17, 2023
May 17, 2023
Nov 15, 2020
May 14, 2023
Feb 24, 2021
Feb 11, 2021
Dec 31, 2020
Feb 11, 2021
Feb 5, 2021
Jan 15, 2021
Jan 15, 2021
Jan 20, 2021
Feb 12, 2021
Jan 28, 2021
Feb 23, 2021
Feb 2, 2021
Jan 29, 2021
Jan 13, 2021
Jan 10, 2021
Jan 14, 2021
Jan 16, 2021
Feb 11, 2021
Feb 25, 2021
Jan 21, 2021
Dec 29, 2020
Jan 20, 2021
Dec 31, 2020
Jan 29, 2021
Dec 31, 2020
Feb 12, 2021

Repository files navigation

raft

PkgGoDev Build Status codecov Go Report Card LICENSE

Package raft implements the Raft distributed consensus protocol based on hslam/rpc.

Features

  • Leader election
  • Log replication
  • Membership changes
  • Log compaction (snapshotting)
  • RPC transport
  • Write-ahead logging and LRU cache
  • ReadIndex/LeaseRead
  • Non-voting members (The leader only replicates log entries to them)
  • Snapshot policies (Never/EverySecond/CustomSync)

Get started

Install

go get github.com/hslam/raft

Import

import "github.com/hslam/raft"

Example

package main

import (
	"flag"
	"github.com/hslam/raft"
	"io"
	"io/ioutil"
	"log"
	"strings"
	"time"
)

var host, path, members string
var port int
var join bool

func init() {
	flag.StringVar(&host, "h", "localhost", "hostname")
	flag.IntVar(&port, "p", 9001, "port")
	flag.StringVar(&path, "path", "raft.example/node.1", "data dir")
	flag.BoolVar(&join, "join", false, "")
	flag.StringVar(&members, "members", "localhost:9001", "host:port,nonVoting;host:port")
	flag.Parse()
}

func main() {
	ctx := &Context{data: ""}
	node, err := raft.NewNode(host, port, path, ctx, join, parse(members))
	if err != nil {
		panic(err)
	}
	node.RegisterCommand(&Command{})
	node.SetCodec(&raft.JSONCodec{})
	node.SetSnapshot(ctx)
	node.LeaderChange(func() {
		if node.IsLeader() {
			node.Do(&Command{"foobar"})
			log.Printf("State:%s, Set:foobar\n", node.State())
			if ok := node.ReadIndex(); ok {
				log.Printf("State:%s, Get:%s\n", node.State(), ctx.Get())
			}
		} else {
			for len(ctx.Get()) == 0 {
				time.Sleep(time.Second)
			}
			log.Printf("State:%s, Get:%s\n", node.State(), ctx.Get())
		}
	})
	node.Start()
	for range time.NewTicker(time.Second * 5).C {
		log.Printf("State:%s, Leader:%s\n", node.State(), node.Leader())
	}
}

type Context struct{ data string }

func (ctx *Context) Set(value string) { ctx.data = value }
func (ctx *Context) Get() string      { return ctx.data }

// Save implements the raft.Snapshot Save method.
func (ctx *Context) Save(w io.Writer) (int, error) { return w.Write([]byte(ctx.Get())) }

// Recover implements the raft.Snapshot Recover method.
func (ctx *Context) Recover(r io.Reader) (int, error) {
	raw, err := ioutil.ReadAll(r)
	if err != nil {
		return 0, err
	}
	ctx.Set(string(raw))
	return len(raw), nil
}

// Command implements the raft.Command interface.
type Command struct{ Data string }

func (c *Command) Type() uint64 { return 1 }
func (c *Command) Do(context interface{}) (interface{}, error) {
	context.(*Context).Set(c.Data)
	return nil, nil
}

func parse(members string) (m []*raft.Member) {
	if members != "" {
		for _, member := range strings.Split(members, ";") {
			if len(member) > 0 {
				strs := strings.Split(member, ",")
				m = append(m, &raft.Member{Address: strs[0]})
				if len(strs) > 1 && strs[1] == "true" {
					m[len(m)-1].NonVoting = true
				}
			}
		}
	}
	return
}

Build

go build -o node main.go

One node

./node -h=localhost -p=9001 -path="raft.example/node.1" -join=false

Three nodes

./node -h=localhost -p=9001 -path="raft.example/node.hw.1" -join=false \
-members="localhost:9001;localhost:9002;localhost:9003"

./node -h=localhost -p=9002 -path="raft.example/node.hw.2" -join=false \
-members="localhost:9001;localhost:9002;localhost:9003"

./node -h=localhost -p=9003 -path="raft.example/node.hw.3" -join=false \
-members="localhost:9001;localhost:9002;localhost:9003"

Membership changes

./node -h=localhost -p=9001 -path="raft.example/node.mc.1" -join=false

./node -h=localhost -p=9002 -path="raft.example/node.mc.2" -join=true \
-members="localhost:9001;localhost:9002"

./node -h=localhost -p=9003 -path="raft.example/node.mc.3" -join=true \
-members="localhost:9001;localhost:9002;localhost:9003"

Non-voting

./node -h=localhost -p=9001 -path="raft.example/node.nv.1" -join=false \
-members="localhost:9001;localhost:9002;localhost:9003;localhost:9004,true"

./node -h=localhost -p=9002 -path="raft.example/node.nv.2" -join=false \
-members="localhost:9001;localhost:9002;localhost:9003;localhost:9004,true"

./node -h=localhost -p=9003 -path="raft.example/node.nv.3" -join=false \
-members="localhost:9001;localhost:9002;localhost:9003;localhost:9004,true"

./node -h=localhost -p=9004 -path="raft.example/node.nv.4" -join=false \
-members="localhost:9001;localhost:9002;localhost:9003;localhost:9004,true"

Running on a three nodes cluster.

Write

write-qpswrite-p99

Read Index

read-qpsread-p99

License

This package is licensed under a MIT license (Copyright (c) 2019 Meng Huang)

Author

raft was written by Meng Huang.

About

Package raft implements the Raft distributed consensus protocol based on hslam/rpc.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages