github.com/jbking/gohan@v0.0.0-20151217002006-b41ccf1c2a96/sync/etcd/etcd.go (about)

     1  // Copyright (C) 2015 NTT Innovation Institute, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
    12  // implied.
    13  // See the License for the specific language governing permissions and
    14  // limitations under the License.
    15  
    16  package etcd
    17  
    18  import (
    19  	"encoding/json"
    20  	"fmt"
    21  	"os"
    22  	"time"
    23  
    24  	"github.com/cloudwan/gohan/sync"
    25  	"github.com/coreos/go-etcd/etcd"
    26  	cmap "github.com/streamrail/concurrent-map"
    27  	"github.com/twinj/uuid"
    28  )
    29  
    30  const masterTTL = 10
    31  
    32  //Sync is struct for etcd based sync
    33  type Sync struct {
    34  	locks      cmap.ConcurrentMap
    35  	etcdClient *etcd.Client
    36  	processID  string
    37  }
    38  
    39  //NewSync initialize new etcd sync
    40  func NewSync(etcdServers []string) *Sync {
    41  	sync := &Sync{locks: cmap.New()}
    42  	sync.etcdClient = etcd.NewClient(etcdServers)
    43  	hostname, _ := os.Hostname()
    44  	sync.processID = hostname + uuid.NewV4().String()
    45  	return sync
    46  }
    47  
    48  //Update sync update sync
    49  func (s *Sync) Update(key, jsonString string) error {
    50  	_, err := s.etcdClient.Set(key, jsonString, 0)
    51  	if err != nil {
    52  		log.Error(fmt.Sprintf("failed to sync with backend %s", err))
    53  		return err
    54  	}
    55  	return nil
    56  }
    57  
    58  //Delete sync update sync
    59  func (s *Sync) Delete(key string) error {
    60  	s.etcdClient.Delete(key, false)
    61  	return nil
    62  }
    63  
    64  //Fetch data from sync
    65  func (s *Sync) Fetch(key string) (interface{}, error) {
    66  	return s.etcdClient.Get(key, true, true)
    67  }
    68  
    69  //HasLock checks current process owns lock or not
    70  func (s *Sync) HasLock(path string) bool {
    71  	value, ok := s.locks.Get(path)
    72  	isLocked, _ := value.(bool)
    73  	return isLocked && ok
    74  }
    75  
    76  // Lock locks resources on sync
    77  // This call blocks until you can get lock
    78  func (s *Sync) Lock(path string, block bool) error {
    79  	for {
    80  		if s.HasLock(path) {
    81  			return nil
    82  		}
    83  		_, err := s.etcdClient.Create(path, s.processID, masterTTL)
    84  		if err != nil {
    85  			log.Notice("failed to lock path %s: %s", path, err)
    86  			s.locks.Set(path, false)
    87  			if !block {
    88  				return err
    89  			}
    90  			time.Sleep(masterTTL * time.Second)
    91  			continue
    92  		}
    93  		log.Info("Locked %s", path)
    94  		s.locks.Set(path, true)
    95  		//Refresh master token
    96  		go func() {
    97  			for s.HasLock(path) {
    98  				_, err := s.etcdClient.CompareAndSwap(
    99  					path, s.processID, masterTTL, s.processID, 0)
   100  				if err != nil {
   101  					log.Notice("failed to keepalive lock for %s %s", path, err)
   102  					s.locks.Set(path, false)
   103  					return
   104  				}
   105  				time.Sleep(masterTTL / 2 * time.Second)
   106  			}
   107  		}()
   108  		return nil
   109  	}
   110  }
   111  
   112  //Unlock path
   113  func (s *Sync) Unlock(path string) error {
   114  	s.locks.Set(path, false)
   115  	s.etcdClient.CompareAndDelete(path, s.processID, 0)
   116  	log.Info("Unlocked path %s", path)
   117  	return nil
   118  }
   119  
   120  func eventsFromNode(action string, node *etcd.Node, responseChan chan *sync.Event) {
   121  	event := &sync.Event{
   122  		Action: action,
   123  		Key:    node.Key,
   124  	}
   125  	json.Unmarshal([]byte(node.Value), &event.Data)
   126  	responseChan <- event
   127  	for _, subnode := range node.Nodes {
   128  		eventsFromNode(action, subnode, responseChan)
   129  	}
   130  }
   131  
   132  //Watch keep watch update under the path
   133  func (s *Sync) Watch(path string, responseChan chan *sync.Event, stopChan chan bool) error {
   134  	var etcdResponseChan chan *etcd.Response
   135  
   136  	etcdResponseChan = make(chan *etcd.Response)
   137  	response, err := s.etcdClient.Get(path, true, true)
   138  	if err != nil {
   139  		if etcdError, ok := err.(*etcd.EtcdError); ok {
   140  			switch etcdError.ErrorCode {
   141  			case 100:
   142  				response, err = s.etcdClient.CreateDir(path, 0)
   143  				if err != nil {
   144  					log.Error(fmt.Sprintf("failed to create dir: %s", err))
   145  					return err
   146  				}
   147  			default:
   148  				log.Error(fmt.Sprintf("etcd error[%d]: %s ", etcdError.ErrorCode, etcdError))
   149  				return err
   150  			}
   151  		} else {
   152  			log.Error(fmt.Sprintf("watch error: %s", err))
   153  			return err
   154  		}
   155  	}
   156  	lastIndex := response.EtcdIndex + 1
   157  	eventsFromNode(response.Action, response.Node, responseChan)
   158  	go func() {
   159  		_, err = s.etcdClient.Watch(path, lastIndex, true, etcdResponseChan, stopChan)
   160  		if err != nil {
   161  			log.Error(fmt.Sprintf("watch error: %s", err))
   162  			stopChan <- true
   163  			return
   164  		}
   165  	}()
   166  
   167  	for {
   168  		select {
   169  		case response := <-etcdResponseChan:
   170  			if response != nil {
   171  				event := &sync.Event{
   172  					Action: response.Action,
   173  					Key:    response.Node.Key,
   174  				}
   175  				json.Unmarshal([]byte(response.Node.Value), &event.Data)
   176  				responseChan <- event
   177  			}
   178  		case stop := <-stopChan:
   179  			if stop == true {
   180  				return nil
   181  			}
   182  		}
   183  	}
   184  }