func (s *Server) FSM() *FSM {
	return s.fsm
}
FSM returns fsm.
package raft
import (
	"net"
	"path/filepath"
	"time"
	"github.com/hashicorp/raft"
	raftboltdb "github.com/hashicorp/raft-boltdb"
	"github.com/pkg/errors"
)
const (
	logStorePrefix    = "log-"
	snapshotsRetained = 2
	raftLogCacheSize = 512
	commandTimeout = 1 * time.Second
	nameResolveRetryInterval = 2 * time.Second
	nameResolveMaxRetry      = 120
)
type PeerInfo struct {
	ID      string
	Address string
}
type Server struct {
	id  string
	fsm *FSM
	inMem    bool
	raftBind string
	peers    []PeerInfo
	config        *raft.Config
	raft          *raft.Raft
	raftStore     *raftboltdb.BoltStore
	raftTransport *raft.NetworkTransport
	logStore    raft.LogStore
	stableStore raft.StableStore
	snapStore   raft.SnapshotStore
	raftLogStorePath string
}
func New(id string, inMem bool, peers []PeerInfo, logStorePath string) *Server {
	raftBind := raftAddressForID(id, peers)
	if raftBind == "" {
		return nil
	}
	return &Server{
		id:               id,
		inMem:            inMem,
		raftBind:         raftBind,
		peers:            peers,
		raftLogStorePath: logStorePath,
	}
}
func tryResolveRaftAdvertiseAddr(bindAddr string) (*net.TCPAddr, error) {
	var err error
	var addr *net.TCPAddr
	for retry := 0; retry < nameResolveMaxRetry; retry++ {
		addr, err = net.ResolveTCPAddr("tcp", bindAddr)
		if err == nil {
			return addr, nil
		}
		time.Sleep(nameResolveRetryInterval)
	}
	return nil, err
}
func (s *Server) StartRaft(config *raft.Config) error {
	defer func() {
		if s.raft == nil && s.raftStore != nil {
			if err := s.raftStore.Close(); err != nil {
				logging.Errorf("failed to close log storage: %v", err)
			}
		}
	}()
	s.fsm = newFSM()
	addr, err := tryResolveRaftAdvertiseAddr(s.raftBind)
	if err != nil {
		return err
	}
	loggerAdapter := newLoggerAdapter()
	trans, err := raft.NewTCPTransportWithLogger(s.raftBind, addr, 3, 10*time.Second, loggerAdapter)
	if err != nil {
		return err
	}
	s.raftTransport = trans
	if s.inMem {
		raftInmem := raft.NewInmemStore()
		s.stableStore = raftInmem
		s.logStore = raftInmem
		s.snapStore = raft.NewInmemSnapshotStore()
	} else {
		if err = ensureDir(s.raftStorePath()); err != nil {
			return errors.Wrap(err, "failed to create log store directory")
		}
		s.raftStore, err = raftboltdb.NewBoltStore(filepath.Join(s.raftStorePath(), "raft.db"))
		if err != nil {
			return err
		}
		s.stableStore = s.raftStore
		s.logStore, err = raft.NewLogCache(raftLogCacheSize, s.raftStore)
		if err != nil {
			return err
		}
		s.snapStore, err = raft.NewFileSnapshotStoreWithLogger(s.raftStorePath(), snapshotsRetained, loggerAdapter)
		if err != nil {
			return err
		}
	}
	if config == nil {
		s.config = &raft.Config{
			ProtocolVersion:    raft.ProtocolVersionMax,
			HeartbeatTimeout:   1000 * time.Millisecond,
			ElectionTimeout:    1000 * time.Millisecond,
			CommitTimeout:      50 * time.Millisecond,
			MaxAppendEntries:   64,
			ShutdownOnRemove:   true,
			TrailingLogs:       10240,
			SnapshotInterval:   120 * time.Second,
			SnapshotThreshold:  8192,
			LeaderLeaseTimeout: 500 * time.Millisecond,
		}
	} else {
		s.config = config
	}
	s.config.Logger = loggerAdapter
	s.config.LocalID = raft.ServerID(s.id)
	bootstrapConf, err := s.bootstrapConfig(s.peers)
	if err != nil {
		return err
	}
	if bootstrapConf != nil {
		if err = raft.BootstrapCluster(
			s.config, s.logStore, s.stableStore,
			s.snapStore, trans, *bootstrapConf); err != nil {
			return err
		}
	}
	s.raft, err = raft.NewRaft(s.config, s.fsm, s.logStore, s.stableStore, s.snapStore, s.raftTransport)
	if err != nil {
		return err
	}
	logging.Infof("Raft server is starting on %s...", s.raftBind)
	return err
}
func (s *Server) bootstrapConfig(peers []PeerInfo) (*raft.Configuration, error) {
	hasState, err := raft.HasExistingState(s.logStore, s.stableStore, s.snapStore)
	if err != nil {
		return nil, err
	}
	if !hasState {
		raftConfig := &raft.Configuration{
			Servers: make([]raft.Server, len(peers)),
		}
		for i, p := range peers {
			raftConfig.Servers[i] = raft.Server{
				ID:      raft.ServerID(p.ID),
				Address: raft.ServerAddress(p.Address),
			}
		}
		return raftConfig, nil
	}
	return nil, nil
}
func (s *Server) raftStorePath() string {
	if s.raftLogStorePath == "" {
		return logStorePrefix + s.id
	}
	return s.raftLogStorePath
}