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.

113 lines
3.3 KiB

  1. package weed_server
  2. // https://yusufs.medium.com/creating-distributed-kv-database-by-implementing-raft-consensus-using-golang-d0884eef2e28
  3. // https://github.com/Jille/raft-grpc-example/blob/cd5bcab0218f008e044fbeee4facdd01b06018ad/application.go#L18
  4. import (
  5. "fmt"
  6. transport "github.com/Jille/raft-grpc-transport"
  7. "github.com/chrislusf/seaweedfs/weed/glog"
  8. "github.com/hashicorp/raft"
  9. boltdb "github.com/hashicorp/raft-boltdb"
  10. "google.golang.org/grpc"
  11. "math/rand"
  12. "os"
  13. "path/filepath"
  14. "time"
  15. )
  16. func NewHashicorpRaftServer(option *RaftServerOption) (*RaftServer, error) {
  17. s := &RaftServer{
  18. peers: option.Peers,
  19. serverAddr: option.ServerAddr,
  20. dataDir: option.DataDir,
  21. topo: option.Topo,
  22. }
  23. c := raft.DefaultConfig()
  24. c.LocalID = raft.ServerID(s.serverAddr) // TODO maybee the IP:port address will change
  25. c.NoSnapshotRestoreOnStart = option.RaftResumeState
  26. c.HeartbeatTimeout = time.Duration(float64(option.HeartbeatInterval) * (rand.Float64()*0.25 + 1))
  27. c.ElectionTimeout = option.ElectionTimeout
  28. if c.LeaderLeaseTimeout > c.HeartbeatTimeout {
  29. c.LeaderLeaseTimeout = c.HeartbeatTimeout
  30. }
  31. if glog.V(4) {
  32. c.LogLevel = "Debug"
  33. } else if glog.V(2) {
  34. c.LogLevel = "Info"
  35. } else if glog.V(1) {
  36. c.LogLevel = "Warn"
  37. } else if glog.V(0) {
  38. c.LogLevel = "Error"
  39. }
  40. baseDir := s.dataDir
  41. ldb, err := boltdb.NewBoltStore(filepath.Join(baseDir, "logs.dat"))
  42. if err != nil {
  43. return nil, fmt.Errorf(`boltdb.NewBoltStore(%q): %v`, filepath.Join(baseDir, "logs.dat"), err)
  44. }
  45. sdb, err := boltdb.NewBoltStore(filepath.Join(baseDir, "stable.dat"))
  46. if err != nil {
  47. return nil, fmt.Errorf(`boltdb.NewBoltStore(%q): %v`, filepath.Join(baseDir, "stable.dat"), err)
  48. }
  49. fss, err := raft.NewFileSnapshotStore(baseDir, 3, os.Stderr)
  50. if err != nil {
  51. return nil, fmt.Errorf(`raft.NewFileSnapshotStore(%q, ...): %v`, baseDir, err)
  52. }
  53. s.TransportManager = transport.New(raft.ServerAddress(s.serverAddr), []grpc.DialOption{option.GrpcDialOption})
  54. stateMachine := StateMachine{topo: option.Topo}
  55. s.RaftHashicorp, err = raft.NewRaft(c, &stateMachine, ldb, sdb, fss, s.TransportManager.Transport())
  56. if err != nil {
  57. return nil, fmt.Errorf("raft.NewRaft: %v", err)
  58. }
  59. if option.RaftBootstrap {
  60. cfg := raft.Configuration{
  61. Servers: []raft.Server{
  62. {
  63. Suffrage: raft.Voter,
  64. ID: c.LocalID,
  65. Address: raft.ServerAddress(s.serverAddr.ToGrpcAddress()),
  66. },
  67. },
  68. }
  69. // Add known peers to bootstrap
  70. for _, peer := range option.Peers {
  71. if peer == option.ServerAddr {
  72. continue
  73. }
  74. cfg.Servers = append(cfg.Servers, raft.Server{
  75. Suffrage: raft.Voter,
  76. ID: raft.ServerID(peer),
  77. Address: raft.ServerAddress(peer.ToGrpcAddress()),
  78. })
  79. }
  80. f := s.RaftHashicorp.BootstrapCluster(cfg)
  81. if err := f.Error(); err != nil {
  82. return nil, fmt.Errorf("raft.Raft.BootstrapCluster: %v", err)
  83. }
  84. }
  85. ticker := time.NewTicker(c.HeartbeatTimeout * 10)
  86. if glog.V(4) {
  87. go func() {
  88. for {
  89. select {
  90. case <-ticker.C:
  91. cfuture := s.RaftHashicorp.GetConfiguration()
  92. if err = cfuture.Error(); err != nil {
  93. glog.Fatalf("error getting config: %s", err)
  94. }
  95. configuration := cfuture.Configuration()
  96. glog.V(4).Infof("Showing peers known by %s:\n%+v", s.RaftHashicorp.String(), configuration.Servers)
  97. }
  98. }
  99. }()
  100. }
  101. return s, nil
  102. }