github.com/clly/consul@v1.4.5/agent/consul/state/state_store.go (about)

     1  package state
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  
     7  	"github.com/hashicorp/consul/types"
     8  	"github.com/hashicorp/go-memdb"
     9  )
    10  
    11  var (
    12  	// ErrMissingNode is the error returned when trying an operation
    13  	// which requires a node registration but none exists.
    14  	ErrMissingNode = errors.New("Missing node registration")
    15  
    16  	// ErrMissingService is the error we return if trying an
    17  	// operation which requires a service but none exists.
    18  	ErrMissingService = errors.New("Missing service registration")
    19  
    20  	// ErrMissingSessionID is returned when a session registration
    21  	// is attempted with an empty session ID.
    22  	ErrMissingSessionID = errors.New("Missing session ID")
    23  
    24  	// ErrMissingACLTokenSecret is returned when a token set is called on a
    25  	// token with an empty SecretID.
    26  	ErrMissingACLTokenSecret = errors.New("Missing ACL Token SecretID")
    27  
    28  	// ErrMissingACLTokenAccessor is returned when a token set is called on a
    29  	// token with an empty AccessorID.
    30  	ErrMissingACLTokenAccessor = errors.New("Missing ACL Token AccessorID")
    31  
    32  	// ErrMissingACLPolicyID is returned when a policy set is called on a
    33  	// policy with an empty ID.
    34  	ErrMissingACLPolicyID = errors.New("Missing ACL Policy ID")
    35  
    36  	// ErrMissingACLPolicyName is returned when a policy set is called on a
    37  	// policy with an empty Name.
    38  	ErrMissingACLPolicyName = errors.New("Missing ACL Policy Name")
    39  
    40  	// ErrMissingQueryID is returned when a Query set is called on
    41  	// a Query with an empty ID.
    42  	ErrMissingQueryID = errors.New("Missing Query ID")
    43  
    44  	// ErrMissingCARootID is returned when an CARoot set is called
    45  	// with an CARoot with an empty ID.
    46  	ErrMissingCARootID = errors.New("Missing CA Root ID")
    47  
    48  	// ErrMissingIntentionID is returned when an Intention set is called
    49  	// with an Intention with an empty ID.
    50  	ErrMissingIntentionID = errors.New("Missing Intention ID")
    51  )
    52  
    53  const (
    54  	// watchLimit is used as a soft limit to cap how many watches we allow
    55  	// for a given blocking query. If this is exceeded, then we will use a
    56  	// higher-level watch that's less fine-grained. This isn't as bad as it
    57  	// seems since we have made the main culprits (nodes and services) more
    58  	// efficient by diffing before we update via register requests.
    59  	//
    60  	// Given the current size of aFew == 32 in memdb's watch_few.go, this
    61  	// will allow for up to ~64 goroutines per blocking query.
    62  	watchLimit = 2048
    63  )
    64  
    65  // Store is where we store all of Consul's state, including
    66  // records of node registrations, services, checks, key/value
    67  // pairs and more. The DB is entirely in-memory and is constructed
    68  // from the Raft log through the FSM.
    69  type Store struct {
    70  	schema *memdb.DBSchema
    71  	db     *memdb.MemDB
    72  
    73  	// abandonCh is used to signal watchers that this state store has been
    74  	// abandoned (usually during a restore). This is only ever closed.
    75  	abandonCh chan struct{}
    76  
    77  	// kvsGraveyard manages tombstones for the key value store.
    78  	kvsGraveyard *Graveyard
    79  
    80  	// lockDelay holds expiration times for locks associated with keys.
    81  	lockDelay *Delay
    82  }
    83  
    84  // Snapshot is used to provide a point-in-time snapshot. It
    85  // works by starting a read transaction against the whole state store.
    86  type Snapshot struct {
    87  	store     *Store
    88  	tx        *memdb.Txn
    89  	lastIndex uint64
    90  }
    91  
    92  // Restore is used to efficiently manage restoring a large amount of
    93  // data to a state store.
    94  type Restore struct {
    95  	store *Store
    96  	tx    *memdb.Txn
    97  }
    98  
    99  // IndexEntry keeps a record of the last index per-table.
   100  type IndexEntry struct {
   101  	Key   string
   102  	Value uint64
   103  }
   104  
   105  // sessionCheck is used to create a many-to-one table such that
   106  // each check registered by a session can be mapped back to the
   107  // session table. This is only used internally in the state
   108  // store and thus it is not exported.
   109  type sessionCheck struct {
   110  	Node    string
   111  	CheckID types.CheckID
   112  	Session string
   113  }
   114  
   115  // NewStateStore creates a new in-memory state storage layer.
   116  func NewStateStore(gc *TombstoneGC) (*Store, error) {
   117  	// Create the in-memory DB.
   118  	schema := stateStoreSchema()
   119  	db, err := memdb.NewMemDB(schema)
   120  	if err != nil {
   121  		return nil, fmt.Errorf("Failed setting up state store: %s", err)
   122  	}
   123  
   124  	// Create and return the state store.
   125  	s := &Store{
   126  		schema:       schema,
   127  		db:           db,
   128  		abandonCh:    make(chan struct{}),
   129  		kvsGraveyard: NewGraveyard(gc),
   130  		lockDelay:    NewDelay(),
   131  	}
   132  	return s, nil
   133  }
   134  
   135  // Snapshot is used to create a point-in-time snapshot of the entire db.
   136  func (s *Store) Snapshot() *Snapshot {
   137  	tx := s.db.Txn(false)
   138  
   139  	var tables []string
   140  	for table := range s.schema.Tables {
   141  		tables = append(tables, table)
   142  	}
   143  	idx := maxIndexTxn(tx, tables...)
   144  
   145  	return &Snapshot{s, tx, idx}
   146  }
   147  
   148  // LastIndex returns that last index that affects the snapshotted data.
   149  func (s *Snapshot) LastIndex() uint64 {
   150  	return s.lastIndex
   151  }
   152  
   153  func (s *Snapshot) Indexes() (memdb.ResultIterator, error) {
   154  	iter, err := s.tx.Get("index", "id")
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	return iter, nil
   159  }
   160  
   161  // IndexRestore is used to restore an index
   162  func (s *Restore) IndexRestore(idx *IndexEntry) error {
   163  	if err := s.tx.Insert("index", idx); err != nil {
   164  		return fmt.Errorf("index insert failed: %v", err)
   165  	}
   166  	return nil
   167  }
   168  
   169  // Close performs cleanup of a state snapshot.
   170  func (s *Snapshot) Close() {
   171  	s.tx.Abort()
   172  }
   173  
   174  // Restore is used to efficiently manage restoring a large amount of data into
   175  // the state store. It works by doing all the restores inside of a single
   176  // transaction.
   177  func (s *Store) Restore() *Restore {
   178  	tx := s.db.Txn(true)
   179  	return &Restore{s, tx}
   180  }
   181  
   182  // Abort abandons the changes made by a restore. This or Commit should always be
   183  // called.
   184  func (s *Restore) Abort() {
   185  	s.tx.Abort()
   186  }
   187  
   188  // Commit commits the changes made by a restore. This or Abort should always be
   189  // called.
   190  func (s *Restore) Commit() {
   191  	s.tx.Commit()
   192  }
   193  
   194  // AbandonCh returns a channel you can wait on to know if the state store was
   195  // abandoned.
   196  func (s *Store) AbandonCh() <-chan struct{} {
   197  	return s.abandonCh
   198  }
   199  
   200  // Abandon is used to signal that the given state store has been abandoned.
   201  // Calling this more than one time will panic.
   202  func (s *Store) Abandon() {
   203  	close(s.abandonCh)
   204  }
   205  
   206  // maxIndex is a helper used to retrieve the highest known index
   207  // amongst a set of tables in the db.
   208  func (s *Store) maxIndex(tables ...string) uint64 {
   209  	tx := s.db.Txn(false)
   210  	defer tx.Abort()
   211  	return maxIndexTxn(tx, tables...)
   212  }
   213  
   214  // maxIndexTxn is a helper used to retrieve the highest known index
   215  // amongst a set of tables in the db.
   216  func maxIndexTxn(tx *memdb.Txn, tables ...string) uint64 {
   217  	var lindex uint64
   218  	for _, table := range tables {
   219  		ti, err := tx.First("index", "id", table)
   220  		if err != nil {
   221  			panic(fmt.Sprintf("unknown index: %s err: %s", table, err))
   222  		}
   223  		if idx, ok := ti.(*IndexEntry); ok && idx.Value > lindex {
   224  			lindex = idx.Value
   225  		}
   226  	}
   227  	return lindex
   228  }
   229  
   230  // indexUpdateMaxTxn is used when restoring entries and sets the table's index to
   231  // the given idx only if it's greater than the current index.
   232  func indexUpdateMaxTxn(tx *memdb.Txn, idx uint64, table string) error {
   233  	ti, err := tx.First("index", "id", table)
   234  	if err != nil {
   235  		return fmt.Errorf("failed to retrieve existing index: %s", err)
   236  	}
   237  
   238  	// Always take the first update, otherwise do the > check.
   239  	if ti == nil {
   240  		if err := tx.Insert("index", &IndexEntry{table, idx}); err != nil {
   241  			return fmt.Errorf("failed updating index %s", err)
   242  		}
   243  	} else if cur, ok := ti.(*IndexEntry); ok && idx > cur.Value {
   244  		if err := tx.Insert("index", &IndexEntry{table, idx}); err != nil {
   245  			return fmt.Errorf("failed updating index %s", err)
   246  		}
   247  	}
   248  
   249  	return nil
   250  }