-
Notifications
You must be signed in to change notification settings - Fork 3.7k
feat: Adds detection for orphaned TSM files while planning compaction #26766
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
cabcdd1
ff08f6e
b9a9667
b3f1b2d
2c178fd
4425ded
4ee4278
86ad19a
84db100
3020f03
0cbf752
003f303
8eb2aec
886f593
e715375
b81d67f
e7d6038
3056c24
0030fb0
397236e
97db9f3
42f6ec8
d19d19d
0c556e2
065d359
a697635
2a73c97
b708182
6896118
4d03abe
8d241d9
c14a416
a149a65
f3498c0
5ace584
40affaa
061ade9
cd61e6d
36e8106
2e11b6f
bbcfa47
f1042fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -142,6 +142,13 @@ type CompactionPlanner interface { | |||||||||||||||||||||||||
| SetAggressiveCompactionPointsPerBlock(aggressiveCompactionPointsPerBlock int) | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| GetAggressiveCompactionPointsPerBlock() int | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // SetNestedCompactor controls whether we enable the nested | ||||||||||||||||||||||||||
| // compaction level checking feature flag. This rectifies an issue where TSM files | ||||||||||||||||||||||||||
| // Have 1-3 rogue lower level file(s) nested within larger level files. This state | ||||||||||||||||||||||||||
| // is not frequent but has been seen. Enable this flag to capture these files during full compaction. | ||||||||||||||||||||||||||
| SetNestedCompactor(enabled bool) | ||||||||||||||||||||||||||
| GetNestedCompactorEnabled() bool | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // DefaultPlanner implements CompactionPlanner using a strategy to roll up | ||||||||||||||||||||||||||
|
|
@@ -179,6 +186,12 @@ type DefaultPlanner struct { | |||||||||||||||||||||||||
| // aggressiveCompactionPointsPerBlock is the amount of points that should be | ||||||||||||||||||||||||||
| // packed in to a TSM file block during aggressive compaction | ||||||||||||||||||||||||||
| aggressiveCompactionPointsPerBlock int | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // enableNestedCompactor controls whether we enable the nested | ||||||||||||||||||||||||||
| // compaction level checking feature flag. This rectifies an issue where TSM files | ||||||||||||||||||||||||||
| // Have 1-3 rogue lower level file(s) nested within larger level files. This state | ||||||||||||||||||||||||||
| // is not frequent but has been seen. Enable this flag to capture these files during full compaction. | ||||||||||||||||||||||||||
| enableNestedCompactor bool | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| type fileStore interface { | ||||||||||||||||||||||||||
|
|
@@ -256,6 +269,14 @@ func (c *DefaultPlanner) SetAggressiveCompactionPointsPerBlock(aggressiveCompact | |||||||||||||||||||||||||
| c.aggressiveCompactionPointsPerBlock = aggressiveCompactionPointsPerBlock | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| func (c *DefaultPlanner) SetNestedCompactor(enableNestedCompactor bool) { | ||||||||||||||||||||||||||
| c.enableNestedCompactor = enableNestedCompactor | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| func (c *DefaultPlanner) GetNestedCompactorEnabled() bool { | ||||||||||||||||||||||||||
| return c.enableNestedCompactor | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| func (c *DefaultPlanner) GetAggressiveCompactionPointsPerBlock() int { | ||||||||||||||||||||||||||
| return c.aggressiveCompactionPointsPerBlock | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
@@ -375,9 +396,29 @@ func (c *DefaultPlanner) PlanLevel(level int) ([]CompactionGroup, int64) { | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Remove any groups in the wrong level | ||||||||||||||||||||||||||
| var levelGroups []tsmGenerations | ||||||||||||||||||||||||||
| for _, cur := range groups { | ||||||||||||||||||||||||||
| if cur.level() == level { | ||||||||||||||||||||||||||
| levelGroups = append(levelGroups, cur) | ||||||||||||||||||||||||||
| if c.enableNestedCompactor { | ||||||||||||||||||||||||||
| // When nested compactor is enabled and planning lower levels (1-3), | ||||||||||||||||||||||||||
| // check if there are higher level files (4+) both BEFORE and AFTER this group | ||||||||||||||||||||||||||
| // Only skip if the lower-level files are truly nested between higher-level files | ||||||||||||||||||||||||||
| for i, cur := range groups { | ||||||||||||||||||||||||||
| if len(groups) > i+1 { | ||||||||||||||||||||||||||
| nextLevel := groups[i+1] | ||||||||||||||||||||||||||
| if cur.level() < nextLevel.level() { | ||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||
| } else if cur.level() == level { | ||||||||||||||||||||||||||
| levelGroups = append(levelGroups, cur) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| if cur.level() == level { | ||||||||||||||||||||||||||
| levelGroups = append(levelGroups, cur) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| for _, cur := range groups { | ||||||||||||||||||||||||||
| if cur.level() == level { | ||||||||||||||||||||||||||
| levelGroups = append(levelGroups, cur) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
@@ -580,11 +621,26 @@ func (c *DefaultPlanner) Plan(lastWrite time.Time) ([]CompactionGroup, int64) { | |||||||||||||||||||||||||
| // each generation in descending break once we see a file less than 4. | ||||||||||||||||||||||||||
| end := 0 | ||||||||||||||||||||||||||
| start := 0 | ||||||||||||||||||||||||||
| lastHighLevelIndex := -1 | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| for i, g := range generations { | ||||||||||||||||||||||||||
| if g.level() <= 3 { | ||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||
| if c.enableNestedCompactor { | ||||||||||||||||||||||||||
| // Track the last high-level generation | ||||||||||||||||||||||||||
| if g.level() > 3 { | ||||||||||||||||||||||||||
| lastHighLevelIndex = i | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| if g.level() <= 3 { | ||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| end = i + 1 | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| end = i + 1 | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // If we have high-level files, only include generations up to the last high-level file | ||||||||||||||||||||||||||
| if c.enableNestedCompactor && lastHighLevelIndex >= 0 { | ||||||||||||||||||||||||||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This may not be needed since we set lastHighLevelIndex to -1 so if the feature flag is disabled it will just always be -1.. I want to remain consistent and indicate that this code is only supposed to be enabled when the feature flag is on so I decided to keep it. |
||||||||||||||||||||||||||
| end = lastHighLevelIndex + 1 | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // As compactions run, the oldest files get bigger. We don't want to re-compact them during | ||||||||||||||||||||||||||
|
|
@@ -610,9 +666,16 @@ func (c *DefaultPlanner) Plan(lastWrite time.Time) ([]CompactionGroup, int64) { | |||||||||||||||||||||||||
| // can become larger faster than ones after them. We want to skip those really big ones and just | ||||||||||||||||||||||||||
| // compact the smaller ones until they are closer in size. | ||||||||||||||||||||||||||
| if i > 0 { | ||||||||||||||||||||||||||
| if g.size()*2 < generations[i-1].size() { | ||||||||||||||||||||||||||
| start = i | ||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||
| if c.enableNestedCompactor { | ||||||||||||||||||||||||||
| if g.size()*2 < generations[i-1].size() && generations[i-1].level() >= generations[i].level() { | ||||||||||||||||||||||||||
|
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Going to add a comment about why this check needs to be different with the feature flag on. It's a bit confusing to just look at without knowing context.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Comment would be great. I still think that this logic will "leave behind" a non fully compacted file, but i think this is likely ok. For example
start will be 4, end will be 6. The 3-2 file won't be picked up. |
||||||||||||||||||||||||||
| start = i | ||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| if g.size()*2 < generations[i-1].size() { | ||||||||||||||||||||||||||
| start = i | ||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
@@ -636,13 +699,15 @@ func (c *DefaultPlanner) Plan(lastWrite time.Time) ([]CompactionGroup, int64) { | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| for j := i; j < i+step && j < len(generations); j++ { | ||||||||||||||||||||||||||
| gen := generations[j] | ||||||||||||||||||||||||||
| lvl := gen.level() | ||||||||||||||||||||||||||
| if !c.enableNestedCompactor { | ||||||||||||||||||||||||||
| lvl := gen.level() | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Skip compacting this group if there happens to be any lower level files in the | ||||||||||||||||||||||||||
| // middle. These will get picked up by the level compactors. | ||||||||||||||||||||||||||
| if lvl <= 3 { | ||||||||||||||||||||||||||
| skipGroup = true | ||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||
| // Skip compacting this group if there happens to be lower level | ||||||||||||||||||||||||||
| // files in the middle. | ||||||||||||||||||||||||||
| if lvl <= 3 { | ||||||||||||||||||||||||||
| skipGroup = true | ||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // Skip the file if it's over the max size and it contains a full block | ||||||||||||||||||||||||||
|
|
@@ -652,7 +717,7 @@ func (c *DefaultPlanner) Plan(lastWrite time.Time) ([]CompactionGroup, int64) { | |||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if skipGroup { | ||||||||||||||||||||||||||
| if !c.enableNestedCompactor && skipGroup { | ||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
@@ -671,12 +736,27 @@ func (c *DefaultPlanner) Plan(lastWrite time.Time) ([]CompactionGroup, int64) { | |||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // With the groups, we need to evaluate whether the group as a whole can be compacted | ||||||||||||||||||||||||||
| compactable := []tsmGenerations{} | ||||||||||||||||||||||||||
| for _, group := range groups { | ||||||||||||||||||||||||||
| // if we don't have enough generations to compact, skip it | ||||||||||||||||||||||||||
| if len(group) < 4 && !group.hasTombstones() { | ||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if c.enableNestedCompactor { | ||||||||||||||||||||||||||
| lastGroupLevel := 4 | ||||||||||||||||||||||||||
| for _, group := range groups { | ||||||||||||||||||||||||||
| // If we have the nested compactor flag enabled we need to try our best to not ever | ||||||||||||||||||||||||||
| // skip over lower lever files. This will ensure that if there are a few higher level files | ||||||||||||||||||||||||||
| // BUT over 4 lower level files nested we will compact them all together. | ||||||||||||||||||||||||||
| if len(group) < 4 && !group.hasTombstones() && lastGroupLevel == group.level() { | ||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| compactable = append(compactable, group) | ||||||||||||||||||||||||||
| lastGroupLevel = group.level() | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||
| for _, group := range groups { | ||||||||||||||||||||||||||
| // if we don't have enough generations to compact, skip it | ||||||||||||||||||||||||||
| if len(group) < 4 && !group.hasTombstones() { | ||||||||||||||||||||||||||
| continue | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| compactable = append(compactable, group) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| compactable = append(compactable, group) | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| // All the files to be compacted must be compacted in order. We need to convert each | ||||||||||||||||||||||||||
|
|
@@ -1946,6 +2026,17 @@ func (a tsmGenerations) hasTombstones() bool { | |||||||||||||||||||||||||
| return false | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| func (a tsmGenerations) lowestLevel() int { | ||||||||||||||||||||||||||
| var level int | ||||||||||||||||||||||||||
| for _, g := range a { | ||||||||||||||||||||||||||
| lev := g.level() | ||||||||||||||||||||||||||
| if lev < level { | ||||||||||||||||||||||||||
| level = lev | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| return level | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| func (a tsmGenerations) level() int { | ||||||||||||||||||||||||||
| var level int | ||||||||||||||||||||||||||
| for _, g := range a { | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.
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.
I don't think this is handling the groups as we want.
concern these generations:
old 4, 4, 4, 2, 2, 2, 2, 1, 1, 1, 1, 4, 4, 4, 3, 3, 3, 2, 2, 1, 1, 1, 1, 1, 1 youngThey will group into:
old G4, G2, G1, G4', G3, G2', G1 youngThis conditional will "skip/continue"
G1but notG2even though bothG1andG2are nested within L4 generations.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.
I think you need to scan ahead. I don't think you can "only" look at the level of an immediately subsequent group.
Perhaps this code: