|
|
@ -216,7 +216,7 @@ func newDBMetrics(db *DB, r prometheus.Registerer) *dbMetrics {
|
|
|
|
})
|
|
|
|
})
|
|
|
|
m.reloadsFailed = prometheus.NewCounter(prometheus.CounterOpts{
|
|
|
|
m.reloadsFailed = prometheus.NewCounter(prometheus.CounterOpts{
|
|
|
|
Name: "prometheus_tsdb_reloads_failures_total",
|
|
|
|
Name: "prometheus_tsdb_reloads_failures_total",
|
|
|
|
Help: "Number of times the database failed to reload block data from disk.",
|
|
|
|
Help: "Number of times the database failed to reloadBlocks block data from disk.",
|
|
|
|
})
|
|
|
|
})
|
|
|
|
m.compactionsTriggered = prometheus.NewCounter(prometheus.CounterOpts{
|
|
|
|
m.compactionsTriggered = prometheus.NewCounter(prometheus.CounterOpts{
|
|
|
|
Name: "prometheus_tsdb_compactions_triggered_total",
|
|
|
|
Name: "prometheus_tsdb_compactions_triggered_total",
|
|
|
@ -769,20 +769,26 @@ func (a dbAppender) Commit() error {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Compact data if possible. After successful compaction blocks are reloaded
|
|
|
|
// Compact data if possible. After successful compaction blocks are reloaded
|
|
|
|
// which will also trigger blocks to be deleted that fall out of the retention
|
|
|
|
// which will also delete the blocks that fall out of the retention window.
|
|
|
|
// window.
|
|
|
|
// Old blocks are only deleted on reloadBlocks based on the new block's parent information.
|
|
|
|
// If no blocks are compacted, the retention window state doesn't change. Thus,
|
|
|
|
// See DB.reloadBlocks documentation for further information.
|
|
|
|
// this is sufficient to reliably delete old data.
|
|
|
|
func (db *DB) Compact() (returnErr error) {
|
|
|
|
// Old blocks are only deleted on reload based on the new block's parent information.
|
|
|
|
|
|
|
|
// See DB.reload documentation for further information.
|
|
|
|
|
|
|
|
func (db *DB) Compact() (err error) {
|
|
|
|
|
|
|
|
db.cmtx.Lock()
|
|
|
|
db.cmtx.Lock()
|
|
|
|
defer db.cmtx.Unlock()
|
|
|
|
defer db.cmtx.Unlock()
|
|
|
|
defer func() {
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
if returnErr != nil {
|
|
|
|
db.metrics.compactionsFailed.Inc()
|
|
|
|
db.metrics.compactionsFailed.Inc()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lastBlockMaxt := int64(math.MinInt64)
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
|
|
|
|
var merr tsdb_errors.MultiError
|
|
|
|
|
|
|
|
merr.Add(returnErr)
|
|
|
|
|
|
|
|
merr.Add(errors.Wrap(db.head.truncateWAL(lastBlockMaxt), "WAL truncation in Compact defer"))
|
|
|
|
|
|
|
|
returnErr = merr.Err()
|
|
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
// Check whether we have pending head blocks that are ready to be persisted.
|
|
|
|
// Check whether we have pending head blocks that are ready to be persisted.
|
|
|
|
// They have the highest priority.
|
|
|
|
// They have the highest priority.
|
|
|
|
for {
|
|
|
|
for {
|
|
|
@ -804,55 +810,59 @@ func (db *DB) Compact() (err error) {
|
|
|
|
// so in order to make sure that overlaps are evaluated
|
|
|
|
// so in order to make sure that overlaps are evaluated
|
|
|
|
// consistently, we explicitly remove the last value
|
|
|
|
// consistently, we explicitly remove the last value
|
|
|
|
// from the block interval here.
|
|
|
|
// from the block interval here.
|
|
|
|
head := NewRangeHead(db.head, mint, maxt-1)
|
|
|
|
if err := db.compactHead(NewRangeHead(db.head, mint, maxt-1)); err != nil {
|
|
|
|
if err := db.compactHead(head); err != nil {
|
|
|
|
return errors.Wrap(err, "compact head")
|
|
|
|
return err
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Consider only successful compactions for WAL truncation.
|
|
|
|
|
|
|
|
lastBlockMaxt = maxt
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Clear some disk space before compacting blocks, especially important
|
|
|
|
|
|
|
|
// when Head compaction happened over a long time range.
|
|
|
|
|
|
|
|
if err := db.head.truncateWAL(lastBlockMaxt); err != nil {
|
|
|
|
|
|
|
|
return errors.Wrap(err, "WAL truncation in Compact")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return db.compactBlocks()
|
|
|
|
return db.compactBlocks()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// CompactHead compacts the given the RangeHead.
|
|
|
|
// CompactHead compacts the given RangeHead.
|
|
|
|
func (db *DB) CompactHead(head *RangeHead) (err error) {
|
|
|
|
func (db *DB) CompactHead(head *RangeHead) error {
|
|
|
|
db.cmtx.Lock()
|
|
|
|
db.cmtx.Lock()
|
|
|
|
defer db.cmtx.Unlock()
|
|
|
|
defer db.cmtx.Unlock()
|
|
|
|
|
|
|
|
|
|
|
|
return db.compactHead(head)
|
|
|
|
if err := db.compactHead(head); err != nil {
|
|
|
|
|
|
|
|
return errors.Wrap(err, "compact head")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if err := db.head.truncateWAL(head.BlockMaxTime()); err != nil {
|
|
|
|
|
|
|
|
return errors.Wrap(err, "WAL truncation")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// compactHead compacts the given the RangeHead.
|
|
|
|
// compactHead compacts the given RangeHead.
|
|
|
|
// The compaction mutex should be held before calling this method.
|
|
|
|
// The compaction mutex should be held before calling this method.
|
|
|
|
func (db *DB) compactHead(head *RangeHead) (err error) {
|
|
|
|
func (db *DB) compactHead(head *RangeHead) error {
|
|
|
|
// Add +1 millisecond to block maxt because block intervals are half-open: [b.MinTime, b.MaxTime).
|
|
|
|
uid, err := db.compactor.Write(db.dir, head, head.MinTime(), head.BlockMaxTime(), nil)
|
|
|
|
// Because of this block intervals are always +1 than the total samples it includes.
|
|
|
|
|
|
|
|
maxt := head.MaxTime() + 1
|
|
|
|
|
|
|
|
uid, err := db.compactor.Write(db.dir, head, head.MinTime(), maxt, nil)
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "persist head block")
|
|
|
|
return errors.Wrap(err, "persist head block")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
runtime.GC()
|
|
|
|
runtime.GC()
|
|
|
|
|
|
|
|
if err := db.reloadBlocks(); err != nil {
|
|
|
|
if err := db.reload(); err != nil {
|
|
|
|
|
|
|
|
if errRemoveAll := os.RemoveAll(filepath.Join(db.dir, uid.String())); errRemoveAll != nil {
|
|
|
|
if errRemoveAll := os.RemoveAll(filepath.Join(db.dir, uid.String())); errRemoveAll != nil {
|
|
|
|
var merr tsdb_errors.MultiError
|
|
|
|
var merr tsdb_errors.MultiError
|
|
|
|
merr.Add(errors.Wrap(err, "reload blocks"))
|
|
|
|
merr.Add(errors.Wrap(err, "reloadBlocks blocks"))
|
|
|
|
merr.Add(errors.Wrapf(errRemoveAll, "delete persisted head block after failed db reload:%s", uid))
|
|
|
|
merr.Add(errors.Wrapf(errRemoveAll, "delete persisted head block after failed db reloadBlocks:%s", uid))
|
|
|
|
return merr.Err()
|
|
|
|
return merr.Err()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return errors.Wrap(err, "reload blocks")
|
|
|
|
return errors.Wrap(err, "reloadBlocks blocks")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (uid == ulid.ULID{}) {
|
|
|
|
if err = db.head.truncateMemory(head.BlockMaxTime()); err != nil {
|
|
|
|
// Compaction resulted in an empty block.
|
|
|
|
return errors.Wrap(err, "head memory truncate")
|
|
|
|
// Head truncating during db.reload() depends on the persisted blocks and
|
|
|
|
|
|
|
|
// in this case no new block will be persisted so manually truncate the head.
|
|
|
|
|
|
|
|
if err = db.head.Truncate(maxt); err != nil {
|
|
|
|
|
|
|
|
return errors.Wrap(err, "head truncate failed (in compact)")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
runtime.GC()
|
|
|
|
runtime.GC()
|
|
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -881,11 +891,11 @@ func (db *DB) compactBlocks() (err error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
runtime.GC()
|
|
|
|
runtime.GC()
|
|
|
|
|
|
|
|
|
|
|
|
if err := db.reload(); err != nil {
|
|
|
|
if err := db.reloadBlocks(); err != nil {
|
|
|
|
if err := os.RemoveAll(filepath.Join(db.dir, uid.String())); err != nil {
|
|
|
|
if err := os.RemoveAll(filepath.Join(db.dir, uid.String())); err != nil {
|
|
|
|
return errors.Wrapf(err, "delete compacted block after failed db reload:%s", uid)
|
|
|
|
return errors.Wrapf(err, "delete compacted block after failed db reloadBlocks:%s", uid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return errors.Wrap(err, "reload blocks")
|
|
|
|
return errors.Wrap(err, "reloadBlocks blocks")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
runtime.GC()
|
|
|
|
runtime.GC()
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -904,9 +914,23 @@ func getBlock(allBlocks []*Block, id ulid.ULID) (*Block, bool) {
|
|
|
|
return nil, false
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// reload blocks and trigger head truncation if new blocks appeared.
|
|
|
|
// reload reloads blocks and truncates the head and its WAL.
|
|
|
|
|
|
|
|
func (db *DB) reload() error {
|
|
|
|
|
|
|
|
if err := db.reloadBlocks(); err != nil {
|
|
|
|
|
|
|
|
return errors.Wrap(err, "reloadBlocks")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(db.blocks) == 0 {
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := db.head.Truncate(db.blocks[len(db.blocks)-1].MaxTime()); err != nil {
|
|
|
|
|
|
|
|
return errors.Wrap(err, "head truncate")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// reloadBlocks reloads blocks without touching head.
|
|
|
|
// Blocks that are obsolete due to replacement or retention will be deleted.
|
|
|
|
// Blocks that are obsolete due to replacement or retention will be deleted.
|
|
|
|
func (db *DB) reload() (err error) {
|
|
|
|
func (db *DB) reloadBlocks() (err error) {
|
|
|
|
defer func() {
|
|
|
|
defer func() {
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
db.metrics.reloadsFailed.Inc()
|
|
|
|
db.metrics.reloadsFailed.Inc()
|
|
|
@ -989,7 +1013,7 @@ func (db *DB) reload() (err error) {
|
|
|
|
blockMetas = append(blockMetas, b.Meta())
|
|
|
|
blockMetas = append(blockMetas, b.Meta())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if overlaps := OverlappingBlocks(blockMetas); len(overlaps) > 0 {
|
|
|
|
if overlaps := OverlappingBlocks(blockMetas); len(overlaps) > 0 {
|
|
|
|
level.Warn(db.logger).Log("msg", "Overlapping blocks found during reload", "detail", overlaps.String())
|
|
|
|
level.Warn(db.logger).Log("msg", "Overlapping blocks found during reloadBlocks", "detail", overlaps.String())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Append blocks to old, deletable blocks, so we can close them.
|
|
|
|
// Append blocks to old, deletable blocks, so we can close them.
|
|
|
@ -999,15 +1023,9 @@ func (db *DB) reload() (err error) {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if err := db.deleteBlocks(deletable); err != nil {
|
|
|
|
if err := db.deleteBlocks(deletable); err != nil {
|
|
|
|
return err
|
|
|
|
return errors.Wrapf(err, "delete %v blocks", len(deletable))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
// Garbage collect data in the head if the most recent persisted block
|
|
|
|
|
|
|
|
// covers data of its current time range.
|
|
|
|
|
|
|
|
if len(toLoad) == 0 {
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return errors.Wrap(db.head.Truncate(toLoad[len(toLoad)-1].Meta().MaxTime), "head truncate failed")
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func openBlocks(l log.Logger, dir string, loaded []*Block, chunkPool chunkenc.Pool) (blocks []*Block, corrupted map[ulid.ULID]error, err error) {
|
|
|
|
func openBlocks(l log.Logger, dir string, loaded []*Block, chunkPool chunkenc.Pool) (blocks []*Block, corrupted map[ulid.ULID]error, err error) {
|
|
|
@ -1020,7 +1038,7 @@ func openBlocks(l log.Logger, dir string, loaded []*Block, chunkPool chunkenc.Po
|
|
|
|
for _, bDir := range bDirs {
|
|
|
|
for _, bDir := range bDirs {
|
|
|
|
meta, _, err := readMetaFile(bDir)
|
|
|
|
meta, _, err := readMetaFile(bDir)
|
|
|
|
if err != nil {
|
|
|
|
if err != nil {
|
|
|
|
level.Error(l).Log("msg", "Failed to read meta.json for a block during reload. Skipping", "dir", bDir, "err", err)
|
|
|
|
level.Error(l).Log("msg", "Failed to read meta.json for a block during reloadBlocks. Skipping", "dir", bDir, "err", err)
|
|
|
|
continue
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -1502,7 +1520,11 @@ func (db *DB) CleanTombstones() (err error) {
|
|
|
|
newUIDs = append(newUIDs, *uid)
|
|
|
|
newUIDs = append(newUIDs, *uid)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return errors.Wrap(db.reload(), "reload blocks")
|
|
|
|
|
|
|
|
|
|
|
|
if err := db.reloadBlocks(); err != nil {
|
|
|
|
|
|
|
|
return errors.Wrap(err, "reload blocks")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func isBlockDir(fi os.FileInfo) bool {
|
|
|
|
func isBlockDir(fi os.FileInfo) bool {
|
|
|
|