Categorygithub.com/dmitry-kovalev/cluster
repositorypackage
0.1.1
Repository: https://github.com/dmitry-kovalev/cluster.git
Documentation: pkg.go.dev

# Packages

No description provided by the author

# README

cluster

Reworked version of Dinghy

Cluster implements leader election using part of the raft protocol. It might be useful if you have several workers but only want one of them at a time doing things.

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"strings"

	"github.com/dmitry-kovalev/cluster"
)

func main() {
	addr := flag.String("addr", "localhost:8899", "The address to listen on.")
	nodesList := flag.String("nodes", "localhost:8899,localhost:8898,localhost:8897", "Comma separated list of host:port")
	flag.Parse()

	nodes := strings.Split(*nodesList, ",")

	onLeader := func() error {
		fmt.Println("leader")
		return nil
	}
	onFollower := func() error {
		fmt.Println("me follower")
		return nil
	}

	cl, err := cluster.New(
		nodes,
		onLeader,
		onFollower,
		&cluster.LogLogger{Logger: log.New(os.Stderr, "logger: ", log.Lshortfile)},
		cluster.DefaultElectionTickRange,
		cluster.DefaultHeartbeatTickRange,
	)
	if err != nil {
		log.Fatal(err)
	}
	for _, route := range cl.Routes() {
		http.HandleFunc(route.Path, route.Handler)
	}
	go func() {
		if err := cl.Start(); err != nil {
			log.Fatal(err)
		}
	}()
	log.Fatal(http.ListenAndServe(*addr, nil))
}

You can test cluster with several nodes using docker-compose

$ export LOCAL_IP=192.168.1.138
$ docker compose up
[+] Running 4/4
 ⠿ Network cluster_default    Created                                                                                                                                                              3.8s
 ⠿ Container cluster_node3_1  Created                                                                                                                                                              0.1s
 ⠿ Container cluster_node1_1  Created                                                                                                                                                              0.1s
 ⠿ Container cluster_node2_1  Created                                                                                                                                                              0.1s
Attaching to node1_1, node2_1, node3_1
node2_1  | 14:19:40 [INFO] [cluster] Creating cluster node with nodes: [192.168.1.138:8899 192.168.1.138:8898 192.168.1.138:8897]
node2_1  | 14:19:40 [INFO] [cluster] Starting cluster node
node2_1  | 14:19:40 [INFO] [cluster] Current state is {"id":3626187060,"leader_id":0,"state":"follower","term":1,"voted_for":0}
node2_1  | 14:19:40 [INFO] [cluster] Entering follower state, leader id 0
node2_1  | 14:19:40 [INFO] Me follower
node3_1  | 14:19:41 [INFO] [cluster] Creating cluster node with nodes: [192.168.1.138:8899 192.168.1.138:8898 192.168.1.138:8897]
node3_1  | 14:19:41 [INFO] [cluster] Starting cluster node
node3_1  | 14:19:41 [INFO] [cluster] Current state is {"id":3776595137,"leader_id":0,"state":"follower","term":1,"voted_for":0}
node3_1  | 14:19:41 [INFO] [cluster] Entering follower state, leader id 0
node3_1  | 14:19:41 [INFO] Me follower
node2_1  | 14:19:42 [INFO] [cluster] Follower heartbeat timeout, transitioning to candidate
node2_1  | 14:19:42 [INFO] [cluster] Current state is {"id":3626187060,"leader_id":0,"state":"candidate","term":2,"voted_for":0}
node2_1  | 14:19:42 [INFO] [cluster] Entering candidate state
node2_1  | 14:19:42 [INFO] [cluster] Requesting vote
node2_1  | 14:19:42 [INFO] [cluster] Got RequestVote request, voting for candidate id 3626187060 {"id":3626187060,"leader_id":0,"state":"candidate","term":2,"voted_for":0}
node3_1  | 14:19:42 [INFO] [cluster] got RequestVote request from newer term, stepping down {Term:2 CandidateID:3626187060 NodeID:3626187060} {"id":3776595137,"leader_id":0,"state":"follower","term":1,"voted_for":0}
node3_1  | 14:19:42 [INFO] [cluster] Got RequestVote request, voting for candidate id 3626187060 {"id":3776595137,"leader_id":0,"state":"follower","term":2,"voted_for":0}
node2_1  | 14:19:42 [INFO] [cluster] Election won with 2 votes, becoming leader {"id":3626187060,"leader_id":0,"state":"candidate","term":2,"voted_for":3626187060}
node2_1  | 14:19:42 [INFO] [cluster] Candidate state changed to leader
node2_1  | 14:19:42 [INFO] [cluster] Current state is {"id":3626187060,"leader_id":3626187060,"state":"leader","term":2,"voted_for":3626187060}
node2_1  | 14:19:42 [INFO] [cluster] Entering leader state
node2_1  | 14:19:42 [INFO] I'm leader
node1_1  | 14:19:43 [INFO] [cluster] Creating cluster node with nodes: [192.168.1.138:8899 192.168.1.138:8898 192.168.1.138:8897]
node1_1  | 14:19:43 [INFO] [cluster] Starting cluster node
node1_1  | 14:19:43 [INFO] [cluster] Current state is {"id":3070019158,"leader_id":0,"state":"follower","term":1,"voted_for":0}
node1_1  | 14:19:43 [INFO] [cluster] Entering follower state, leader id 0
node1_1  | 14:19:43 [INFO] Me follower
node1_1  | 14:19:43 [INFO] [cluster] Got AppendEntries request from newer term, stepping down {Term:2 LeaderID:3626187060 NodeID:3626187060} {"id":3070019158,"leader_id":0,"state":"follower","term":1,"voted_for":0}