github.com/qorio/etcd@v0.1.2-0.20131003183127-5cc585af9618/command.go (about) 1 package main 2 3 import ( 4 "encoding/binary" 5 "encoding/json" 6 "fmt" 7 "os" 8 "path" 9 "time" 10 11 etcdErr "github.com/coreos/etcd/error" 12 "github.com/coreos/etcd/store" 13 "github.com/coreos/go-raft" 14 ) 15 16 const commandPrefix = "etcd:" 17 18 func commandName(name string) string { 19 return commandPrefix + name 20 } 21 22 // A command represents an action to be taken on the replicated state machine. 23 type Command interface { 24 CommandName() string 25 Apply(server *raft.Server) (interface{}, error) 26 } 27 28 // Set command 29 type SetCommand struct { 30 Key string `json:"key"` 31 Value string `json:"value"` 32 ExpireTime time.Time `json:"expireTime"` 33 } 34 35 // The name of the set command in the log 36 func (c *SetCommand) CommandName() string { 37 return commandName("set") 38 } 39 40 // Set the key-value pair 41 func (c *SetCommand) Apply(server *raft.Server) (interface{}, error) { 42 return etcdStore.Set(c.Key, c.Value, c.ExpireTime, server.CommitIndex()) 43 } 44 45 // TestAndSet command 46 type TestAndSetCommand struct { 47 Key string `json:"key"` 48 Value string `json:"value"` 49 PrevValue string `json: prevValue` 50 ExpireTime time.Time `json:"expireTime"` 51 } 52 53 // The name of the testAndSet command in the log 54 func (c *TestAndSetCommand) CommandName() string { 55 return commandName("testAndSet") 56 } 57 58 // Set the key-value pair if the current value of the key equals to the given prevValue 59 func (c *TestAndSetCommand) Apply(server *raft.Server) (interface{}, error) { 60 return etcdStore.TestAndSet(c.Key, c.PrevValue, c.Value, c.ExpireTime, server.CommitIndex()) 61 } 62 63 // Get command 64 type GetCommand struct { 65 Key string `json:"key"` 66 } 67 68 // The name of the get command in the log 69 func (c *GetCommand) CommandName() string { 70 return commandName("get") 71 } 72 73 // Get the value of key 74 func (c *GetCommand) Apply(server *raft.Server) (interface{}, error) { 75 return etcdStore.Get(c.Key) 76 } 77 78 // Delete command 79 type DeleteCommand struct { 80 Key string `json:"key"` 81 } 82 83 // The name of the delete command in the log 84 func (c *DeleteCommand) CommandName() string { 85 return commandName("delete") 86 } 87 88 // Delete the key 89 func (c *DeleteCommand) Apply(server *raft.Server) (interface{}, error) { 90 return etcdStore.Delete(c.Key, server.CommitIndex()) 91 } 92 93 // Watch command 94 type WatchCommand struct { 95 Key string `json:"key"` 96 SinceIndex uint64 `json:"sinceIndex"` 97 } 98 99 // The name of the watch command in the log 100 func (c *WatchCommand) CommandName() string { 101 return commandName("watch") 102 } 103 104 func (c *WatchCommand) Apply(server *raft.Server) (interface{}, error) { 105 // create a new watcher 106 watcher := store.NewWatcher() 107 108 // add to the watchers list 109 etcdStore.AddWatcher(c.Key, watcher, c.SinceIndex) 110 111 // wait for the notification for any changing 112 res := <-watcher.C 113 114 if res == nil { 115 return nil, fmt.Errorf("Clearing watch") 116 } 117 118 return json.Marshal(res) 119 } 120 121 // JoinCommand 122 type JoinCommand struct { 123 RaftVersion string `json:"raftVersion"` 124 Name string `json:"name"` 125 RaftURL string `json:"raftURL"` 126 EtcdURL string `json:"etcdURL"` 127 } 128 129 func newJoinCommand() *JoinCommand { 130 return &JoinCommand{ 131 RaftVersion: r.version, 132 Name: r.name, 133 RaftURL: r.url, 134 EtcdURL: e.url, 135 } 136 } 137 138 // The name of the join command in the log 139 func (c *JoinCommand) CommandName() string { 140 return commandName("join") 141 } 142 143 // Join a server to the cluster 144 func (c *JoinCommand) Apply(raftServer *raft.Server) (interface{}, error) { 145 146 // check if the join command is from a previous machine, who lost all its previous log. 147 response, _ := etcdStore.RawGet(path.Join("_etcd/machines", c.Name)) 148 149 b := make([]byte, 8) 150 binary.PutUvarint(b, raftServer.CommitIndex()) 151 152 if response != nil { 153 return b, nil 154 } 155 156 // check machine number in the cluster 157 num := machineNum() 158 if num == maxClusterSize { 159 debug("Reject join request from ", c.Name) 160 return []byte{0}, etcdErr.NewError(103, "") 161 } 162 163 addNameToURL(c.Name, c.RaftVersion, c.RaftURL, c.EtcdURL) 164 165 // add peer in raft 166 err := raftServer.AddPeer(c.Name, "") 167 168 // add machine in etcd storage 169 key := path.Join("_etcd/machines", c.Name) 170 value := fmt.Sprintf("raft=%s&etcd=%s&raftVersion=%s", c.RaftURL, c.EtcdURL, c.RaftVersion) 171 etcdStore.Set(key, value, time.Unix(0, 0), raftServer.CommitIndex()) 172 173 // add peer stats 174 if c.Name != r.Name() { 175 r.followersStats.Followers[c.Name] = &raftFollowerStats{} 176 r.followersStats.Followers[c.Name].Latency.Minimum = 1 << 63 177 } 178 179 return b, err 180 } 181 182 func (c *JoinCommand) NodeName() string { 183 return c.Name 184 } 185 186 // RemoveCommand 187 type RemoveCommand struct { 188 Name string `json:"name"` 189 } 190 191 // The name of the remove command in the log 192 func (c *RemoveCommand) CommandName() string { 193 return commandName("remove") 194 } 195 196 // Remove a server from the cluster 197 func (c *RemoveCommand) Apply(raftServer *raft.Server) (interface{}, error) { 198 199 // remove machine in etcd storage 200 key := path.Join("_etcd/machines", c.Name) 201 202 _, err := etcdStore.Delete(key, raftServer.CommitIndex()) 203 204 // delete from stats 205 delete(r.followersStats.Followers, c.Name) 206 207 if err != nil { 208 return []byte{0}, err 209 } 210 211 // remove peer in raft 212 err = raftServer.RemovePeer(c.Name) 213 214 if err != nil { 215 return []byte{0}, err 216 } 217 218 if c.Name == raftServer.Name() { 219 // the removed node is this node 220 221 // if the node is not replaying the previous logs 222 // and the node has sent out a join request in this 223 // start. It is sure that this node received a new remove 224 // command and need to be removed 225 if raftServer.CommitIndex() > r.joinIndex && r.joinIndex != 0 { 226 debugf("server [%s] is removed", raftServer.Name()) 227 os.Exit(0) 228 } else { 229 // else ignore remove 230 debugf("ignore previous remove command.") 231 } 232 } 233 234 b := make([]byte, 8) 235 binary.PutUvarint(b, raftServer.CommitIndex()) 236 237 return b, err 238 }