github.imxd.top/hashicorp/consul@v1.4.5/agent/consul/state/coordinate.go (about) 1 package state 2 3 import ( 4 "fmt" 5 6 "github.com/hashicorp/consul/agent/structs" 7 "github.com/hashicorp/consul/lib" 8 "github.com/hashicorp/go-memdb" 9 ) 10 11 // coordinatesTableSchema returns a new table schema used for storing 12 // network coordinates. 13 func coordinatesTableSchema() *memdb.TableSchema { 14 return &memdb.TableSchema{ 15 Name: "coordinates", 16 Indexes: map[string]*memdb.IndexSchema{ 17 "id": &memdb.IndexSchema{ 18 Name: "id", 19 AllowMissing: false, 20 Unique: true, 21 Indexer: &memdb.CompoundIndex{ 22 // AllowMissing is required since we allow 23 // Segment to be an empty string. 24 AllowMissing: true, 25 Indexes: []memdb.Indexer{ 26 &memdb.StringFieldIndex{ 27 Field: "Node", 28 Lowercase: true, 29 }, 30 &memdb.StringFieldIndex{ 31 Field: "Segment", 32 Lowercase: true, 33 }, 34 }, 35 }, 36 }, 37 "node": &memdb.IndexSchema{ 38 Name: "node", 39 AllowMissing: false, 40 Unique: false, 41 Indexer: &memdb.StringFieldIndex{ 42 Field: "Node", 43 Lowercase: true, 44 }, 45 }, 46 }, 47 } 48 } 49 50 func init() { 51 registerSchema(coordinatesTableSchema) 52 } 53 54 // Coordinates is used to pull all the coordinates from the snapshot. 55 func (s *Snapshot) Coordinates() (memdb.ResultIterator, error) { 56 iter, err := s.tx.Get("coordinates", "id") 57 if err != nil { 58 return nil, err 59 } 60 return iter, nil 61 } 62 63 // Coordinates is used when restoring from a snapshot. For general inserts, use 64 // CoordinateBatchUpdate. We do less vetting of the updates here because they 65 // already got checked on the way in during a batch update. 66 func (s *Restore) Coordinates(idx uint64, updates structs.Coordinates) error { 67 for _, update := range updates { 68 // Skip any bad data that may have gotten into the database from 69 // a bad client in the past. 70 if !update.Coord.IsValid() { 71 continue 72 } 73 74 if err := s.tx.Insert("coordinates", update); err != nil { 75 return fmt.Errorf("failed restoring coordinate: %s", err) 76 } 77 } 78 79 if err := indexUpdateMaxTxn(s.tx, idx, "coordinates"); err != nil { 80 return fmt.Errorf("failed updating index: %s", err) 81 } 82 83 return nil 84 } 85 86 // Coordinate returns a map of coordinates for the given node, indexed by 87 // network segment. 88 func (s *Store) Coordinate(node string, ws memdb.WatchSet) (uint64, lib.CoordinateSet, error) { 89 tx := s.db.Txn(false) 90 defer tx.Abort() 91 92 tableIdx := maxIndexTxn(tx, "coordinates") 93 94 iter, err := tx.Get("coordinates", "node", node) 95 if err != nil { 96 return 0, nil, fmt.Errorf("failed coordinate lookup: %s", err) 97 } 98 ws.Add(iter.WatchCh()) 99 100 results := make(lib.CoordinateSet) 101 for raw := iter.Next(); raw != nil; raw = iter.Next() { 102 coord := raw.(*structs.Coordinate) 103 results[coord.Segment] = coord.Coord 104 } 105 return tableIdx, results, nil 106 } 107 108 // Coordinates queries for all nodes with coordinates. 109 func (s *Store) Coordinates(ws memdb.WatchSet) (uint64, structs.Coordinates, error) { 110 tx := s.db.Txn(false) 111 defer tx.Abort() 112 113 // Get the table index. 114 idx := maxIndexTxn(tx, "coordinates") 115 116 // Pull all the coordinates. 117 iter, err := tx.Get("coordinates", "id") 118 if err != nil { 119 return 0, nil, fmt.Errorf("failed coordinate lookup: %s", err) 120 } 121 ws.Add(iter.WatchCh()) 122 123 var results structs.Coordinates 124 for coord := iter.Next(); coord != nil; coord = iter.Next() { 125 results = append(results, coord.(*structs.Coordinate)) 126 } 127 return idx, results, nil 128 } 129 130 // CoordinateBatchUpdate processes a batch of coordinate updates and applies 131 // them in a single transaction. 132 func (s *Store) CoordinateBatchUpdate(idx uint64, updates structs.Coordinates) error { 133 tx := s.db.Txn(true) 134 defer tx.Abort() 135 136 // Upsert the coordinates. 137 for _, update := range updates { 138 // Skip any bad data that may have gotten into the database from 139 // a bad client in the past. 140 if !update.Coord.IsValid() { 141 continue 142 } 143 144 // Since the cleanup of coordinates is tied to deletion of 145 // nodes, we silently drop any updates for nodes that we don't 146 // know about. This might be possible during normal operation 147 // if we happen to get a coordinate update for a node that 148 // hasn't been able to add itself to the catalog yet. Since we 149 // don't carefully sequence this, and since it will fix itself 150 // on the next coordinate update from that node, we don't return 151 // an error or log anything. 152 node, err := tx.First("nodes", "id", update.Node) 153 if err != nil { 154 return fmt.Errorf("failed node lookup: %s", err) 155 } 156 if node == nil { 157 continue 158 } 159 160 if err := tx.Insert("coordinates", update); err != nil { 161 return fmt.Errorf("failed inserting coordinate: %s", err) 162 } 163 } 164 165 // Update the index. 166 if err := tx.Insert("index", &IndexEntry{"coordinates", idx}); err != nil { 167 return fmt.Errorf("failed updating index: %s", err) 168 } 169 170 tx.Commit() 171 return nil 172 }