github.com/zuoyebang/bitalostable@v1.0.1-0.20240229032404-e3b99a834294/pacer.go (about) 1 // Copyright 2019 The LevelDB-Go and Pebble and Bitalostored Authors. All rights reserved. Use 2 // of this source code is governed by a BSD-style license that can be found in 3 // the LICENSE file. 4 5 package bitalostable 6 7 import ( 8 "time" 9 10 "github.com/cockroachdb/errors" 11 "github.com/zuoyebang/bitalostable/internal/rate" 12 ) 13 14 var nilPacer = &noopPacer{} 15 16 type limiter interface { 17 DelayN(now time.Time, n int) time.Duration 18 AllowN(now time.Time, n int) bool 19 Burst() int 20 } 21 22 // pacer is the interface for flush and compaction rate limiters. The rate limiter 23 // is possible applied on each iteration step of a flush or compaction. This is to 24 // limit background IO usage so that it does not contend with foreground traffic. 25 type pacer interface { 26 maybeThrottle(bytesIterated uint64) error 27 } 28 29 // deletionPacerInfo contains any info from the db necessary to make deletion 30 // pacing decisions. 31 type deletionPacerInfo struct { 32 freeBytes uint64 33 obsoleteBytes uint64 34 liveBytes uint64 35 } 36 37 // deletionPacer rate limits deletions of obsolete files. This is necessary to 38 // prevent overloading the disk with too many deletions too quickly after a 39 // large compaction, or an iterator close. On some SSDs, disk performance can be 40 // negatively impacted if too many blocks are deleted very quickly, so this 41 // mechanism helps mitigate that. 42 type deletionPacer struct { 43 limiter limiter 44 freeSpaceThreshold uint64 45 obsoleteBytesMaxRatio float64 46 47 getInfo func() deletionPacerInfo 48 } 49 50 // newDeletionPacer instantiates a new deletionPacer for use when deleting 51 // obsolete files. The limiter passed in must be a singleton shared across this 52 // bitalostable instance. 53 func newDeletionPacer(limiter limiter, getInfo func() deletionPacerInfo) *deletionPacer { 54 return &deletionPacer{ 55 limiter: limiter, 56 // If there are less than freeSpaceThreshold bytes of free space on 57 // disk, do not pace deletions at all. 58 freeSpaceThreshold: 16 << 30, // 16 GB 59 // If the ratio of obsolete bytes to live bytes is greater than 60 // obsoleteBytesMaxRatio, do not pace deletions at all. 61 obsoleteBytesMaxRatio: 0.20, 62 63 getInfo: getInfo, 64 } 65 } 66 67 // limit applies rate limiting if the current free disk space is more than 68 // freeSpaceThreshold, and the ratio of obsolete to live bytes is less than 69 // obsoleteBytesMaxRatio. 70 func (p *deletionPacer) limit(amount uint64, info deletionPacerInfo) error { 71 obsoleteBytesRatio := float64(1.0) 72 if info.liveBytes > 0 { 73 obsoleteBytesRatio = float64(info.obsoleteBytes) / float64(info.liveBytes) 74 } 75 paceDeletions := info.freeBytes > p.freeSpaceThreshold && 76 obsoleteBytesRatio < p.obsoleteBytesMaxRatio 77 if paceDeletions { 78 burst := p.limiter.Burst() 79 for amount > uint64(burst) { 80 d := p.limiter.DelayN(time.Now(), burst) 81 if d == rate.InfDuration { 82 return errors.Errorf("pacing failed") 83 } 84 time.Sleep(d) 85 amount -= uint64(burst) 86 } 87 d := p.limiter.DelayN(time.Now(), int(amount)) 88 if d == rate.InfDuration { 89 return errors.Errorf("pacing failed") 90 } 91 time.Sleep(d) 92 } else { 93 burst := p.limiter.Burst() 94 for amount > uint64(burst) { 95 // AllowN will subtract burst if there are enough tokens available, 96 // else leave the tokens untouched. That is, we are making a 97 // best-effort to account for this activity in the limiter, but by 98 // ignoring the return value, we do the activity instantaneously 99 // anyway. 100 p.limiter.AllowN(time.Now(), burst) 101 amount -= uint64(burst) 102 } 103 p.limiter.AllowN(time.Now(), int(amount)) 104 } 105 return nil 106 } 107 108 // maybeThrottle slows down a deletion of this file if it's faster than 109 // opts.Experimental.MinDeletionRate. 110 func (p *deletionPacer) maybeThrottle(bytesToDelete uint64) error { 111 return p.limit(bytesToDelete, p.getInfo()) 112 } 113 114 type noopPacer struct{} 115 116 func (p *noopPacer) maybeThrottle(_ uint64) error { 117 return nil 118 }