github.com/alpe/etcd@v0.1.2-0.20130915230056-09f31af88aeb/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  	if c.Name != r.Name() {
   174  		r.peersStats[c.Name] = &raftPeerStats{MinLatency: 1 << 63}
   175  	}
   176  
   177  	return b, err
   178  }
   179  
   180  func (c *JoinCommand) NodeName() string {
   181  	return c.Name
   182  }
   183  
   184  // RemoveCommand
   185  type RemoveCommand struct {
   186  	Name string `json:"name"`
   187  }
   188  
   189  // The name of the remove command in the log
   190  func (c *RemoveCommand) CommandName() string {
   191  	return commandName("remove")
   192  }
   193  
   194  // Remove a server from the cluster
   195  func (c *RemoveCommand) Apply(raftServer *raft.Server) (interface{}, error) {
   196  
   197  	// remove machine in etcd storage
   198  	key := path.Join("_etcd/machines", c.Name)
   199  
   200  	_, err := etcdStore.Delete(key, raftServer.CommitIndex())
   201  	delete(r.peersStats, c.Name)
   202  
   203  	if err != nil {
   204  		return []byte{0}, err
   205  	}
   206  
   207  	// remove peer in raft
   208  	err = raftServer.RemovePeer(c.Name)
   209  
   210  	if err != nil {
   211  		return []byte{0}, err
   212  	}
   213  
   214  	if c.Name == raftServer.Name() {
   215  		// the removed node is this node
   216  
   217  		// if the node is not replaying the previous logs
   218  		// and the node has sent out a join request in this
   219  		// start. It is sure that this node received a new remove
   220  		// command and need to be removed
   221  		if raftServer.CommitIndex() > r.joinIndex && r.joinIndex != 0 {
   222  			debugf("server [%s] is removed", raftServer.Name())
   223  			os.Exit(0)
   224  		} else {
   225  			// else ignore remove
   226  			debugf("ignore previous remove command.")
   227  		}
   228  	}
   229  
   230  	b := make([]byte, 8)
   231  	binary.PutUvarint(b, raftServer.CommitIndex())
   232  
   233  	return b, err
   234  }