Files
SugarDB/server/memberlist.go
2023-07-31 00:20:10 +08:00

181 lines
4.2 KiB
Go

package main
import (
"context"
"encoding/json"
"fmt"
"log"
"time"
"github.com/hashicorp/memberlist"
"github.com/hashicorp/raft"
"github.com/sethvargo/go-retry"
)
type BroadcastMessage struct {
Action string `json:"Action"`
ServerID raft.ServerID `json:"ServerID"`
ServerAddr raft.ServerAddress `json:"ServerAddr"`
Content string `json:"Content"`
}
// Implements Broadcast interface
func (broadcastMessage *BroadcastMessage) Invalidates(other memberlist.Broadcast) bool {
mb, ok := other.(*BroadcastMessage)
if !ok {
return false
}
if mb.ServerID == broadcastMessage.ServerID && mb.Action == "RaftJoin" {
return true
}
return false
}
// Implements Broadcast interface
func (broadcastMessage *BroadcastMessage) Message() []byte {
msg, err := json.Marshal(broadcastMessage)
if err != nil {
fmt.Println(err)
return []byte{}
}
return msg
}
// Implements Broadcast interface
func (broadcastMessage *BroadcastMessage) Finished() {
// No-Op
}
func (server *Server) MemberListInit() {
// Triggered before RaftInit
cfg := memberlist.DefaultLocalConfig()
cfg.BindAddr = server.config.BindAddr
cfg.BindPort = int(server.config.MemberListBindPort)
cfg.Events = server
cfg.Delegate = server
server.broadcastQueue.RetransmitMult = 1
server.broadcastQueue.NumNodes = func() int {
return server.numOfNodes
}
list, err := memberlist.Create(cfg)
server.memberList = list
if err != nil {
log.Fatal(err)
}
if server.config.JoinAddr != "" {
ctx := context.Background()
backoffPolicy := RetryBackoff(retry.NewFibonacci(1*time.Second), 5, 200*time.Millisecond, 0, 0)
err := retry.Do(ctx, backoffPolicy, func(ctx context.Context) error {
_, err := list.Join([]string{server.config.JoinAddr})
if err != nil {
return retry.RetryableError(err)
}
return nil
})
if err != nil {
log.Fatal(err)
}
go server.broadcastRaftAddress()
}
}
func (server *Server) broadcastRaftAddress() {
ticker := time.NewTicker(5 * time.Second)
for {
select {
case <-ticker.C:
fmt.Println("[JOIN_BROADCAST]: Trying to joing server if id ", server.config.ServerID)
msg := BroadcastMessage{
Action: "RaftJoin",
ServerID: raft.ServerID(server.config.ServerID),
ServerAddr: raft.ServerAddress(fmt.Sprintf("%s:%d", server.config.BindAddr, server.config.RaftBindPort)),
}
server.broadcastQueue.QueueBroadcast(&msg)
case msg := <-*server.raftJoinSuccessCh:
if msg.ServerID == raft.ServerID(server.config.ServerID) {
fmt.Printf("Server %s succesfully joined raft cluster.\n", msg.ServerID)
return
}
}
}
}
// Implements Delegate interface
func (server *Server) NodeMeta(limit int) []byte {
return []byte("")
}
// Implements Delegate interface
func (server *Server) NotifyMsg(msgBytes []byte) {
var msg BroadcastMessage
if err := json.Unmarshal(msgBytes, &msg); err != nil {
fmt.Print(err)
return
}
switch msg.Action {
case "RaftJoin":
if err := server.addVoter(raft.ServerID(msg.ServerID), raft.ServerAddress(msg.ServerAddr), 0, 0); err != nil {
fmt.Println(err)
}
case "RaftJoinSuccess":
*server.raftJoinSuccessCh <- msg
case "MutateData":
// Mutate the value at a given key
case "FetchData":
// Fetch the value at a fiven key
}
}
// Implements Delegate interface
func (server *Server) GetBroadcasts(overhead, limit int) [][]byte {
return server.broadcastQueue.GetBroadcasts(overhead, limit)
}
// Implements Delegate interface
func (server *Server) LocalState(join bool) []byte {
// No-Op
return []byte("")
}
// Implements Delegate interface
func (server *Server) MergeRemoteState(buf []byte, join bool) {
// No-Op
}
// Implements EventDelegate interface
func (server *Server) NotifyJoin(node *memberlist.Node) {
server.numOfNodes += 1
}
// Implements EventDelegate interface
func (server *Server) NotifyLeave(node *memberlist.Node) {
server.numOfNodes -= 1
}
// Implements EventDelegate interface
func (server *Server) NotifyUpdate(node *memberlist.Node) {
// No-Op
}
func (server *Server) MemberListShutdown() {
// Triggered after RaftShutdown
// Gracefully leave memberlist cluster
// Broadcast message to remove current node from raft cluster
fmt.Println("Shutting down memberlist.")
}