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