Skip to content
This repository has been archived by the owner on Aug 13, 2019. It is now read-only.

Compaction implementation for block-ranges #103

Merged
merged 2 commits into from
Jul 12, 2017

Conversation

gouthamve
Copy link
Collaborator

@gouthamve gouthamve commented Jul 6, 2017

Super early implementation:

So if you have 20, 60, 240, 720, 2160 as your ranges, you pick the oldest 2160 block you can compact and inside that, you pick the oldest 720 block and so on.

TODO:

  • More testing.
  • Simplification and more comments.
  • Optimise some parts.
  • Choose the ranges by retention.
  • Align ranges by parent instead of 0 aligning all ranges.
  • Cut blocks properly.

/cc @fabxc


This change is Reviewable

@gouthamve gouthamve requested a review from fabxc July 6, 2017 19:36
@gouthamve
Copy link
Collaborator Author

Fixes #102

@gouthamve
Copy link
Collaborator Author

I just pushed a commit which some very strong defaults:

  1. The min-block-size is 2h or lower not more than that.
  2. We were thinking about doing 0 aligning of the largest block and every other block aligned to the parent. By making sure that every block-dur cleanly divides the parent, we can 0 align all the blocks and still be safe.
  3. We will choose the max block duration so that we don't store data 20% older than retention.

The repercussions for these are the min block duration is 2h or something that divides 2h cleanly which is kinda weird but seems okay.

/cc @fabxc @brian-brazil

Signed-off-by: Goutham Veeramachaneni <cs14btech11014@iith.ac.in>
Copy link
Contributor

@fabxc fabxc left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a first pass on the the configuration of it all. Will go over the selection implementation next.

@@ -0,0 +1,232 @@
package tsdb
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

License header missing.

compact.go Outdated
int64(24 * time.Hour),
int64(72 * time.Hour), // 3d
int64(216 * time.Hour), // 9d
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a TSDB perspective defaults probably don't make sense as we don't know the timestamp unit and the sample interval. Maybe a helper function here would we best to which we can give

  • min size
  • steps
  • blocks per step

For example in prometheus: ExponentialBlockRanges(2 * time.Hour / time.Millisecond, 5, 3)

compact.go Outdated
}

