github.imxd.top/hashicorp/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 }