@@ -639,39 +639,13 @@ func compensatedSize(f *fileMetadata) uint64 {
639639 return f .Size + fileCompensation (f )
640640}
641641
642- // compensatedSizeAnnotator implements manifest.Annotator, annotating B-Tree
643- // nodes with the sum of the files' compensated sizes. Its annotation type is
644- // a *uint64. Compensated sizes may change once a table's stats are loaded
645- // asynchronously, so its values are marked as cacheable only if a file's
646- // stats have been loaded.
647- type compensatedSizeAnnotator struct {
648- }
649-
650- var _ manifest.Annotator = compensatedSizeAnnotator {}
651-
652- func (a compensatedSizeAnnotator ) Zero (dst interface {}) interface {} {
653- if dst == nil {
654- return new (uint64 )
655- }
656- v := dst .(* uint64 )
657- * v = 0
658- return v
659- }
660-
661- func (a compensatedSizeAnnotator ) Accumulate (
662- f * fileMetadata , dst interface {},
663- ) (v interface {}, cacheOK bool ) {
664- vptr := dst .(* uint64 )
665- * vptr = * vptr + compensatedSize (f )
666- return vptr , f .StatsValid ()
667- }
668-
669- func (a compensatedSizeAnnotator ) Merge (src interface {}, dst interface {}) interface {} {
670- srcV := src .(* uint64 )
671- dstV := dst .(* uint64 )
672- * dstV = * dstV + * srcV
673- return dstV
674- }
642+ // compensatedSizeAnnotator is a manifest.Annotator that annotates B-Tree
643+ // nodes with the sum of the files' compensated sizes. Compensated sizes may
644+ // change once a table's stats are loaded asynchronously, so its values are
645+ // marked as cacheable only if a file's stats have been loaded.
646+ var compensatedSizeAnnotator = manifest .SumAnnotator (func (f * fileMetadata ) (uint64 , bool ) {
647+ return compensatedSize (f ), f .StatsValid ()
648+ })
675649
676650// totalCompensatedSize computes the compensated size over a file metadata
677651// iterator. Note that this function is linear in the files available to the
@@ -912,10 +886,6 @@ func calculateSizeAdjust(inProgressCompactions []compactionInfo) [numLevels]leve
912886 return sizeAdjust
913887}
914888
915- func levelCompensatedSize (lm manifest.LevelMetadata ) uint64 {
916- return * lm .Annotation (compensatedSizeAnnotator {}).(* uint64 )
917- }
918-
919889func (p * compactionPickerByScore ) calculateLevelScores (
920890 inProgressCompactions []compactionInfo ,
921891) [numLevels ]candidateLevelInfo {
@@ -932,7 +902,7 @@ func (p *compactionPickerByScore) calculateLevelScores(
932902 }
933903 sizeAdjust := calculateSizeAdjust (inProgressCompactions )
934904 for level := 1 ; level < numLevels ; level ++ {
935- compensatedLevelSize := levelCompensatedSize (p .vers .Levels [level ]) + sizeAdjust [level ].compensated ()
905+ compensatedLevelSize := * compensatedSizeAnnotator . LevelAnnotation (p .vers .Levels [level ]) + sizeAdjust [level ].compensated ()
936906 scores [level ].compensatedScore = float64 (compensatedLevelSize ) / float64 (p .levelMaxBytes [level ])
937907 scores [level ].uncompensatedScore = float64 (p .vers .Levels [level ].Size ()+ sizeAdjust [level ].actual ()) / float64 (p .levelMaxBytes [level ])
938908 }
@@ -1393,109 +1363,56 @@ func (p *compactionPickerByScore) addScoresToPickedCompactionMetrics(
13931363 }
13941364}
13951365
1396- // elisionOnlyAnnotator implements the manifest.Annotator interface,
1397- // annotating B-Tree nodes with the *fileMetadata of a file meeting the
1398- // obsolete keys criteria for an elision-only compaction within the subtree.
1399- // If multiple files meet the criteria, it chooses whichever file has the
1400- // lowest LargestSeqNum. The lowest LargestSeqNum file will be the first
1401- // eligible for an elision-only compaction once snapshots less than or equal
1402- // to its LargestSeqNum are closed.
1403- type elisionOnlyAnnotator struct {}
1404-
1405- var _ manifest.Annotator = elisionOnlyAnnotator {}
1406-
1407- func (a elisionOnlyAnnotator ) Zero (interface {}) interface {} {
1408- return nil
1409- }
1410-
1411- func (a elisionOnlyAnnotator ) Accumulate (f * fileMetadata , dst interface {}) (interface {}, bool ) {
1412- if f .IsCompacting () {
1413- return dst , true
1414- }
1415- if ! f .StatsValid () {
1416- return dst , false
1417- }
1418- // Bottommost files are large and not worthwhile to compact just
1419- // to remove a few tombstones. Consider a file ineligible if its
1420- // own range deletions delete less than 10% of its data and its
1421- // deletion tombstones make up less than 10% of its entries.
1422- //
1423- // TODO(jackson): This does not account for duplicate user keys
1424- // which may be collapsed. Ideally, we would have 'obsolete keys'
1425- // statistics that would include tombstones, the keys that are
1426- // dropped by tombstones and duplicated user keys. See #847.
1427- //
1428- // Note that tables that contain exclusively range keys (i.e. no point keys,
1429- // `NumEntries` and `RangeDeletionsBytesEstimate` are both zero) are excluded
1430- // from elision-only compactions.
1431- // TODO(travers): Consider an alternative heuristic for elision of range-keys.
1432- if f .Stats .RangeDeletionsBytesEstimate * 10 < f .Size &&
1433- f .Stats .NumDeletions * 10 <= f .Stats .NumEntries {
1434- return dst , true
1435- }
1436- if dst == nil {
1437- return f , true
1438- } else if dstV := dst .(* fileMetadata ); dstV .LargestSeqNum > f .LargestSeqNum {
1439- return f , true
1440- }
1441- return dst , true
1442- }
1443-
1444- func (a elisionOnlyAnnotator ) Merge (v interface {}, accum interface {}) interface {} {
1445- if v == nil {
1446- return accum
1447- }
1448- // If we haven't accumulated an eligible file yet, or f's LargestSeqNum is
1449- // less than the accumulated file's, use f.
1450- if accum == nil {
1451- return v
1452- }
1453- f := v .(* fileMetadata )
1454- accumV := accum .(* fileMetadata )
1455- if accumV == nil || accumV .LargestSeqNum > f .LargestSeqNum {
1456- return f
1457- }
1458- return accumV
1459- }
1460-
1461- // markedForCompactionAnnotator implements the manifest.Annotator interface,
1462- // annotating B-Tree nodes with the *fileMetadata of a file that is marked for
1463- // compaction within the subtree. If multiple files meet the criteria, it
1464- // chooses whichever file has the lowest LargestSeqNum.
1465- type markedForCompactionAnnotator struct {}
1466-
1467- var _ manifest.Annotator = markedForCompactionAnnotator {}
1468-
1469- func (a markedForCompactionAnnotator ) Zero (interface {}) interface {} {
1470- return nil
1471- }
1472-
1473- func (a markedForCompactionAnnotator ) Accumulate (
1474- f * fileMetadata , dst interface {},
1475- ) (interface {}, bool ) {
1476- if ! f .MarkedForCompaction {
1477- // Not marked for compaction; return dst.
1478- return dst , true
1479- }
1480- return markedMergeHelper (f , dst )
1481- }
1482-
1483- func (a markedForCompactionAnnotator ) Merge (v interface {}, accum interface {}) interface {} {
1484- if v == nil {
1485- return accum
1486- }
1487- accum , _ = markedMergeHelper (v .(* fileMetadata ), accum )
1488- return accum
1489- }
1490-
1491- // REQUIRES: f is non-nil, and f.MarkedForCompaction=true.
1492- func markedMergeHelper (f * fileMetadata , dst interface {}) (interface {}, bool ) {
1493- if dst == nil {
1494- return f , true
1495- } else if dstV := dst .(* fileMetadata ); dstV .LargestSeqNum > f .LargestSeqNum {
1496- return f , true
1497- }
1498- return dst , true
1366+ // elisionOnlyAnnotator is a manifest.Annotator that annotates B-Tree
1367+ // nodes with the *fileMetadata of a file meeting the obsolete keys criteria
1368+ // for an elision-only compaction within the subtree. If multiple files meet
1369+ // the criteria, it chooses whichever file has the lowest LargestSeqNum. The
1370+ // lowest LargestSeqNum file will be the first eligible for an elision-only
1371+ // compaction once snapshots less than or equal to its LargestSeqNum are closed.
1372+ var elisionOnlyAnnotator = & manifest.Annotator [fileMetadata ]{
1373+ Aggregator : manifest.PickFileAggregator {
1374+ Filter : func (f * fileMetadata ) (eligible bool , cacheOK bool ) {
1375+ if f .IsCompacting () {
1376+ return false , true
1377+ }
1378+ if ! f .StatsValid () {
1379+ return false , false
1380+ }
1381+ // Bottommost files are large and not worthwhile to compact just
1382+ // to remove a few tombstones. Consider a file eligible only if
1383+ // either its own range deletions delete at least 10% of its data or
1384+ // its deletion tombstones make at least 10% of its entries.
1385+ //
1386+ // TODO(jackson): This does not account for duplicate user keys
1387+ // which may be collapsed. Ideally, we would have 'obsolete keys'
1388+ // statistics that would include tombstones, the keys that are
1389+ // dropped by tombstones and duplicated user keys. See #847.
1390+ //
1391+ // Note that tables that contain exclusively range keys (i.e. no point keys,
1392+ // `NumEntries` and `RangeDeletionsBytesEstimate` are both zero) are excluded
1393+ // from elision-only compactions.
1394+ // TODO(travers): Consider an alternative heuristic for elision of range-keys.
1395+ return f .Stats .RangeDeletionsBytesEstimate * 10 >= f .Size || f .Stats .NumDeletions * 10 > f .Stats .NumEntries , true
1396+ },
1397+ Compare : func (f1 * fileMetadata , f2 * fileMetadata ) bool {
1398+ return f1 .LargestSeqNum < f2 .LargestSeqNum
1399+ },
1400+ },
1401+ }
1402+
1403+ // markedForCompactionAnnotator is a manifest.Annotator that annotates B-Tree
1404+ // nodes with the *fileMetadata of a file that is marked for compaction
1405+ // within the subtree. If multiple files meet the criteria, it chooses
1406+ // whichever file has the lowest LargestSeqNum.
1407+ var markedForCompactionAnnotator = & manifest.Annotator [fileMetadata ]{
1408+ Aggregator : manifest.PickFileAggregator {
1409+ Filter : func (f * fileMetadata ) (eligible bool , cacheOK bool ) {
1410+ return f .MarkedForCompaction , true
1411+ },
1412+ Compare : func (f1 * fileMetadata , f2 * fileMetadata ) bool {
1413+ return f1 .LargestSeqNum < f2 .LargestSeqNum
1414+ },
1415+ },
14991416}
15001417
15011418// pickElisionOnlyCompaction looks for compactions of sstables in the
@@ -1506,11 +1423,10 @@ func (p *compactionPickerByScore) pickElisionOnlyCompaction(
15061423 if p .opts .private .disableElisionOnlyCompactions {
15071424 return nil
15081425 }
1509- v := p .vers .Levels [numLevels - 1 ]. Annotation ( elisionOnlyAnnotator {} )
1510- if v == nil {
1426+ candidate := elisionOnlyAnnotator . LevelAnnotation ( p .vers .Levels [numLevels - 1 ])
1427+ if candidate == nil {
15111428 return nil
15121429 }
1513- candidate := v .(* fileMetadata )
15141430 if candidate .IsCompacting () || candidate .LargestSeqNum >= env .earliestSnapshotSeqNum {
15151431 return nil
15161432 }
@@ -1542,12 +1458,11 @@ func (p *compactionPickerByScore) pickElisionOnlyCompaction(
15421458// the input level.
15431459func (p * compactionPickerByScore ) pickRewriteCompaction (env compactionEnv ) (pc * pickedCompaction ) {
15441460 for l := numLevels - 1 ; l >= 0 ; l -- {
1545- v := p .vers .Levels [l ]. Annotation ( markedForCompactionAnnotator {} )
1546- if v == nil {
1461+ candidate := markedForCompactionAnnotator . LevelAnnotation ( p .vers .Levels [l ])
1462+ if candidate == nil {
15471463 // Try the next level.
15481464 continue
15491465 }
1550- candidate := v .(* fileMetadata )
15511466 if candidate .IsCompacting () {
15521467 // Try the next level.
15531468 continue
0 commit comments