-
Notifications
You must be signed in to change notification settings - Fork 180
Compaction implementation for block-ranges #103
Conversation
Fixes #102 |
I just pushed a commit which some very strong defaults:
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>
There was a problem hiding this 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 |
There was a problem hiding this comment.
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 | ||
} |
There was a problem hiding this comment.
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 | ||
} |
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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] | ||
} | ||
} |
There was a problem hiding this comment.
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?
74cb7e0
to
6a7e329
Compare
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 { |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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) | ||
} | ||
} |
There was a problem hiding this comment.
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. |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
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.") |
There was a problem hiding this comment.
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 { |
There was a problem hiding this comment.
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?
@fabxc Incorporated all the changes. PTAL. |
Travis is failing. |
👍 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. |
Signed-off-by: Goutham Veeramachaneni <cs14btech11014@iith.ac.in>
Super early implementation:
So if you have
20, 60, 240, 720, 2160
as your ranges, you pick the oldest2160
block you can compact and inside that, you pick the oldest720
block and so on.TODO:
/cc @fabxc
This change is