github.com/ghodss/etcd@v0.3.1-0.20140417172404-cc329bfa55cb/server/join_command.go (about) 1 package server 2 3 import ( 4 "encoding/binary" 5 "encoding/json" 6 7 etcdErr "github.com/coreos/etcd/error" 8 "github.com/coreos/etcd/log" 9 "github.com/coreos/etcd/third_party/github.com/goraft/raft" 10 ) 11 12 func init() { 13 raft.RegisterCommand(&JoinCommandV1{}) 14 raft.RegisterCommand(&JoinCommandV2{}) 15 } 16 17 // JoinCommandV1 represents a request to join the cluster. 18 // The command returns the join_index (Uvarint). 19 type JoinCommandV1 struct { 20 MinVersion int `json:"minVersion"` 21 MaxVersion int `json:"maxVersion"` 22 Name string `json:"name"` 23 RaftURL string `json:"raftURL"` 24 EtcdURL string `json:"etcdURL"` 25 } 26 27 // The name of the join command in the log 28 func (c *JoinCommandV1) CommandName() string { 29 return "etcd:join" 30 } 31 32 func (c *JoinCommandV1) updatePeerURL(ps *PeerServer) error { 33 log.Debugf("Update peer URL of %v to %v", c.Name, c.RaftURL) 34 if err := ps.registry.UpdatePeerURL(c.Name, c.RaftURL); err != nil { 35 log.Debugf("Error while updating in registry: %s (%v)", c.Name, err) 36 return err 37 } 38 // Flush commit index, so raft will replay to here when restarted 39 ps.raftServer.FlushCommitIndex() 40 return nil 41 } 42 43 // Join a server to the cluster 44 func (c *JoinCommandV1) Apply(context raft.Context) (interface{}, error) { 45 ps, _ := context.Server().Context().(*PeerServer) 46 47 b := make([]byte, 8) 48 binary.PutUvarint(b, context.CommitIndex()) 49 50 // Make sure we're not getting a cached value from the registry. 51 ps.registry.Invalidate(c.Name) 52 53 // Check if the join command is from a previous peer, who lost all its previous log. 54 if peerURL, ok := ps.registry.PeerURL(c.Name); ok { 55 // If previous node restarts with different peer URL, 56 // update its information. 57 if peerURL != c.RaftURL { 58 log.Infof("Rejoin with %v instead of %v from %v", c.RaftURL, peerURL, c.Name) 59 if err := c.updatePeerURL(ps); err != nil { 60 return []byte{0}, err 61 } 62 } 63 return b, nil 64 } 65 66 // Check peer number in the cluster 67 if ps.registry.PeerCount() >= ps.ClusterConfig().ActiveSize { 68 log.Debug("Reject join request from ", c.Name) 69 return []byte{0}, etcdErr.NewError(etcdErr.EcodeNoMorePeer, "", context.CommitIndex()) 70 } 71 72 // Add to shared peer registry. 73 ps.registry.RegisterPeer(c.Name, c.RaftURL, c.EtcdURL) 74 75 // Add peer in raft 76 err := context.Server().AddPeer(c.Name, "") 77 78 // Add peer stats 79 if c.Name != ps.RaftServer().Name() { 80 ps.followersStats.Followers[c.Name] = &raftFollowerStats{} 81 ps.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63 82 } 83 84 return b, err 85 } 86 87 func (c *JoinCommandV1) NodeName() string { 88 return c.Name 89 } 90 91 // JoinCommandV2 represents a request to join the cluster. 92 type JoinCommandV2 struct { 93 MinVersion int `json:"minVersion"` 94 MaxVersion int `json:"maxVersion"` 95 Name string `json:"name"` 96 PeerURL string `json:"peerURL"` 97 ClientURL string `json:"clientURL"` 98 } 99 100 // CommandName returns the name of the command in the Raft log. 101 func (c *JoinCommandV2) CommandName() string { 102 return "etcd:v2:join" 103 } 104 105 func (c *JoinCommandV2) updatePeerURL(ps *PeerServer) error { 106 log.Debugf("Update peer URL of %v to %v", c.Name, c.PeerURL) 107 if err := ps.registry.UpdatePeerURL(c.Name, c.PeerURL); err != nil { 108 log.Debugf("Error while updating in registry: %s (%v)", c.Name, err) 109 return err 110 } 111 // Flush commit index, so raft will replay to here when restart 112 ps.raftServer.FlushCommitIndex() 113 return nil 114 } 115 116 // Apply attempts to join a machine to the cluster. 117 func (c *JoinCommandV2) Apply(context raft.Context) (interface{}, error) { 118 ps, _ := context.Server().Context().(*PeerServer) 119 var msg = joinMessageV2{ 120 Mode: PeerMode, 121 CommitIndex: context.CommitIndex(), 122 } 123 124 // Make sure we're not getting a cached value from the registry. 125 ps.registry.Invalidate(c.Name) 126 127 // Check if the join command is from a previous peer, who lost all its previous log. 128 if peerURL, ok := ps.registry.PeerURL(c.Name); ok { 129 // If previous node restarts with different peer URL, 130 // update its information. 131 if peerURL != c.PeerURL { 132 log.Infof("Rejoin with %v instead of %v from %v", c.PeerURL, peerURL, c.Name) 133 if err := c.updatePeerURL(ps); err != nil { 134 return []byte{0}, err 135 } 136 } 137 return json.Marshal(msg) 138 } 139 140 // Check peer number in the cluster. 141 if ps.registry.PeerCount() >= ps.ClusterConfig().ActiveSize { 142 log.Debug("Join as standby ", c.Name) 143 ps.registry.RegisterStandby(c.Name, c.PeerURL, c.ClientURL) 144 msg.Mode = StandbyMode 145 return json.Marshal(msg) 146 } 147 148 // Remove it as a standby if it is one. 149 if ps.registry.StandbyExists(c.Name) { 150 ps.registry.UnregisterStandby(c.Name) 151 } 152 153 // Add to shared peer registry. 154 ps.registry.RegisterPeer(c.Name, c.PeerURL, c.ClientURL) 155 156 // Add peer in raft 157 if err := context.Server().AddPeer(c.Name, ""); err != nil { 158 b, _ := json.Marshal(msg) 159 return b, err 160 } 161 162 // Add peer stats 163 if c.Name != ps.RaftServer().Name() { 164 ps.followersStats.Followers[c.Name] = &raftFollowerStats{} 165 ps.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63 166 } 167 168 return json.Marshal(msg) 169 } 170 171 func (c *JoinCommandV2) NodeName() string { 172 return c.Name 173 } 174 175 type joinMessageV2 struct { 176 CommitIndex uint64 `json:"commitIndex"` 177 Mode Mode `json:"mode"` 178 }