github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/admission/admissionpb/io_threshold.go (about) 1 // Copyright 2022 The Cockroach Authors. 2 // 3 // Use of this software is governed by the Business Source License 4 // included in the file licenses/BSL.txt. 5 // 6 // As of the Change Date specified in that file, in accordance with 7 // the Business Source License, use of this software will be governed 8 // by the Apache License, Version 2.0, included in the file 9 // licenses/APL.txt. 10 11 package admissionpb 12 13 import ( 14 "math" 15 16 "github.com/cockroachdb/redact" 17 "github.com/cockroachdb/redact/interfaces" 18 ) 19 20 // Score returns, as the second return value, whether IO admission control is 21 // considering the Store overloaded wrt compaction of L0. The first return 22 // value is a 1-normalized float (i.e. 1.0 is the threshold at which the 23 // second value flips to true). 24 // 25 // The zero value returns (0, false). Use of the nil pointer is not allowed. 26 // 27 // TODO(sumeer): consider whether we need to enhance this to incorporate 28 // overloading via flush bandwidth. I suspect we can get away without 29 // incorporating flush bandwidth since typically chronic overload will be due 30 // to compactions falling behind (though that may change if we increase the 31 // max number of compactions). And we will need to incorporate overload due to 32 // disk bandwidth bottleneck. 33 func (iot *IOThreshold) Score() (float64, bool) { 34 // iot.L0NumFilesThreshold and iot.L0NumSubLevelsThreshold are initialized to 35 // 0 by default, and there appears to be a period of time before we update 36 // iot.L0NumFilesThreshold and iot.L0NumSubLevelsThreshold to their 37 // appropriate values. During this period of time, to prevent dividing by 0 38 // below and Score() returning NaN, we check if iot.L0NumFilesThreshold or 39 // iot.L0NumSubLevelsThreshold are 0 (i.e. currently uninitialized) and 40 // return 0 as the score if so. 41 if iot == nil || iot.L0NumFilesThreshold == 0 || iot.L0NumSubLevelsThreshold == 0 { 42 return 0, false 43 } 44 numSubLevels := iot.L0NumSubLevels 45 if iot.L0MinimumSizePerSubLevel > 0 { 46 // Upper-bound on number of sub-levels. See the comment for the cluster 47 // setting admission.l0_sub_level_count_overload_threshold. 48 maxNumSubLevels := int64(math.Round(float64(iot.L0Size) / float64(iot.L0MinimumSizePerSubLevel))) 49 // Say numSubLevels is 30 and maxNumSubLevels is 5. We don't want to 50 // ignore the huge disparity, since that could allow the numSubLevels to 51 // grow to a huge value. So we place a lower-bound. The divisor of 3 was 52 // chosen somewhat arbitrarily. 53 // 54 // NB: the lower-bound can be greater than the upper-bound, in which case 55 // the lower-bound takes precedence. 56 minNumSubLevels := numSubLevels / 3 57 if numSubLevels > maxNumSubLevels { 58 numSubLevels = maxNumSubLevels 59 } 60 if numSubLevels < minNumSubLevels { 61 numSubLevels = minNumSubLevels 62 } 63 } 64 f := math.Max( 65 float64(iot.L0NumFiles)/float64(iot.L0NumFilesThreshold), 66 float64(numSubLevels)/float64(iot.L0NumSubLevelsThreshold), 67 ) 68 return f, f > 1.0 69 } 70 71 // SafeFormat implements redact.SafeFormatter. 72 func (iot *IOThreshold) SafeFormat(s interfaces.SafePrinter, _ rune) { 73 if iot == nil { 74 s.Printf("N/A") 75 return 76 } 77 sc, overload := iot.Score() 78 s.Printf("%.3f", redact.SafeFloat(sc)) 79 if overload { 80 s.Printf("[L0-overload]") 81 } 82 } 83 84 func (iot *IOThreshold) String() string { 85 return redact.StringWithoutMarkers(iot) 86 }