github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/manager/state/store/memory.go (about)

     1  package store
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"runtime"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/docker/go-events"
    15  	"github.com/docker/go-metrics"
    16  	"github.com/docker/swarmkit/api"
    17  	pb "github.com/docker/swarmkit/api"
    18  	"github.com/docker/swarmkit/manager/state"
    19  	"github.com/docker/swarmkit/watch"
    20  	gogotypes "github.com/gogo/protobuf/types"
    21  	memdb "github.com/hashicorp/go-memdb"
    22  )
    23  
    24  const (
    25  	indexID           = "id"
    26  	indexName         = "name"
    27  	indexRuntime      = "runtime"
    28  	indexServiceID    = "serviceid"
    29  	indexNodeID       = "nodeid"
    30  	indexSlot         = "slot"
    31  	indexDesiredState = "desiredstate"
    32  	indexTaskState    = "taskstate"
    33  	indexRole         = "role"
    34  	indexMembership   = "membership"
    35  	indexNetwork      = "network"
    36  	indexSecret       = "secret"
    37  	indexConfig       = "config"
    38  	indexKind         = "kind"
    39  	indexCustom       = "custom"
    40  
    41  	prefix = "_prefix"
    42  
    43  	// MaxChangesPerTransaction is the number of changes after which a new
    44  	// transaction should be started within Batch.
    45  	MaxChangesPerTransaction = 200
    46  
    47  	// MaxTransactionBytes is the maximum serialized transaction size.
    48  	MaxTransactionBytes = 1.5 * 1024 * 1024
    49  )
    50  
    51  var (
    52  	// ErrExist is returned by create operations if the provided ID is already
    53  	// taken.
    54  	ErrExist = errors.New("object already exists")
    55  
    56  	// ErrNotExist is returned by altering operations (update, delete) if the
    57  	// provided ID is not found.
    58  	ErrNotExist = errors.New("object does not exist")
    59  
    60  	// ErrNameConflict is returned by create/update if the object name is
    61  	// already in use by another object.
    62  	ErrNameConflict = errors.New("name conflicts with an existing object")
    63  
    64  	// ErrInvalidFindBy is returned if an unrecognized type is passed to Find.
    65  	ErrInvalidFindBy = errors.New("invalid find argument type")
    66  
    67  	// ErrSequenceConflict is returned when trying to update an object
    68  	// whose sequence information does not match the object in the store's.
    69  	ErrSequenceConflict = errors.New("update out of sequence")
    70  
    71  	objectStorers []ObjectStoreConfig
    72  	schema        = &memdb.DBSchema{
    73  		Tables: map[string]*memdb.TableSchema{},
    74  	}
    75  	errUnknownStoreAction = errors.New("unknown store action")
    76  
    77  	// WedgeTimeout is the maximum amount of time the store lock may be
    78  	// held before declaring a suspected deadlock.
    79  	WedgeTimeout = 30 * time.Second
    80  
    81  	// update()/write tx latency timer.
    82  	updateLatencyTimer metrics.Timer
    83  
    84  	// view()/read tx latency timer.
    85  	viewLatencyTimer metrics.Timer
    86  
    87  	// lookup() latency timer.
    88  	lookupLatencyTimer metrics.Timer
    89  
    90  	// Batch() latency timer.
    91  	batchLatencyTimer metrics.Timer
    92  
    93  	// timer to capture the duration for which the memory store mutex is locked.
    94  	storeLockDurationTimer metrics.Timer
    95  )
    96  
    97  func init() {
    98  	ns := metrics.NewNamespace("swarm", "store", nil)
    99  	updateLatencyTimer = ns.NewTimer("write_tx_latency",
   100  		"Raft store write tx latency.")
   101  	viewLatencyTimer = ns.NewTimer("read_tx_latency",
   102  		"Raft store read tx latency.")
   103  	lookupLatencyTimer = ns.NewTimer("lookup_latency",
   104  		"Raft store read latency.")
   105  	batchLatencyTimer = ns.NewTimer("batch_latency",
   106  		"Raft store batch latency.")
   107  	storeLockDurationTimer = ns.NewTimer("memory_store_lock_duration",
   108  		"Duration for which the raft memory store lock was held.")
   109  	metrics.Register(ns)
   110  }
   111  
   112  func register(os ObjectStoreConfig) {
   113  	objectStorers = append(objectStorers, os)
   114  	schema.Tables[os.Table.Name] = os.Table
   115  }
   116  
   117  // timedMutex wraps a sync.Mutex, and keeps track of when it was locked.
   118  type timedMutex struct {
   119  	sync.Mutex
   120  	lockedAt atomic.Value
   121  }
   122  
   123  func (m *timedMutex) Lock() {
   124  	m.Mutex.Lock()
   125  	m.lockedAt.Store(time.Now())
   126  }
   127  
   128  // Unlocks the timedMutex and captures the duration
   129  // for which it was locked in a metric.
   130  func (m *timedMutex) Unlock() {
   131  	unlockedTimestamp := m.lockedAt.Load()
   132  	m.lockedAt.Store(time.Time{})
   133  	m.Mutex.Unlock()
   134  	lockedFor := time.Since(unlockedTimestamp.(time.Time))
   135  	storeLockDurationTimer.Update(lockedFor)
   136  }
   137  
   138  func (m *timedMutex) LockedAt() time.Time {
   139  	lockedTimestamp := m.lockedAt.Load()
   140  	if lockedTimestamp == nil {
   141  		return time.Time{}
   142  	}
   143  	return lockedTimestamp.(time.Time)
   144  }
   145  
   146  // MemoryStore is a concurrency-safe, in-memory implementation of the Store
   147  // interface.
   148  type MemoryStore struct {
   149  	// updateLock must be held during an update transaction.
   150  	updateLock timedMutex
   151  
   152  	memDB *memdb.MemDB
   153  	queue *watch.Queue
   154  
   155  	proposer state.Proposer
   156  }
   157  
   158  // NewMemoryStore returns an in-memory store. The argument is an optional
   159  // Proposer which will be used to propagate changes to other members in a
   160  // cluster.
   161  func NewMemoryStore(proposer state.Proposer) *MemoryStore {
   162  	memDB, err := memdb.NewMemDB(schema)
   163  	if err != nil {
   164  		// This shouldn't fail
   165  		panic(err)
   166  	}
   167  
   168  	return &MemoryStore{
   169  		memDB:    memDB,
   170  		queue:    watch.NewQueue(),
   171  		proposer: proposer,
   172  	}
   173  }
   174  
   175  // Close closes the memory store and frees its associated resources.
   176  func (s *MemoryStore) Close() error {
   177  	return s.queue.Close()
   178  }
   179  
   180  func fromArgs(args ...interface{}) ([]byte, error) {
   181  	if len(args) != 1 {
   182  		return nil, fmt.Errorf("must provide only a single argument")
   183  	}
   184  	arg, ok := args[0].(string)
   185  	if !ok {
   186  		return nil, fmt.Errorf("argument must be a string: %#v", args[0])
   187  	}
   188  	// Add the null character as a terminator
   189  	arg += "\x00"
   190  	return []byte(arg), nil
   191  }
   192  
   193  func prefixFromArgs(args ...interface{}) ([]byte, error) {
   194  	val, err := fromArgs(args...)
   195  	if err != nil {
   196  		return nil, err
   197  	}
   198  
   199  	// Strip the null terminator, the rest is a prefix
   200  	n := len(val)
   201  	if n > 0 {
   202  		return val[:n-1], nil
   203  	}
   204  	return val, nil
   205  }
   206  
   207  // ReadTx is a read transaction. Note that transaction does not imply
   208  // any internal batching. It only means that the transaction presents a
   209  // consistent view of the data that cannot be affected by other
   210  // transactions.
   211  type ReadTx interface {
   212  	lookup(table, index, id string) api.StoreObject
   213  	get(table, id string) api.StoreObject
   214  	find(table string, by By, checkType func(By) error, appendResult func(api.StoreObject)) error
   215  }
   216  
   217  type readTx struct {
   218  	memDBTx *memdb.Txn
   219  }
   220  
   221  // View executes a read transaction.
   222  func (s *MemoryStore) View(cb func(ReadTx)) {
   223  	defer metrics.StartTimer(viewLatencyTimer)()
   224  	memDBTx := s.memDB.Txn(false)
   225  
   226  	readTx := readTx{
   227  		memDBTx: memDBTx,
   228  	}
   229  	cb(readTx)
   230  	memDBTx.Commit()
   231  }
   232  
   233  // Tx is a read/write transaction. Note that transaction does not imply
   234  // any internal batching. The purpose of this transaction is to give the
   235  // user a guarantee that its changes won't be visible to other transactions
   236  // until the transaction is over.
   237  type Tx interface {
   238  	ReadTx
   239  	create(table string, o api.StoreObject) error
   240  	update(table string, o api.StoreObject) error
   241  	delete(table, id string) error
   242  }
   243  
   244  type tx struct {
   245  	readTx
   246  	curVersion *api.Version
   247  	changelist []api.Event
   248  }
   249  
   250  // changelistBetweenVersions returns the changes after "from" up to and
   251  // including "to".
   252  func (s *MemoryStore) changelistBetweenVersions(from, to api.Version) ([]api.Event, error) {
   253  	if s.proposer == nil {
   254  		return nil, errors.New("store does not support versioning")
   255  	}
   256  	changes, err := s.proposer.ChangesBetween(from, to)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	var changelist []api.Event
   262  
   263  	for _, change := range changes {
   264  		for _, sa := range change.StoreActions {
   265  			event, err := api.EventFromStoreAction(sa, nil)
   266  			if err != nil {
   267  				return nil, err
   268  			}
   269  			changelist = append(changelist, event)
   270  		}
   271  		changelist = append(changelist, state.EventCommit{Version: change.Version.Copy()})
   272  	}
   273  
   274  	return changelist, nil
   275  }
   276  
   277  // ApplyStoreActions updates a store based on StoreAction messages.
   278  func (s *MemoryStore) ApplyStoreActions(actions []api.StoreAction) error {
   279  	s.updateLock.Lock()
   280  	memDBTx := s.memDB.Txn(true)
   281  
   282  	tx := tx{
   283  		readTx: readTx{
   284  			memDBTx: memDBTx,
   285  		},
   286  	}
   287  
   288  	for _, sa := range actions {
   289  		if err := applyStoreAction(&tx, sa); err != nil {
   290  			memDBTx.Abort()
   291  			s.updateLock.Unlock()
   292  			return err
   293  		}
   294  	}
   295  
   296  	memDBTx.Commit()
   297  
   298  	for _, c := range tx.changelist {
   299  		s.queue.Publish(c)
   300  	}
   301  	if len(tx.changelist) != 0 {
   302  		s.queue.Publish(state.EventCommit{})
   303  	}
   304  	s.updateLock.Unlock()
   305  	return nil
   306  }
   307  
   308  func applyStoreAction(tx Tx, sa api.StoreAction) error {
   309  	for _, os := range objectStorers {
   310  		err := os.ApplyStoreAction(tx, sa)
   311  		if err != errUnknownStoreAction {
   312  			return err
   313  		}
   314  	}
   315  
   316  	return errors.New("unrecognized action type")
   317  }
   318  
   319  func (s *MemoryStore) update(proposer state.Proposer, cb func(Tx) error) error {
   320  	defer metrics.StartTimer(updateLatencyTimer)()
   321  	s.updateLock.Lock()
   322  	memDBTx := s.memDB.Txn(true)
   323  
   324  	var curVersion *api.Version
   325  
   326  	if proposer != nil {
   327  		curVersion = proposer.GetVersion()
   328  	}
   329  
   330  	var tx tx
   331  	tx.init(memDBTx, curVersion)
   332  
   333  	err := cb(&tx)
   334  
   335  	if err == nil {
   336  		if proposer == nil {
   337  			memDBTx.Commit()
   338  		} else {
   339  			var sa []api.StoreAction
   340  			sa, err = tx.changelistStoreActions()
   341  
   342  			if err == nil {
   343  				if len(sa) != 0 {
   344  					err = proposer.ProposeValue(context.Background(), sa, func() {
   345  						memDBTx.Commit()
   346  					})
   347  				} else {
   348  					memDBTx.Commit()
   349  				}
   350  			}
   351  		}
   352  	}
   353  
   354  	if err == nil {
   355  		for _, c := range tx.changelist {
   356  			s.queue.Publish(c)
   357  		}
   358  		if len(tx.changelist) != 0 {
   359  			if proposer != nil {
   360  				curVersion = proposer.GetVersion()
   361  			}
   362  
   363  			s.queue.Publish(state.EventCommit{Version: curVersion})
   364  		}
   365  	} else {
   366  		memDBTx.Abort()
   367  	}
   368  	s.updateLock.Unlock()
   369  	return err
   370  }
   371  
   372  func (s *MemoryStore) updateLocal(cb func(Tx) error) error {
   373  	return s.update(nil, cb)
   374  }
   375  
   376  // Update executes a read/write transaction.
   377  func (s *MemoryStore) Update(cb func(Tx) error) error {
   378  	return s.update(s.proposer, cb)
   379  }
   380  
   381  // Batch provides a mechanism to batch updates to a store.
   382  type Batch struct {
   383  	tx    tx
   384  	store *MemoryStore
   385  	// applied counts the times Update has run successfully
   386  	applied int
   387  	// transactionSizeEstimate is the running count of the size of the
   388  	// current transaction.
   389  	transactionSizeEstimate int
   390  	// changelistLen is the last known length of the transaction's
   391  	// changelist.
   392  	changelistLen int
   393  	err           error
   394  }
   395  
   396  // Update adds a single change to a batch. Each call to Update is atomic, but
   397  // different calls to Update may be spread across multiple transactions to
   398  // circumvent transaction size limits.
   399  func (batch *Batch) Update(cb func(Tx) error) error {
   400  	if batch.err != nil {
   401  		return batch.err
   402  	}
   403  
   404  	if err := cb(&batch.tx); err != nil {
   405  		return err
   406  	}
   407  
   408  	batch.applied++
   409  
   410  	for batch.changelistLen < len(batch.tx.changelist) {
   411  		sa, err := api.NewStoreAction(batch.tx.changelist[batch.changelistLen])
   412  		if err != nil {
   413  			return err
   414  		}
   415  		batch.transactionSizeEstimate += sa.Size()
   416  		batch.changelistLen++
   417  	}
   418  
   419  	if batch.changelistLen >= MaxChangesPerTransaction || batch.transactionSizeEstimate >= (MaxTransactionBytes*3)/4 {
   420  		if err := batch.commit(); err != nil {
   421  			return err
   422  		}
   423  
   424  		// Yield the update lock
   425  		batch.store.updateLock.Unlock()
   426  		runtime.Gosched()
   427  		batch.store.updateLock.Lock()
   428  
   429  		batch.newTx()
   430  	}
   431  
   432  	return nil
   433  }
   434  
   435  func (batch *Batch) newTx() {
   436  	var curVersion *api.Version
   437  
   438  	if batch.store.proposer != nil {
   439  		curVersion = batch.store.proposer.GetVersion()
   440  	}
   441  
   442  	batch.tx.init(batch.store.memDB.Txn(true), curVersion)
   443  	batch.transactionSizeEstimate = 0
   444  	batch.changelistLen = 0
   445  }
   446  
   447  func (batch *Batch) commit() error {
   448  	if batch.store.proposer != nil {
   449  		var sa []api.StoreAction
   450  		sa, batch.err = batch.tx.changelistStoreActions()
   451  
   452  		if batch.err == nil {
   453  			if len(sa) != 0 {
   454  				batch.err = batch.store.proposer.ProposeValue(context.Background(), sa, func() {
   455  					batch.tx.memDBTx.Commit()
   456  				})
   457  			} else {
   458  				batch.tx.memDBTx.Commit()
   459  			}
   460  		}
   461  	} else {
   462  		batch.tx.memDBTx.Commit()
   463  	}
   464  
   465  	if batch.err != nil {
   466  		batch.tx.memDBTx.Abort()
   467  		return batch.err
   468  	}
   469  
   470  	for _, c := range batch.tx.changelist {
   471  		batch.store.queue.Publish(c)
   472  	}
   473  	if len(batch.tx.changelist) != 0 {
   474  		batch.store.queue.Publish(state.EventCommit{})
   475  	}
   476  
   477  	return nil
   478  }
   479  
   480  // Batch performs one or more transactions that allow reads and writes
   481  // It invokes a callback that is passed a Batch object. The callback may
   482  // call batch.Update for each change it wants to make as part of the
   483  // batch. The changes in the batch may be split over multiple
   484  // transactions if necessary to keep transactions below the size limit.
   485  // Batch holds a lock over the state, but will yield this lock every
   486  // it creates a new transaction to allow other writers to proceed.
   487  // Thus, unrelated changes to the state may occur between calls to
   488  // batch.Update.
   489  //
   490  // This method allows the caller to iterate over a data set and apply
   491  // changes in sequence without holding the store write lock for an
   492  // excessive time, or producing a transaction that exceeds the maximum
   493  // size.
   494  //
   495  // If Batch returns an error, no guarantees are made about how many updates
   496  // were committed successfully.
   497  func (s *MemoryStore) Batch(cb func(*Batch) error) error {
   498  	defer metrics.StartTimer(batchLatencyTimer)()
   499  	s.updateLock.Lock()
   500  
   501  	batch := Batch{
   502  		store: s,
   503  	}
   504  	batch.newTx()
   505  
   506  	if err := cb(&batch); err != nil {
   507  		batch.tx.memDBTx.Abort()
   508  		s.updateLock.Unlock()
   509  		return err
   510  	}
   511  
   512  	err := batch.commit()
   513  	s.updateLock.Unlock()
   514  	return err
   515  }
   516  
   517  func (tx *tx) init(memDBTx *memdb.Txn, curVersion *api.Version) {
   518  	tx.memDBTx = memDBTx
   519  	tx.curVersion = curVersion
   520  	tx.changelist = nil
   521  }
   522  
   523  func (tx tx) changelistStoreActions() ([]api.StoreAction, error) {
   524  	var actions []api.StoreAction
   525  
   526  	for _, c := range tx.changelist {
   527  		sa, err := api.NewStoreAction(c)
   528  		if err != nil {
   529  			return nil, err
   530  		}
   531  		actions = append(actions, sa)
   532  	}
   533  
   534  	return actions, nil
   535  }
   536  
   537  // lookup is an internal typed wrapper around memdb.
   538  func (tx readTx) lookup(table, index, id string) api.StoreObject {
   539  	defer metrics.StartTimer(lookupLatencyTimer)()
   540  	j, err := tx.memDBTx.First(table, index, id)
   541  	if err != nil {
   542  		return nil
   543  	}
   544  	if j != nil {
   545  		return j.(api.StoreObject)
   546  	}
   547  	return nil
   548  }
   549  
   550  // create adds a new object to the store.
   551  // Returns ErrExist if the ID is already taken.
   552  func (tx *tx) create(table string, o api.StoreObject) error {
   553  	if tx.lookup(table, indexID, o.GetID()) != nil {
   554  		return ErrExist
   555  	}
   556  
   557  	copy := o.CopyStoreObject()
   558  	meta := copy.GetMeta()
   559  	if err := touchMeta(&meta, tx.curVersion); err != nil {
   560  		return err
   561  	}
   562  	copy.SetMeta(meta)
   563  
   564  	err := tx.memDBTx.Insert(table, copy)
   565  	if err == nil {
   566  		tx.changelist = append(tx.changelist, copy.EventCreate())
   567  		o.SetMeta(meta)
   568  	}
   569  	return err
   570  }
   571  
   572  // Update updates an existing object in the store.
   573  // Returns ErrNotExist if the object doesn't exist.
   574  func (tx *tx) update(table string, o api.StoreObject) error {
   575  	oldN := tx.lookup(table, indexID, o.GetID())
   576  	if oldN == nil {
   577  		return ErrNotExist
   578  	}
   579  
   580  	meta := o.GetMeta()
   581  
   582  	if tx.curVersion != nil {
   583  		if oldN.GetMeta().Version != meta.Version {
   584  			return ErrSequenceConflict
   585  		}
   586  	}
   587  
   588  	copy := o.CopyStoreObject()
   589  	if err := touchMeta(&meta, tx.curVersion); err != nil {
   590  		return err
   591  	}
   592  	copy.SetMeta(meta)
   593  
   594  	err := tx.memDBTx.Insert(table, copy)
   595  	if err == nil {
   596  		tx.changelist = append(tx.changelist, copy.EventUpdate(oldN))
   597  		o.SetMeta(meta)
   598  	}
   599  	return err
   600  }
   601  
   602  // Delete removes an object from the store.
   603  // Returns ErrNotExist if the object doesn't exist.
   604  func (tx *tx) delete(table, id string) error {
   605  	n := tx.lookup(table, indexID, id)
   606  	if n == nil {
   607  		return ErrNotExist
   608  	}
   609  
   610  	err := tx.memDBTx.Delete(table, n)
   611  	if err == nil {
   612  		tx.changelist = append(tx.changelist, n.EventDelete())
   613  	}
   614  	return err
   615  }
   616  
   617  // Get looks up an object by ID.
   618  // Returns nil if the object doesn't exist.
   619  func (tx readTx) get(table, id string) api.StoreObject {
   620  	o := tx.lookup(table, indexID, id)
   621  	if o == nil {
   622  		return nil
   623  	}
   624  	return o.CopyStoreObject()
   625  }
   626  
   627  // findIterators returns a slice of iterators. The union of items from these
   628  // iterators provides the result of the query.
   629  func (tx readTx) findIterators(table string, by By, checkType func(By) error) ([]memdb.ResultIterator, error) {
   630  	switch by.(type) {
   631  	case byAll, orCombinator: // generic types
   632  	default: // all other types
   633  		if err := checkType(by); err != nil {
   634  			return nil, err
   635  		}
   636  	}
   637  
   638  	switch v := by.(type) {
   639  	case byAll:
   640  		it, err := tx.memDBTx.Get(table, indexID)
   641  		if err != nil {
   642  			return nil, err
   643  		}
   644  		return []memdb.ResultIterator{it}, nil
   645  	case orCombinator:
   646  		var iters []memdb.ResultIterator
   647  		for _, subBy := range v.bys {
   648  			it, err := tx.findIterators(table, subBy, checkType)
   649  			if err != nil {
   650  				return nil, err
   651  			}
   652  			iters = append(iters, it...)
   653  		}
   654  		return iters, nil
   655  	case byName:
   656  		it, err := tx.memDBTx.Get(table, indexName, strings.ToLower(string(v)))
   657  		if err != nil {
   658  			return nil, err
   659  		}
   660  		return []memdb.ResultIterator{it}, nil
   661  	case byIDPrefix:
   662  		it, err := tx.memDBTx.Get(table, indexID+prefix, string(v))
   663  		if err != nil {
   664  			return nil, err
   665  		}
   666  		return []memdb.ResultIterator{it}, nil
   667  	case byNamePrefix:
   668  		it, err := tx.memDBTx.Get(table, indexName+prefix, strings.ToLower(string(v)))
   669  		if err != nil {
   670  			return nil, err
   671  		}
   672  		return []memdb.ResultIterator{it}, nil
   673  	case byRuntime:
   674  		it, err := tx.memDBTx.Get(table, indexRuntime, string(v))
   675  		if err != nil {
   676  			return nil, err
   677  		}
   678  		return []memdb.ResultIterator{it}, nil
   679  	case byNode:
   680  		it, err := tx.memDBTx.Get(table, indexNodeID, string(v))
   681  		if err != nil {
   682  			return nil, err
   683  		}
   684  		return []memdb.ResultIterator{it}, nil
   685  	case byService:
   686  		it, err := tx.memDBTx.Get(table, indexServiceID, string(v))
   687  		if err != nil {
   688  			return nil, err
   689  		}
   690  		return []memdb.ResultIterator{it}, nil
   691  	case bySlot:
   692  		it, err := tx.memDBTx.Get(table, indexSlot, v.serviceID+"\x00"+strconv.FormatUint(v.slot, 10))
   693  		if err != nil {
   694  			return nil, err
   695  		}
   696  		return []memdb.ResultIterator{it}, nil
   697  	case byDesiredState:
   698  		it, err := tx.memDBTx.Get(table, indexDesiredState, strconv.FormatInt(int64(v), 10))
   699  		if err != nil {
   700  			return nil, err
   701  		}
   702  		return []memdb.ResultIterator{it}, nil
   703  	case byTaskState:
   704  		it, err := tx.memDBTx.Get(table, indexTaskState, strconv.FormatInt(int64(v), 10))
   705  		if err != nil {
   706  			return nil, err
   707  		}
   708  		return []memdb.ResultIterator{it}, nil
   709  	case byRole:
   710  		it, err := tx.memDBTx.Get(table, indexRole, strconv.FormatInt(int64(v), 10))
   711  		if err != nil {
   712  			return nil, err
   713  		}
   714  		return []memdb.ResultIterator{it}, nil
   715  	case byMembership:
   716  		it, err := tx.memDBTx.Get(table, indexMembership, strconv.FormatInt(int64(v), 10))
   717  		if err != nil {
   718  			return nil, err
   719  		}
   720  		return []memdb.ResultIterator{it}, nil
   721  	case byReferencedNetworkID:
   722  		it, err := tx.memDBTx.Get(table, indexNetwork, string(v))
   723  		if err != nil {
   724  			return nil, err
   725  		}
   726  		return []memdb.ResultIterator{it}, nil
   727  	case byReferencedSecretID:
   728  		it, err := tx.memDBTx.Get(table, indexSecret, string(v))
   729  		if err != nil {
   730  			return nil, err
   731  		}
   732  		return []memdb.ResultIterator{it}, nil
   733  	case byReferencedConfigID:
   734  		it, err := tx.memDBTx.Get(table, indexConfig, string(v))
   735  		if err != nil {
   736  			return nil, err
   737  		}
   738  		return []memdb.ResultIterator{it}, nil
   739  	case byKind:
   740  		it, err := tx.memDBTx.Get(table, indexKind, string(v))
   741  		if err != nil {
   742  			return nil, err
   743  		}
   744  		return []memdb.ResultIterator{it}, nil
   745  	case byCustom:
   746  		var key string
   747  		if v.objType != "" {
   748  			key = v.objType + "|" + v.index + "|" + v.value
   749  		} else {
   750  			key = v.index + "|" + v.value
   751  		}
   752  		it, err := tx.memDBTx.Get(table, indexCustom, key)
   753  		if err != nil {
   754  			return nil, err
   755  		}
   756  		return []memdb.ResultIterator{it}, nil
   757  	case byCustomPrefix:
   758  		var key string
   759  		if v.objType != "" {
   760  			key = v.objType + "|" + v.index + "|" + v.value
   761  		} else {
   762  			key = v.index + "|" + v.value
   763  		}
   764  		it, err := tx.memDBTx.Get(table, indexCustom+prefix, key)
   765  		if err != nil {
   766  			return nil, err
   767  		}
   768  		return []memdb.ResultIterator{it}, nil
   769  	default:
   770  		return nil, ErrInvalidFindBy
   771  	}
   772  }
   773  
   774  // find selects a set of objects calls a callback for each matching object.
   775  func (tx readTx) find(table string, by By, checkType func(By) error, appendResult func(api.StoreObject)) error {
   776  	fromResultIterators := func(its ...memdb.ResultIterator) {
   777  		ids := make(map[string]struct{})
   778  		for _, it := range its {
   779  			for {
   780  				obj := it.Next()
   781  				if obj == nil {
   782  					break
   783  				}
   784  				o := obj.(api.StoreObject)
   785  				id := o.GetID()
   786  				if _, exists := ids[id]; !exists {
   787  					appendResult(o.CopyStoreObject())
   788  					ids[id] = struct{}{}
   789  				}
   790  			}
   791  		}
   792  	}
   793  
   794  	iters, err := tx.findIterators(table, by, checkType)
   795  	if err != nil {
   796  		return err
   797  	}
   798  
   799  	fromResultIterators(iters...)
   800  
   801  	return nil
   802  }
   803  
   804  // Save serializes the data in the store.
   805  func (s *MemoryStore) Save(tx ReadTx) (*pb.StoreSnapshot, error) {
   806  	var snapshot pb.StoreSnapshot
   807  	for _, os := range objectStorers {
   808  		if err := os.Save(tx, &snapshot); err != nil {
   809  			return nil, err
   810  		}
   811  	}
   812  
   813  	return &snapshot, nil
   814  }
   815  
   816  // Restore sets the contents of the store to the serialized data in the
   817  // argument.
   818  func (s *MemoryStore) Restore(snapshot *pb.StoreSnapshot) error {
   819  	return s.updateLocal(func(tx Tx) error {
   820  		for _, os := range objectStorers {
   821  			if err := os.Restore(tx, snapshot); err != nil {
   822  				return err
   823  			}
   824  		}
   825  		return nil
   826  	})
   827  }
   828  
   829  // WatchQueue returns the publish/subscribe queue.
   830  func (s *MemoryStore) WatchQueue() *watch.Queue {
   831  	return s.queue
   832  }
   833  
   834  // ViewAndWatch calls a callback which can observe the state of this
   835  // MemoryStore. It also returns a channel that will return further events from
   836  // this point so the snapshot can be kept up to date. The watch channel must be
   837  // released with watch.StopWatch when it is no longer needed. The channel is
   838  // guaranteed to get all events after the moment of the snapshot, and only
   839  // those events.
   840  func ViewAndWatch(store *MemoryStore, cb func(ReadTx) error, specifiers ...api.Event) (watch chan events.Event, cancel func(), err error) {
   841  	// Using Update to lock the store and guarantee consistency between
   842  	// the watcher and the the state seen by the callback. snapshotReadTx
   843  	// exposes this Tx as a ReadTx so the callback can't modify it.
   844  	err = store.Update(func(tx Tx) error {
   845  		if err := cb(tx); err != nil {
   846  			return err
   847  		}
   848  		watch, cancel = state.Watch(store.WatchQueue(), specifiers...)
   849  		return nil
   850  	})
   851  	if watch != nil && err != nil {
   852  		cancel()
   853  		cancel = nil
   854  		watch = nil
   855  	}
   856  	return
   857  }
   858  
   859  // WatchFrom returns a channel that will return past events from starting
   860  // from "version", and new events until the channel is closed. If "version"
   861  // is nil, this function is equivalent to
   862  //
   863  //     state.Watch(store.WatchQueue(), specifiers...).
   864  //
   865  // If the log has been compacted and it's not possible to produce the exact
   866  // set of events leading from "version" to the current state, this function
   867  // will return an error, and the caller should re-sync.
   868  //
   869  // The watch channel must be released with watch.StopWatch when it is no
   870  // longer needed.
   871  func WatchFrom(store *MemoryStore, version *api.Version, specifiers ...api.Event) (chan events.Event, func(), error) {
   872  	if version == nil {
   873  		ch, cancel := state.Watch(store.WatchQueue(), specifiers...)
   874  		return ch, cancel, nil
   875  	}
   876  
   877  	if store.proposer == nil {
   878  		return nil, nil, errors.New("store does not support versioning")
   879  	}
   880  
   881  	var (
   882  		curVersion  *api.Version
   883  		watch       chan events.Event
   884  		cancelWatch func()
   885  	)
   886  	// Using Update to lock the store
   887  	err := store.Update(func(tx Tx) error {
   888  		// Get current version
   889  		curVersion = store.proposer.GetVersion()
   890  		// Start the watch with the store locked so events cannot be
   891  		// missed
   892  		watch, cancelWatch = state.Watch(store.WatchQueue(), specifiers...)
   893  		return nil
   894  	})
   895  	if watch != nil && err != nil {
   896  		cancelWatch()
   897  		return nil, nil, err
   898  	}
   899  
   900  	if curVersion == nil {
   901  		cancelWatch()
   902  		return nil, nil, errors.New("could not get current version from store")
   903  	}
   904  
   905  	changelist, err := store.changelistBetweenVersions(*version, *curVersion)
   906  	if err != nil {
   907  		cancelWatch()
   908  		return nil, nil, err
   909  	}
   910  
   911  	ch := make(chan events.Event)
   912  	stop := make(chan struct{})
   913  	cancel := func() {
   914  		close(stop)
   915  	}
   916  
   917  	go func() {
   918  		defer cancelWatch()
   919  
   920  		matcher := state.Matcher(specifiers...)
   921  		for _, change := range changelist {
   922  			if matcher(change) {
   923  				select {
   924  				case ch <- change:
   925  				case <-stop:
   926  					return
   927  				}
   928  			}
   929  		}
   930  
   931  		for {
   932  			select {
   933  			case <-stop:
   934  				return
   935  			case e := <-watch:
   936  				ch <- e
   937  			}
   938  		}
   939  	}()
   940  
   941  	return ch, cancel, nil
   942  }
   943  
   944  // touchMeta updates an object's timestamps when necessary and bumps the version
   945  // if provided.
   946  func touchMeta(meta *api.Meta, version *api.Version) error {
   947  	// Skip meta update if version is not defined as it means we're applying
   948  	// from raft or restoring from a snapshot.
   949  	if version == nil {
   950  		return nil
   951  	}
   952  
   953  	now, err := gogotypes.TimestampProto(time.Now())
   954  	if err != nil {
   955  		return err
   956  	}
   957  
   958  	meta.Version = *version
   959  
   960  	// Updated CreatedAt if not defined
   961  	if meta.CreatedAt == nil {
   962  		meta.CreatedAt = now
   963  	}
   964  
   965  	meta.UpdatedAt = now
   966  
   967  	return nil
   968  }
   969  
   970  // Wedged returns true if the store lock has been held for a long time,
   971  // possibly indicating a deadlock.
   972  func (s *MemoryStore) Wedged() bool {
   973  	lockedAt := s.updateLock.LockedAt()
   974  	if lockedAt.IsZero() {
   975  		return false
   976  	}
   977  
   978  	return time.Since(lockedAt) > WedgeTimeout
   979  }