@ -820,9 +820,22 @@ func (h *Head) truncateMemory(mint int64) (err error) {
h . metrics . headTruncateTotal . Inc ( )
start := time . Now ( )
h . gc ( )
actualMint := h . gc ( )
level . Info ( h . logger ) . Log ( "msg" , "Head GC completed" , "duration" , time . Since ( start ) )
h . metrics . gcDuration . Observe ( time . Since ( start ) . Seconds ( ) )
if actualMint > h . minTime . Load ( ) {
// The actual mint of the Head is higher than the one asked to truncate.
appendableMinValidTime := h . appendableMinValidTime ( )
if actualMint < appendableMinValidTime {
h . minTime . Store ( actualMint )
h . minValidTime . Store ( actualMint )
} else {
// The actual min time is in the appendable window.
// So we set the mint to the appendableMinValidTime.
h . minTime . Store ( appendableMinValidTime )
h . minValidTime . Store ( appendableMinValidTime )
}
}
// Truncate the chunk m-mapper.
if err := h . chunkDiskMapper . Truncate ( mint ) ; err != nil {
@ -1054,10 +1067,8 @@ func (h *Head) appender() *headAppender {
cleanupAppendIDsBelow := h . iso . lowWatermark ( )
return & headAppender {
head : h ,
// Set the minimum valid time to whichever is greater the head min valid time or the compaction window.
// This ensures that no samples will be added within the compaction window to avoid races.
minValidTime : max ( h . minValidTime . Load ( ) , h . MaxTime ( ) - h . chunkRange . Load ( ) / 2 ) ,
head : h ,
minValidTime : h . appendableMinValidTime ( ) ,
mint : math . MaxInt64 ,
maxt : math . MinInt64 ,
samples : h . getAppendBuffer ( ) ,
@ -1067,6 +1078,12 @@ func (h *Head) appender() *headAppender {
}
}
func ( h * Head ) appendableMinValidTime ( ) int64 {
// Setting the minimum valid time to whichever is greater, the head min valid time or the compaction window,
// ensures that no samples will be added within the compaction window to avoid races.
return max ( h . minValidTime . Load ( ) , h . MaxTime ( ) - h . chunkRange . Load ( ) / 2 )
}
func max ( a , b int64 ) int64 {
if a > b {
return a
@ -1335,13 +1352,14 @@ func (h *Head) Delete(mint, maxt int64, ms ...*labels.Matcher) error {
}
// gc removes data before the minimum timestamp from the head.
func ( h * Head ) gc ( ) {
// It returns the actual min times of the chunks present in the Head.
func ( h * Head ) gc ( ) int64 {
// Only data strictly lower than this timestamp must be deleted.
mint := h . MinTime ( )
// Drop old chunks and remember series IDs and hashes if they can be
// deleted entirely.
deleted , chunksRemoved := h . series . gc ( mint )
deleted , chunksRemoved , actualMint := h . series . gc ( mint )
seriesRemoved := len ( deleted )
h . metrics . seriesRemoved . Add ( float64 ( seriesRemoved ) )
@ -1382,6 +1400,8 @@ func (h *Head) gc() {
panic ( err )
}
h . symbols = symbols
return actualMint
}
// Tombstones returns a new reader over the head's tombstones
@ -1813,11 +1833,12 @@ func newStripeSeries(stripeSize int, seriesCallback SeriesLifecycleCallback) *st
// gc garbage collects old chunks that are strictly before mint and removes
// series entirely that have no chunks left.
func ( s * stripeSeries ) gc ( mint int64 ) ( map [ uint64 ] struct { } , int ) {
func ( s * stripeSeries ) gc ( mint int64 ) ( map [ uint64 ] struct { } , int , int64 ) {
var (
deleted = map [ uint64 ] struct { } { }
deletedForCallback = [ ] labels . Labels { }
rmChunks = 0
deleted = map [ uint64 ] struct { } { }
deletedForCallback = [ ] labels . Labels { }
rmChunks = 0
actualMint int64 = math . MaxInt64
)
// Run through all series and truncate old chunks. Mark those with no
// chunks left as deleted and store their ID.
@ -1830,6 +1851,10 @@ func (s *stripeSeries) gc(mint int64) (map[uint64]struct{}, int) {
rmChunks += series . truncateChunksBefore ( mint )
if len ( series . mmappedChunks ) > 0 || series . headChunk != nil || series . pendingCommit {
seriesMint := series . minTime ( )
if seriesMint < actualMint {
actualMint = seriesMint
}
series . Unlock ( )
continue
}
@ -1864,7 +1889,11 @@ func (s *stripeSeries) gc(mint int64) (map[uint64]struct{}, int) {
deletedForCallback = deletedForCallback [ : 0 ]
}
return deleted , rmChunks
if actualMint == math . MaxInt64 {
actualMint = mint
}
return deleted , rmChunks , actualMint
}
func ( s * stripeSeries ) getByID ( id uint64 ) * memSeries {