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