@@ -75,6 +75,9 @@ type Head struct {
7575 symbols map [string ]struct {}
7676 values map [string ]stringset // label names to possible values
7777
78+ deletedMtx sync.Mutex
79+ deleted map [uint64 ]int // Deleted series, and what WAL segment they must be kept until.
80+
7881 postings * index.MemPostings // postings lists for terms
7982}
8083
@@ -234,6 +237,7 @@ func NewHead(r prometheus.Registerer, l log.Logger, wal *wal.WAL, chunkRange int
234237 values : map [string ]stringset {},
235238 symbols : map [string ]struct {}{},
236239 postings : index .NewUnorderedMemPostings (),
240+ deleted : map [uint64 ]int {},
237241 }
238242 h .metrics = newHeadMetrics (h , r )
239243
@@ -557,7 +561,13 @@ func (h *Head) Truncate(mint int64) (err error) {
557561 }
558562
559563 keep := func (id uint64 ) bool {
560- return h .series .getByID (id ) != nil
564+ if h .series .getByID (id ) != nil {
565+ return true
566+ }
567+ h .deletedMtx .Lock ()
568+ _ , ok := h .deleted [id ]
569+ h .deletedMtx .Unlock ()
570+ return ok
561571 }
562572 h .metrics .checkpointCreationTotal .Inc ()
563573 if _ , err = Checkpoint (h .wal , first , last , keep , mint ); err != nil {
@@ -570,6 +580,17 @@ func (h *Head) Truncate(mint int64) (err error) {
570580 // that supersedes them.
571581 level .Error (h .logger ).Log ("msg" , "truncating segments failed" , "err" , err )
572582 }
583+
584+ // The checkpoint is written and segments before it is truncated, so we no
585+ // longer need to track deleted series that are before it.
586+ h .deletedMtx .Lock ()
587+ for ref , segment := range h .deleted {
588+ if segment < first {
589+ delete (h .deleted , ref )
590+ }
591+ }
592+ h .deletedMtx .Unlock ()
593+
573594 h .metrics .checkpointDeleteTotal .Inc ()
574595 if err := DeleteCheckpoints (h .wal .Dir (), last ); err != nil {
575596 // Leftover old checkpoints do not cause problems down the line beyond
@@ -953,6 +974,21 @@ func (h *Head) gc() {
953974 // Remove deleted series IDs from the postings lists.
954975 h .postings .Delete (deleted )
955976
977+ if h .wal != nil {
978+ _ , last , _ := h .wal .Segments ()
979+ h .deletedMtx .Lock ()
980+ // Keep series records until we're past segment 'last'
981+ // because the WAL will still have samples records with
982+ // this ref ID. If we didn't keep these series records then
983+ // on start up when we replay the WAL, or any other code
984+ // that reads the WAL, wouldn't be able to use those
985+ // samples since we would have no labels for that ref ID.
986+ for ref := range deleted {
987+ h .deleted [ref ] = last
988+ }
989+ h .deletedMtx .Unlock ()
990+ }
991+
956992 // Rebuild symbols and label value indices from what is left in the postings terms.
957993 symbols := make (map [string ]struct {}, len (h .symbols ))
958994 values := make (map [string ]stringset , len (h .values ))
0 commit comments