func newCompactor(dir string, r prometheus.Registerer, l log.Logger, opts *compactorOptions) *compactor {
if opts.blockRanges == nil {
opts.blockRanges = defaultBlockRanges
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per above defaults are not generally applicable, just not compacting seems like the right choice here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need sanity checks on startup that the ranges actually fit within each other?
What happens if the chosen ranges do not work with already existing blocks?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if the chosen ranges do not work with already existing blocks?

Some blocks which cross the boundaries of the max-block-range would never be compacted. Which is okay, I guess.

db.go Outdated
@@ -60,11 +59,9 @@ type Options struct {

// The timestamp range of head blocks after which they get persisted.
// It's the minimum duration of any persisted block.
// It should divide 2h cleanly and should be less than or equal to 2h. Default 2h.
MinBlockDuration uint64
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per above suggestion, we probably want to pass in the full ranges as config options instead of this value. The first range is than equivalent to MinBlockDuration. As said there, we also shouldn't make assumptions about stuff like 2h.

At least for bench/debugging purposes it probably would be a good idea to still allow passing in short initial ranges via Prometheus. Maybe a hidden --debug.storage.tsdb... flag?

db.go Outdated
if float64(copts.blockRanges[len(copts.blockRanges)-1])/float64(opts.RetentionDuration) > 0.2 {
copts.blockRanges = copts.blockRanges[:len(copts.blockRanges)-1]
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would just spin if the highest range is less than 20% of the retention, no?

@gouthamve gouthamve force-pushed the block-ranges branch 2 times, most recently from 74cb7e0 to 6a7e329 Compare July 11, 2017 09:48
db.go Outdated
return nil, errors.New("atleast one block-range should exist.")
}

for float64(copts.blockRanges[len(copts.blockRanges)-1])/float64(opts.RetentionDuration) > 0.2 {
Copy link
Collaborator Author

@gouthamve gouthamve Jul 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fabxc Given we allow passing of full ranges, should we still do this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea, I think so. Imagine we generally have a hard-coded set of ranges. The maximum one we actually compact to should still be ceiled by the dynamically configured retention.

compact.go Outdated
}

func (c *compactor) selectDirs(ds []dirMeta) []dirMeta {
// The way tp skip compaction is to not have blockRanges.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably still want this later on to ensure we re-compact stuff with lots of tombstones.
Probably part of #91 – sorry about that lingering around for so long without review.

compact.go Outdated
}

func (c *compactor) match(dirs []dirMeta) bool {
g := dirs[0].meta.Compaction.Generation
func splitByRange(ds []dirMeta, tr int64) [][]dirMeta {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function definitely needs heading and inlined docs.

compact.go Outdated
t0 = dir.meta.MinTime - dir.meta.MinTime%tr
if intervalContains(t0, t0+tr, dir.meta.MinTime) && intervalContains(t0, t0+tr, dir.meta.MaxTime) {
dirs = append(dirs, dir)
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Repeating initialization and the condition at different indentation levels makes is hard to grasp.

var splitDirs [][]dirMeta

for i := 0; i < len(ds); {
    var group []dirMeta
    // Compute start of aligned time range of size tr closest to the current block's start.
    t0 := ds[i].meta.MinTime - (ds[i].meta.MinTime % tr)

    // Add all dirs to the current group that are within [t0, t0+tr].
    for ;i < len(ds); i++ {
        if ds[i].meta.MinTime < t0 || ds[i].meta.MaxTime > t0+tr {
            break
        }
        group = append(group, ds[i])
    }

    if len(group) > 0 {
        splitDirs = append(splitDirs, group)
    }
}

return splitDirs

Haven't verified this one, but should roughly do the same.

dirs := []dirMeta{}
for i := len(blocks) - 1; i >= 0; i-- {
// We need to choose the oldest blocks to compact. If there are a couple of blocks in
// the largest interval, we should compact those first.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are reverse-iterating. Doesn't that make us chose the youngest group of compactable blocks?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope. So we actually sort the dirs in ascending order here: https://github.com/Gouthamve/tsdb/blob/block-ranges/compact.go#L144-L146 hence it makes sense to reverse-iterate.

db.go Outdated
}

if len(copts.blockRanges) == 0 {
return nil, errors.New("atleast one block-range should exist.")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at least

must exist

No . in errors.

compact.go Outdated
// If there are too many blocks, see if a smaller interval will catch them.
// i.e, if we have 0-20, 60-80, 80-100; all fall under 0-240, but we'd rather compact 60-100
// than all at once.
if len(dirs) > 2 {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before we were always compacting groups of 3. Why are we now searching in smaller ranges if we have 3?

@gouthamve gouthamve changed the title [WIP] compaction implementation for block-ranges Compaction implementation for block-ranges Jul 12, 2017
@gouthamve
Copy link
Collaborator Author

@fabxc Incorporated all the changes. PTAL.

@fabxc
Copy link
Contributor

fabxc commented Jul 12, 2017

Travis is failing.

@fabxc
Copy link
Contributor

fabxc commented Jul 12, 2017

👍 if manual testing also confirmed this. Probably good to spin up prombench and let it run for a bit after the changes have been pulled there.

We might want to introduce debug flags to manually set the range so we can run tests with frequent compactions.
Or we even keep min block duration around for high frequency cases... they probably still exist and will run out of memory otherwise.

Signed-off-by: Goutham Veeramachaneni <cs14btech11014@iith.ac.in>
@gouthamve gouthamve merged commit 7b9c536 into prometheus-junkyard:master Jul 12, 2017
@gouthamve gouthamve mentioned this pull request Oct 3, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants