You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							199 lines
						
					
					
						
							4.9 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							199 lines
						
					
					
						
							4.9 KiB
						
					
					
				
								package weed_server
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"encoding/json"
							 | 
						|
									"io"
							 | 
						|
									"math/rand/v2"
							 | 
						|
									"os"
							 | 
						|
									"path"
							 | 
						|
									"time"
							 | 
						|
								
							 | 
						|
									transport "github.com/Jille/raft-grpc-transport"
							 | 
						|
								
							 | 
						|
									"google.golang.org/grpc"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb"
							 | 
						|
								
							 | 
						|
									hashicorpRaft "github.com/hashicorp/raft"
							 | 
						|
									"github.com/seaweedfs/raft"
							 | 
						|
								
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/glog"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/topology"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								type RaftServerOption struct {
							 | 
						|
									GrpcDialOption    grpc.DialOption
							 | 
						|
									Peers             map[string]pb.ServerAddress
							 | 
						|
									ServerAddr        pb.ServerAddress
							 | 
						|
									DataDir           string
							 | 
						|
									Topo              *topology.Topology
							 | 
						|
									RaftResumeState   bool
							 | 
						|
									HeartbeatInterval time.Duration
							 | 
						|
									ElectionTimeout   time.Duration
							 | 
						|
									RaftBootstrap     bool
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								type RaftServer struct {
							 | 
						|
									peers            map[string]pb.ServerAddress // initial peers to join with
							 | 
						|
									raftServer       raft.Server
							 | 
						|
									RaftHashicorp    *hashicorpRaft.Raft
							 | 
						|
									TransportManager *transport.Manager
							 | 
						|
									dataDir          string
							 | 
						|
									serverAddr       pb.ServerAddress
							 | 
						|
									topo             *topology.Topology
							 | 
						|
									*raft.GrpcServer
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								type StateMachine struct {
							 | 
						|
									raft.StateMachine
							 | 
						|
									topo *topology.Topology
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								var _ hashicorpRaft.FSM = &StateMachine{}
							 | 
						|
								
							 | 
						|
								func (s StateMachine) Save() ([]byte, error) {
							 | 
						|
									state := topology.MaxVolumeIdCommand{
							 | 
						|
										MaxVolumeId: s.topo.GetMaxVolumeId(),
							 | 
						|
									}
							 | 
						|
									glog.V(1).Infof("Save raft state %+v", state)
							 | 
						|
									return json.Marshal(state)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (s StateMachine) Recovery(data []byte) error {
							 | 
						|
									state := topology.MaxVolumeIdCommand{}
							 | 
						|
									err := json.Unmarshal(data, &state)
							 | 
						|
									if err != nil {
							 | 
						|
										return err
							 | 
						|
									}
							 | 
						|
									glog.V(1).Infof("Recovery raft state %+v", state)
							 | 
						|
									s.topo.UpAdjustMaxVolumeId(state.MaxVolumeId)
							 | 
						|
									return nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (s *StateMachine) Apply(l *hashicorpRaft.Log) interface{} {
							 | 
						|
									before := s.topo.GetMaxVolumeId()
							 | 
						|
									state := topology.MaxVolumeIdCommand{}
							 | 
						|
									err := json.Unmarshal(l.Data, &state)
							 | 
						|
									if err != nil {
							 | 
						|
										return err
							 | 
						|
									}
							 | 
						|
									s.topo.UpAdjustMaxVolumeId(state.MaxVolumeId)
							 | 
						|
								
							 | 
						|
									glog.V(1).Infoln("max volume id", before, "==>", s.topo.GetMaxVolumeId())
							 | 
						|
									return nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (s *StateMachine) Snapshot() (hashicorpRaft.FSMSnapshot, error) {
							 | 
						|
									return &topology.MaxVolumeIdCommand{
							 | 
						|
										MaxVolumeId: s.topo.GetMaxVolumeId(),
							 | 
						|
									}, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (s *StateMachine) Restore(r io.ReadCloser) error {
							 | 
						|
									b, err := io.ReadAll(r)
							 | 
						|
									if err != nil {
							 | 
						|
										return err
							 | 
						|
									}
							 | 
						|
									if err := s.Recovery(b); err != nil {
							 | 
						|
										return err
							 | 
						|
									}
							 | 
						|
									return nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func NewRaftServer(option *RaftServerOption) (*RaftServer, error) {
							 | 
						|
									s := &RaftServer{
							 | 
						|
										peers:      option.Peers,
							 | 
						|
										serverAddr: option.ServerAddr,
							 | 
						|
										dataDir:    option.DataDir,
							 | 
						|
										topo:       option.Topo,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if glog.V(4) {
							 | 
						|
										raft.SetLogLevel(2)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									raft.RegisterCommand(&topology.MaxVolumeIdCommand{})
							 | 
						|
								
							 | 
						|
									var err error
							 | 
						|
									transporter := raft.NewGrpcTransporter(option.GrpcDialOption)
							 | 
						|
									glog.V(0).Infof("Starting RaftServer with %v", option.ServerAddr)
							 | 
						|
								
							 | 
						|
									// always clear previous log to avoid server is promotable
							 | 
						|
									os.RemoveAll(path.Join(s.dataDir, "log"))
							 | 
						|
									if !option.RaftResumeState {
							 | 
						|
										// always clear previous metadata
							 | 
						|
										os.RemoveAll(path.Join(s.dataDir, "conf"))
							 | 
						|
										os.RemoveAll(path.Join(s.dataDir, "snapshot"))
							 | 
						|
									}
							 | 
						|
									if err := os.MkdirAll(path.Join(s.dataDir, "snapshot"), os.ModePerm); err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									stateMachine := StateMachine{topo: option.Topo}
							 | 
						|
									s.raftServer, err = raft.NewServer(string(s.serverAddr), s.dataDir, transporter, stateMachine, option.Topo, s.serverAddr.ToGrpcAddress())
							 | 
						|
									if err != nil {
							 | 
						|
										glog.V(0).Infoln(err)
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
									heartbeatInterval := time.Duration(float64(option.HeartbeatInterval) * (rand.Float64()*0.25 + 1))
							 | 
						|
									s.raftServer.SetHeartbeatInterval(heartbeatInterval)
							 | 
						|
									s.raftServer.SetElectionTimeout(option.ElectionTimeout)
							 | 
						|
									if err := s.raftServer.LoadSnapshot(); err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
									if err := s.raftServer.Start(); err != nil {
							 | 
						|
										return nil, err
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									for name, peer := range s.peers {
							 | 
						|
										if err := s.raftServer.AddPeer(name, peer.ToGrpcAddress()); err != nil {
							 | 
						|
											return nil, err
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Remove deleted peers
							 | 
						|
									for existsPeerName := range s.raftServer.Peers() {
							 | 
						|
										if existingPeer, found := s.peers[existsPeerName]; !found {
							 | 
						|
											if err := s.raftServer.RemovePeer(existsPeerName); err != nil {
							 | 
						|
												glog.V(0).Infoln(err)
							 | 
						|
												return nil, err
							 | 
						|
											} else {
							 | 
						|
												glog.V(0).Infof("removing old peer: %s", existingPeer)
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									s.GrpcServer = raft.NewGrpcServer(s.raftServer)
							 | 
						|
								
							 | 
						|
									glog.V(0).Infof("current cluster leader: %v", s.raftServer.Leader())
							 | 
						|
								
							 | 
						|
									return s, nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (s *RaftServer) Peers() (members []string) {
							 | 
						|
									if s.raftServer != nil {
							 | 
						|
										peers := s.raftServer.Peers()
							 | 
						|
										for _, p := range peers {
							 | 
						|
											members = append(members, p.Name)
							 | 
						|
										}
							 | 
						|
									} else if s.RaftHashicorp != nil {
							 | 
						|
										cfg := s.RaftHashicorp.GetConfiguration()
							 | 
						|
										for _, p := range cfg.Configuration().Servers {
							 | 
						|
											members = append(members, string(p.ID))
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									return
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (s *RaftServer) DoJoinCommand() {
							 | 
						|
								
							 | 
						|
									glog.V(0).Infoln("Initializing new cluster")
							 | 
						|
								
							 | 
						|
									if _, err := s.raftServer.Do(&raft.DefaultJoinCommand{
							 | 
						|
										Name:             s.raftServer.Name(),
							 | 
						|
										ConnectionString: s.serverAddr.ToGrpcAddress(),
							 | 
						|
									}); err != nil {
							 | 
						|
										glog.Errorf("fail to send join command: %v", err)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
								}
							 |