github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/state/multiwatcher.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package state
     5  
     6  import (
     7  	"container/list"
     8  	stderrors "errors"
     9  	"reflect"
    10  
    11  	"github.com/juju/errors"
    12  	"gopkg.in/tomb.v1"
    13  
    14  	"github.com/juju/juju/state/multiwatcher"
    15  	"github.com/juju/juju/state/watcher"
    16  )
    17  
    18  // Multiwatcher watches any changes to the state.
    19  type Multiwatcher struct {
    20  	all *storeManager
    21  
    22  	// used indicates that the watcher was used (i.e. Next() called).
    23  	used bool
    24  
    25  	// The following fields are maintained by the storeManager
    26  	// goroutine.
    27  	revno   int64
    28  	stopped bool
    29  }
    30  
    31  // NewMultiwatcher creates a new watcher that can observe
    32  // changes to an underlying store manager.
    33  func NewMultiwatcher(all *storeManager) *Multiwatcher {
    34  	// Note that we want to be clear about the defaults. So we set zero
    35  	// values explicitly.
    36  	//  used:    false means that the watcher has not been used yet
    37  	//  revno:   0 means that *all* transactions prior to the first
    38  	//           Next() call will be reflected in the deltas.
    39  	//  stopped: false means that the watcher immediately starts off
    40  	//           handling changes.
    41  	return &Multiwatcher{
    42  		all:     all,
    43  		used:    false,
    44  		revno:   0,
    45  		stopped: false,
    46  	}
    47  }
    48  
    49  // Stop stops the watcher.
    50  func (w *Multiwatcher) Stop() error {
    51  	select {
    52  	case w.all.request <- &request{w: w}:
    53  		return nil
    54  	case <-w.all.tomb.Dead():
    55  	}
    56  	return errors.Trace(w.all.tomb.Err())
    57  }
    58  
    59  var ErrStopped = stderrors.New("watcher was stopped")
    60  
    61  // Next retrieves all changes that have happened since the last
    62  // time it was called, blocking until there are some changes available.
    63  //
    64  // The result from the initial call to Next() is different from
    65  // subsequent calls. The latter will reflect changes that have happened
    66  // since the last Next() call. In contrast, the initial Next() call will
    67  // return the deltas that represent the model's complete state at that
    68  // moment, even when the model is empty. In that empty model case an
    69  // empty set of deltas is returned.
    70  func (w *Multiwatcher) Next() ([]multiwatcher.Delta, error) {
    71  	req := &request{
    72  		w:     w,
    73  		reply: make(chan bool),
    74  	}
    75  	if !w.used {
    76  		req.noChanges = make(chan struct{})
    77  		w.used = true
    78  	}
    79  
    80  	select {
    81  	case <-w.all.tomb.Dying():
    82  		return nil, errors.Errorf("shared state watcher was stopped")
    83  	case w.all.request <- req:
    84  	}
    85  
    86  	// TODO(ericsnow) Clean up Multiwatcher/storeManager interaction.
    87  	// Relying on req.reply and req.noChanges here is not an ideal
    88  	// solution. It reflects the level of coupling we have between
    89  	// the Multiwatcher, request, and storeManager types.
    90  	select {
    91  	case <-w.all.tomb.Dying():
    92  		return nil, errors.Errorf("shared state watcher was stopped")
    93  	case ok := <-req.reply:
    94  		if !ok {
    95  			return nil, errors.Trace(ErrStopped)
    96  		}
    97  	case <-req.noChanges:
    98  		return []multiwatcher.Delta{}, nil
    99  	}
   100  	return req.changes, nil
   101  }
   102  
   103  // storeManager holds a shared record of current state and replies to
   104  // requests from Multiwatchers to tell them when it changes.
   105  type storeManager struct {
   106  	tomb tomb.Tomb
   107  
   108  	// backing knows how to fetch information from
   109  	// the underlying state.
   110  	backing Backing
   111  
   112  	// request receives requests from Multiwatcher clients.
   113  	request chan *request
   114  
   115  	// all holds information on everything the storeManager cares about.
   116  	all *multiwatcherStore
   117  
   118  	// Each entry in the waiting map holds a linked list of Next requests
   119  	// outstanding for the associated Multiwatcher.
   120  	waiting map[*Multiwatcher]*request
   121  }
   122  
   123  // Backing is the interface required by the storeManager to access the
   124  // underlying state.
   125  type Backing interface {
   126  
   127  	// GetAll retrieves information about all information
   128  	// known to the Backing and stashes it in the Store.
   129  	GetAll(all *multiwatcherStore) error
   130  
   131  	// Changed informs the backing about a change received
   132  	// from a watcher channel.  The backing is responsible for
   133  	// updating the Store to reflect the change.
   134  	Changed(all *multiwatcherStore, change watcher.Change) error
   135  
   136  	// Watch watches for any changes and sends them
   137  	// on the given channel.
   138  	Watch(in chan<- watcher.Change)
   139  
   140  	// Unwatch stops watching for changes on the
   141  	// given channel.
   142  	Unwatch(in chan<- watcher.Change)
   143  
   144  	// Release cleans up resources opened by the Backing.
   145  	Release() error
   146  }
   147  
   148  // request holds a message from the Multiwatcher to the
   149  // storeManager for some changes. The request will be
   150  // replied to when some changes are available.
   151  type request struct {
   152  	// w holds the Multiwatcher that has originated the request.
   153  	w *Multiwatcher
   154  
   155  	// reply receives a message when deltas are ready.  If reply is
   156  	// nil, the Multiwatcher will be stopped.  If the reply is true,
   157  	// the request has been processed; if false, the Multiwatcher
   158  	// has been stopped,
   159  	reply chan bool
   160  
   161  	// noChanges receives a message when the manager checks for changes
   162  	// and there are none.
   163  	noChanges chan struct{}
   164  
   165  	// On reply, changes will hold changes that have occurred since
   166  	// the last replied-to Next request.
   167  	changes []multiwatcher.Delta
   168  
   169  	// next points to the next request in the list of outstanding
   170  	// requests on a given watcher.  It is used only by the central
   171  	// storeManager goroutine.
   172  	next *request
   173  }
   174  
   175  // newStoreManagerNoRun creates the store manager
   176  // but does not start its run loop.
   177  func newStoreManagerNoRun(backing Backing) *storeManager {
   178  	return &storeManager{
   179  		backing: backing,
   180  		request: make(chan *request),
   181  		all:     newStore(),
   182  		waiting: make(map[*Multiwatcher]*request),
   183  	}
   184  }
   185  
   186  // newStoreManager returns a new storeManager that retrieves information
   187  // using the given backing.
   188  func newStoreManager(backing Backing) *storeManager {
   189  	sm := newStoreManagerNoRun(backing)
   190  	go func() {
   191  		defer sm.tomb.Done()
   192  		// TODO(rog) distinguish between temporary and permanent errors:
   193  		// if we get an error in loop, this logic kill the state's storeManager
   194  		// forever. This currently fits the way we go about things,
   195  		// because we reconnect to the state on any error, but
   196  		// perhaps there are errors we could recover from.
   197  
   198  		err := sm.loop()
   199  		cause := errors.Cause(err)
   200  		// tomb expects ErrDying or ErrStillAlive as
   201  		// exact values, so we need to log and unwrap
   202  		// the error first.
   203  		if err != nil && cause != tomb.ErrDying {
   204  			logger.Infof("store manager loop failed: %v", err)
   205  		}
   206  		sm.tomb.Kill(cause)
   207  	}()
   208  	return sm
   209  }
   210  
   211  func (sm *storeManager) loop() error {
   212  	in := make(chan watcher.Change)
   213  	sm.backing.Watch(in)
   214  	defer sm.backing.Unwatch(in)
   215  	// We have no idea what changes the watcher might be trying to
   216  	// send us while getAll proceeds, but we don't mind, because
   217  	// storeManager.changed is idempotent with respect to both updates
   218  	// and removals.
   219  	// TODO(rog) Perhaps find a way to avoid blocking all other
   220  	// watchers while GetAll is running.
   221  	if err := sm.backing.GetAll(sm.all); err != nil {
   222  		return err
   223  	}
   224  	for {
   225  		select {
   226  		case <-sm.tomb.Dying():
   227  			return errors.Trace(tomb.ErrDying)
   228  		case change := <-in:
   229  			if err := sm.backing.Changed(sm.all, change); err != nil {
   230  				return errors.Trace(err)
   231  			}
   232  		case req := <-sm.request:
   233  			sm.handle(req)
   234  		}
   235  		sm.respond()
   236  	}
   237  }
   238  
   239  // Stop stops the storeManager.
   240  func (sm *storeManager) Stop() error {
   241  	sm.tomb.Kill(nil)
   242  	return errors.Trace(sm.tomb.Wait())
   243  }
   244  
   245  // handle processes a request from a Multiwatcher to the storeManager.
   246  func (sm *storeManager) handle(req *request) {
   247  	if req.w.stopped {
   248  		// The watcher has previously been stopped.
   249  		if req.reply != nil {
   250  			select {
   251  			case req.reply <- false:
   252  			case <-sm.tomb.Dying():
   253  			}
   254  		}
   255  		return
   256  	}
   257  	if req.reply == nil {
   258  		// This is a request to stop the watcher.
   259  		for req := sm.waiting[req.w]; req != nil; req = req.next {
   260  			select {
   261  			case req.reply <- false:
   262  			case <-sm.tomb.Dying():
   263  			}
   264  		}
   265  		delete(sm.waiting, req.w)
   266  		req.w.stopped = true
   267  		sm.leave(req.w)
   268  		return
   269  	}
   270  	// Add request to head of list.
   271  	req.next = sm.waiting[req.w]
   272  	sm.waiting[req.w] = req
   273  }
   274  
   275  // respond responds to all outstanding requests that are satisfiable.
   276  func (sm *storeManager) respond() {
   277  	for w, req := range sm.waiting {
   278  		revno := w.revno
   279  		changes := sm.all.ChangesSince(revno)
   280  		if len(changes) == 0 {
   281  			if req.noChanges != nil {
   282  				req.noChanges <- struct{}{}
   283  				sm.removeWaitingReq(w, req)
   284  			}
   285  			continue
   286  		}
   287  
   288  		req.changes = changes
   289  		w.revno = sm.all.latestRevno
   290  		req.reply <- true
   291  		sm.removeWaitingReq(w, req)
   292  		sm.seen(revno)
   293  	}
   294  }
   295  
   296  func (sm *storeManager) removeWaitingReq(w *Multiwatcher, req *request) {
   297  	if req := req.next; req == nil {
   298  		// Last request for this watcher.
   299  		delete(sm.waiting, w)
   300  	} else {
   301  		sm.waiting[w] = req
   302  	}
   303  }
   304  
   305  // seen states that a Multiwatcher has just been given information about
   306  // all entities newer than the given revno.  We assume it has already
   307  // seen all the older entities.
   308  func (sm *storeManager) seen(revno int64) {
   309  	for e := sm.all.list.Front(); e != nil; {
   310  		next := e.Next()
   311  		entry := e.Value.(*entityEntry)
   312  		if entry.revno <= revno {
   313  			break
   314  		}
   315  		if entry.creationRevno > revno {
   316  			if !entry.removed {
   317  				// This is a new entity that hasn't been seen yet,
   318  				// so increment the entry's refCount.
   319  				entry.refCount++
   320  			}
   321  		} else if entry.removed {
   322  			// This is an entity that we previously saw, but
   323  			// has now been removed, so decrement its refCount, removing
   324  			// the entity if nothing else is waiting to be notified that it's
   325  			// gone.
   326  			sm.all.decRef(entry)
   327  		}
   328  		e = next
   329  	}
   330  }
   331  
   332  // leave is called when the given watcher leaves.  It decrements the reference
   333  // counts of any entities that have been seen by the watcher.
   334  func (sm *storeManager) leave(w *Multiwatcher) {
   335  	for e := sm.all.list.Front(); e != nil; {
   336  		next := e.Next()
   337  		entry := e.Value.(*entityEntry)
   338  		if entry.creationRevno <= w.revno {
   339  			// The watcher has seen this entry.
   340  			if entry.removed && entry.revno <= w.revno {
   341  				// The entity has been removed and the
   342  				// watcher has already been informed of that,
   343  				// so its refcount has already been decremented.
   344  				e = next
   345  				continue
   346  			}
   347  			sm.all.decRef(entry)
   348  		}
   349  		e = next
   350  	}
   351  }
   352  
   353  // entityEntry holds an entry in the linked list of all entities known
   354  // to a Multiwatcher.
   355  type entityEntry struct {
   356  	// The revno holds the local idea of the latest change to the
   357  	// given entity.  It is not the same as the transaction revno -
   358  	// this means we can unconditionally move a newly fetched entity
   359  	// to the front of the list without worrying if the revno has
   360  	// changed since the watcher reported it.
   361  	revno int64
   362  
   363  	// creationRevno holds the revision number when the
   364  	// entity was created.
   365  	creationRevno int64
   366  
   367  	// removed marks whether the entity has been removed.
   368  	removed bool
   369  
   370  	// refCount holds a count of the number of watchers that
   371  	// have seen this entity. When the entity is marked as removed,
   372  	// the ref count is decremented whenever a Multiwatcher that
   373  	// has previously seen the entry now sees that it has been removed;
   374  	// the entry will be deleted when all such Multiwatchers have
   375  	// been notified.
   376  	refCount int
   377  
   378  	// info holds the actual information on the entity.
   379  	info multiwatcher.EntityInfo
   380  }
   381  
   382  // multiwatcherStore holds a list of all entities known
   383  // to a Multiwatcher.
   384  type multiwatcherStore struct {
   385  	latestRevno int64
   386  	entities    map[interface{}]*list.Element
   387  	list        *list.List
   388  }
   389  
   390  // newStore returns an Store instance holding information about the
   391  // current state of all entities in the model.
   392  // It is only exposed here for testing purposes.
   393  func newStore() *multiwatcherStore {
   394  	return &multiwatcherStore{
   395  		entities: make(map[interface{}]*list.Element),
   396  		list:     list.New(),
   397  	}
   398  }
   399  
   400  // All returns all the entities stored in the Store,
   401  // oldest first. It is only exposed for testing purposes.
   402  func (a *multiwatcherStore) All() []multiwatcher.EntityInfo {
   403  	entities := make([]multiwatcher.EntityInfo, 0, a.list.Len())
   404  	for e := a.list.Front(); e != nil; e = e.Next() {
   405  		entry := e.Value.(*entityEntry)
   406  		if entry.removed {
   407  			continue
   408  		}
   409  		entities = append(entities, entry.info)
   410  	}
   411  	return entities
   412  }
   413  
   414  // add adds a new entity with the given id and associated
   415  // information to the list.
   416  func (a *multiwatcherStore) add(id interface{}, info multiwatcher.EntityInfo) {
   417  	if _, ok := a.entities[id]; ok {
   418  		panic("adding new entry with duplicate id")
   419  	}
   420  	a.latestRevno++
   421  	entry := &entityEntry{
   422  		info:          info,
   423  		revno:         a.latestRevno,
   424  		creationRevno: a.latestRevno,
   425  	}
   426  	a.entities[id] = a.list.PushFront(entry)
   427  }
   428  
   429  // decRef decrements the reference count of an entry within the list,
   430  // deleting it if it becomes zero and the entry is removed.
   431  func (a *multiwatcherStore) decRef(entry *entityEntry) {
   432  	if entry.refCount--; entry.refCount > 0 {
   433  		return
   434  	}
   435  	if entry.refCount < 0 {
   436  		panic("negative reference count")
   437  	}
   438  	if !entry.removed {
   439  		return
   440  	}
   441  	id := entry.info.EntityId()
   442  	elem, ok := a.entities[id]
   443  	if !ok {
   444  		panic("delete of non-existent entry")
   445  	}
   446  	delete(a.entities, id)
   447  	a.list.Remove(elem)
   448  }
   449  
   450  // delete deletes the entry with the given info id.
   451  func (a *multiwatcherStore) delete(id multiwatcher.EntityId) {
   452  	elem, ok := a.entities[id]
   453  	if !ok {
   454  		return
   455  	}
   456  	delete(a.entities, id)
   457  	a.list.Remove(elem)
   458  }
   459  
   460  // Remove marks that the entity with the given id has
   461  // been removed from the backing. If nothing has seen the
   462  // entity, then we delete it immediately.
   463  func (a *multiwatcherStore) Remove(id multiwatcher.EntityId) {
   464  	if elem := a.entities[id]; elem != nil {
   465  		entry := elem.Value.(*entityEntry)
   466  		if entry.removed {
   467  			return
   468  		}
   469  		a.latestRevno++
   470  		if entry.refCount == 0 {
   471  			a.delete(id)
   472  			return
   473  		}
   474  		entry.revno = a.latestRevno
   475  		entry.removed = true
   476  		a.list.MoveToFront(elem)
   477  	}
   478  }
   479  
   480  // Update updates the information for the given entity.
   481  func (a *multiwatcherStore) Update(info multiwatcher.EntityInfo) {
   482  	id := info.EntityId()
   483  	elem, ok := a.entities[id]
   484  	if !ok {
   485  		a.add(id, info)
   486  		return
   487  	}
   488  	entry := elem.Value.(*entityEntry)
   489  	// Nothing has changed, so change nothing.
   490  	// TODO(rog) do the comparison more efficiently.
   491  	if reflect.DeepEqual(info, entry.info) {
   492  		return
   493  	}
   494  	// We already know about the entity; update its doc.
   495  	a.latestRevno++
   496  	entry.revno = a.latestRevno
   497  	entry.info = info
   498  	// The app might have been removed and re-added.
   499  	entry.removed = false
   500  	a.list.MoveToFront(elem)
   501  }
   502  
   503  // Get returns the stored entity with the given id, or nil if none was found.
   504  // The contents of the returned entity MUST not be changed.
   505  func (a *multiwatcherStore) Get(id multiwatcher.EntityId) multiwatcher.EntityInfo {
   506  	e, ok := a.entities[id]
   507  	if !ok {
   508  		return nil
   509  	}
   510  	return e.Value.(*entityEntry).info
   511  }
   512  
   513  // ChangesSince returns any changes that have occurred since
   514  // the given revno, oldest first.
   515  func (a *multiwatcherStore) ChangesSince(revno int64) []multiwatcher.Delta {
   516  	e := a.list.Front()
   517  	n := 0
   518  	for ; e != nil; e = e.Next() {
   519  		entry := e.Value.(*entityEntry)
   520  		if entry.revno <= revno {
   521  			break
   522  		}
   523  		n++
   524  	}
   525  	if e != nil {
   526  		// We've found an element that we've already seen.
   527  		e = e.Prev()
   528  	} else {
   529  		// We haven't seen any elements, so we want all of them.
   530  		e = a.list.Back()
   531  		n++
   532  	}
   533  	changes := make([]multiwatcher.Delta, 0, n)
   534  	for ; e != nil; e = e.Prev() {
   535  		entry := e.Value.(*entityEntry)
   536  		if entry.removed && entry.creationRevno > revno {
   537  			// Don't include entries that have been created
   538  			// and removed since the revno.
   539  			continue
   540  		}
   541  		changes = append(changes, multiwatcher.Delta{
   542  			Removed: entry.removed,
   543  			Entity:  entry.info,
   544  		})
   545  	}
   546  	return changes
   547  }