github.com/clly/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  }