github.com/alpe/etcd@v0.1.2-0.20130915230056-09f31af88aeb/store/watcher.go (about)

     1  package store
     2  
     3  import (
     4  	"path"
     5  	"strconv"
     6  	"strings"
     7  )
     8  
     9  //------------------------------------------------------------------------------
    10  //
    11  // Typedefs
    12  //
    13  //------------------------------------------------------------------------------
    14  
    15  // WatcherHub is where the client register its watcher
    16  type WatcherHub struct {
    17  	watchers map[string][]*Watcher
    18  }
    19  
    20  // Currently watcher only contains a response channel
    21  type Watcher struct {
    22  	C chan *Response
    23  }
    24  
    25  // Create a new watcherHub
    26  func newWatcherHub() *WatcherHub {
    27  	w := new(WatcherHub)
    28  	w.watchers = make(map[string][]*Watcher)
    29  	return w
    30  }
    31  
    32  // Create a new watcher
    33  func NewWatcher() *Watcher {
    34  	return &Watcher{C: make(chan *Response, 1)}
    35  }
    36  
    37  // Add a watcher to the watcherHub
    38  func (w *WatcherHub) addWatcher(prefix string, watcher *Watcher, sinceIndex uint64,
    39  	responseStartIndex uint64, currentIndex uint64, resMap map[string]*Response) error {
    40  
    41  	prefix = path.Clean("/" + prefix)
    42  
    43  	if sinceIndex != 0 && sinceIndex >= responseStartIndex {
    44  		for i := sinceIndex; i <= currentIndex; i++ {
    45  			if checkResponse(prefix, i, resMap) {
    46  				watcher.C <- resMap[strconv.FormatUint(i, 10)]
    47  				return nil
    48  			}
    49  		}
    50  	}
    51  
    52  	_, ok := w.watchers[prefix]
    53  
    54  	if !ok {
    55  		w.watchers[prefix] = make([]*Watcher, 0)
    56  	}
    57  
    58  	w.watchers[prefix] = append(w.watchers[prefix], watcher)
    59  
    60  	return nil
    61  }
    62  
    63  // Check if the response has what we are watching
    64  func checkResponse(prefix string, index uint64, resMap map[string]*Response) bool {
    65  
    66  	resp, ok := resMap[strconv.FormatUint(index, 10)]
    67  
    68  	if !ok {
    69  		// not storage system command
    70  		return false
    71  	} else {
    72  		path := resp.Key
    73  		if strings.HasPrefix(path, prefix) {
    74  			prefixLen := len(prefix)
    75  			if len(path) == prefixLen || path[prefixLen] == '/' {
    76  				return true
    77  			}
    78  
    79  		}
    80  	}
    81  
    82  	return false
    83  }
    84  
    85  // Notify the watcher a action happened
    86  func (w *WatcherHub) notify(resp Response) error {
    87  	resp.Key = path.Clean(resp.Key)
    88  
    89  	segments := strings.Split(resp.Key, "/")
    90  	currPath := "/"
    91  
    92  	// walk through all the pathes
    93  	for _, segment := range segments {
    94  		currPath = path.Join(currPath, segment)
    95  
    96  		watchers, ok := w.watchers[currPath]
    97  
    98  		if ok {
    99  
   100  			newWatchers := make([]*Watcher, 0)
   101  			// notify all the watchers
   102  			for _, watcher := range watchers {
   103  				watcher.C <- &resp
   104  			}
   105  
   106  			if len(newWatchers) == 0 {
   107  				// we have notified all the watchers at this path
   108  				// delete the map
   109  				delete(w.watchers, currPath)
   110  			} else {
   111  				w.watchers[currPath] = newWatchers
   112  			}
   113  		}
   114  
   115  	}
   116  
   117  	return nil
   118  }
   119  
   120  // stopWatchers stops all the watchers
   121  // This function is used when the etcd recovery from a snapshot at runtime
   122  func (w *WatcherHub) stopWatchers() {
   123  	for _, subWatchers := range w.watchers {
   124  		for _, watcher := range subWatchers {
   125  			watcher.C <- nil
   126  		}
   127  	}
   128  	w.watchers = nil
   129  }