github.phpd.cn/hashicorp/consul@v1.4.5/agent/consul/state/graveyard.go (about)

     1  package state
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/hashicorp/go-memdb"
     7  )
     8  
     9  // Tombstone is the internal type used to track tombstones.
    10  type Tombstone struct {
    11  	Key   string
    12  	Index uint64
    13  }
    14  
    15  // Graveyard manages a set of tombstones.
    16  type Graveyard struct {
    17  	// GC is when we create tombstones to track their time-to-live.
    18  	// The GC is consumed upstream to manage clearing of tombstones.
    19  	gc *TombstoneGC
    20  }
    21  
    22  // NewGraveyard returns a new graveyard.
    23  func NewGraveyard(gc *TombstoneGC) *Graveyard {
    24  	return &Graveyard{gc: gc}
    25  }
    26  
    27  // InsertTxn adds a new tombstone.
    28  func (g *Graveyard) InsertTxn(tx *memdb.Txn, key string, idx uint64) error {
    29  	// Insert the tombstone.
    30  	stone := &Tombstone{Key: key, Index: idx}
    31  	if err := tx.Insert("tombstones", stone); err != nil {
    32  		return fmt.Errorf("failed inserting tombstone: %s", err)
    33  	}
    34  
    35  	if err := tx.Insert("index", &IndexEntry{"tombstones", idx}); err != nil {
    36  		return fmt.Errorf("failed updating index: %s", err)
    37  	}
    38  
    39  	// If GC is configured, then we hint that this index requires reaping.
    40  	if g.gc != nil {
    41  		tx.Defer(func() { g.gc.Hint(idx) })
    42  	}
    43  	return nil
    44  }
    45  
    46  // GetMaxIndexTxn returns the highest index tombstone whose key matches the
    47  // given context, using a prefix match.
    48  func (g *Graveyard) GetMaxIndexTxn(tx *memdb.Txn, prefix string) (uint64, error) {
    49  	stones, err := tx.Get("tombstones", "id_prefix", prefix)
    50  	if err != nil {
    51  		return 0, fmt.Errorf("failed querying tombstones: %s", err)
    52  	}
    53  
    54  	var lindex uint64
    55  	for stone := stones.Next(); stone != nil; stone = stones.Next() {
    56  		s := stone.(*Tombstone)
    57  		if s.Index > lindex {
    58  			lindex = s.Index
    59  		}
    60  	}
    61  	return lindex, nil
    62  }
    63  
    64  // DumpTxn returns all the tombstones.
    65  func (g *Graveyard) DumpTxn(tx *memdb.Txn) (memdb.ResultIterator, error) {
    66  	iter, err := tx.Get("tombstones", "id")
    67  	if err != nil {
    68  		return nil, err
    69  	}
    70  
    71  	return iter, nil
    72  }
    73  
    74  // RestoreTxn is used when restoring from a snapshot. For general inserts, use
    75  // InsertTxn.
    76  func (g *Graveyard) RestoreTxn(tx *memdb.Txn, stone *Tombstone) error {
    77  	if err := tx.Insert("tombstones", stone); err != nil {
    78  		return fmt.Errorf("failed inserting tombstone: %s", err)
    79  	}
    80  
    81  	if err := indexUpdateMaxTxn(tx, stone.Index, "tombstones"); err != nil {
    82  		return fmt.Errorf("failed updating index: %s", err)
    83  	}
    84  	return nil
    85  }
    86  
    87  // ReapTxn cleans out all tombstones whose index values are less than or equal
    88  // to the given idx. This prevents unbounded storage growth of the tombstones.
    89  func (g *Graveyard) ReapTxn(tx *memdb.Txn, idx uint64) error {
    90  	// This does a full table scan since we currently can't index on a
    91  	// numeric value. Since this is all in-memory and done infrequently
    92  	// this pretty reasonable.
    93  	stones, err := tx.Get("tombstones", "id")
    94  	if err != nil {
    95  		return fmt.Errorf("failed querying tombstones: %s", err)
    96  	}
    97  
    98  	// Find eligible tombstones.
    99  	var objs []interface{}
   100  	for stone := stones.Next(); stone != nil; stone = stones.Next() {
   101  		if stone.(*Tombstone).Index <= idx {
   102  			objs = append(objs, stone)
   103  		}
   104  	}
   105  
   106  	// Delete the tombstones in a separate loop so we don't trash the
   107  	// iterator.
   108  	for _, obj := range objs {
   109  		if err := tx.Delete("tombstones", obj); err != nil {
   110  			return fmt.Errorf("failed deleting tombstone: %s", err)
   111  		}
   112  	}
   113  	return nil
   114  }