github.com/cockroachdb/cockroachdb-parser@v0.23.3-0.20240213214944-911057d40c9a/pkg/util/admission/admissionpb/admissionpb.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/errors" 17 "github.com/cockroachdb/redact" 18 ) 19 20 // WorkPriority represents the priority of work. In an WorkQueue, it is only 21 // used for ordering within a tenant. High priority work can starve lower 22 // priority work. 23 type WorkPriority int8 24 25 // When adding to this list, remember to update the initialization logic of 26 // workPriorityToLockPriMap. 27 const ( 28 // LowPri is low priority work. 29 LowPri WorkPriority = math.MinInt8 30 // TTLLowPri is low priority work from TTL internal submissions. 31 TTLLowPri WorkPriority = -100 32 // UserLowPri is low priority work from user submissions (SQL). 33 UserLowPri WorkPriority = -50 34 // BulkNormalPri is bulk priority work from bulk jobs, which could be run due 35 // to user submissions or be automatic. 36 BulkNormalPri WorkPriority = -30 37 // NormalPri is normal priority work. 38 NormalPri WorkPriority = 0 39 // LockingNormalPri is used for user normal priority transactions that are 40 // acquiring locks. 41 LockingNormalPri WorkPriority = 10 42 // UserHighPri is high priority work from user submissions (SQL). 43 UserHighPri WorkPriority = 50 44 // LockingUserHighPri is for user high priority transactions that are 45 // acquiring locks. 46 LockingUserHighPri WorkPriority = 100 47 // HighPri is high priority work. 48 HighPri WorkPriority = math.MaxInt8 49 // OneAboveHighPri is one priority level above the highest priority. 50 OneAboveHighPri int = int(HighPri) + 1 51 ) 52 53 func (w WorkPriority) String() string { 54 return redact.StringWithoutMarkers(w) 55 } 56 57 // SafeFormat implements the redact.SafeFormatter interface. 58 func (w WorkPriority) SafeFormat(p redact.SafePrinter, verb rune) { 59 if s, ok := WorkPriorityDict[w]; ok { 60 p.Print(s) 61 return 62 } 63 p.Printf("custom-pri=%d", int8(w)) 64 } 65 66 // WorkPriorityDict is a mapping of the priorities to a short string name. The 67 // name is used as the suffix on exported work queue metrics. 68 var WorkPriorityDict = map[WorkPriority]string{ 69 LowPri: "low-pri", 70 TTLLowPri: "ttl-low-pri", 71 UserLowPri: "user-low-pri", 72 BulkNormalPri: "bulk-normal-pri", 73 NormalPri: "normal-pri", 74 LockingNormalPri: "locking-normal-pri", 75 UserHighPri: "user-high-pri", 76 // This ought to be called "locking-user-high-pri", but we retain the old 77 // name for continuity with metrics in older versions. 78 LockingUserHighPri: "locking-pri", 79 HighPri: "high-pri", 80 } 81 82 // workPriorityToLockPriMap maps WorkPriority to another WorkPriority for when 83 // the txn has already acquired a lock. Since WorkPriority can be negative, 84 // and this map is an array, the index into the array is priToArrayIndex(p) 85 // where p is a WorkPriority. 86 // 87 // The priority mapping is not simply p+1 since the enum values are used in 88 // exported metrics, and we don't want to increase the number of such metrics. 89 var workPriorityToLockPriMap [math.MaxInt8 - math.MinInt8 + 1]WorkPriority 90 91 func priToArrayIndex(pri WorkPriority) int { 92 return int(pri) - math.MinInt8 93 } 94 95 // TestingReverseWorkPriorityDict is the reverse-lookup dictionary for 96 // WorkPriorityDict, for use in tests. 97 var TestingReverseWorkPriorityDict map[string]WorkPriority 98 99 func init() { 100 TestingReverseWorkPriorityDict = make(map[string]WorkPriority) 101 for k, v := range WorkPriorityDict { 102 TestingReverseWorkPriorityDict[v] = k 103 } 104 105 orderedPris := []WorkPriority{ 106 LowPri, 107 TTLLowPri, 108 UserLowPri, 109 BulkNormalPri, 110 NormalPri, 111 LockingNormalPri, 112 UserHighPri, 113 LockingUserHighPri, 114 HighPri, 115 } 116 j := 0 117 for i := range workPriorityToLockPriMap { 118 pri := WorkPriority(i) + math.MinInt8 119 if pri == orderedPris[j] && i != len(workPriorityToLockPriMap)-1 { 120 // Move to the next higher priority. 121 j++ 122 } 123 workPriorityToLockPriMap[i] = orderedPris[j] 124 } 125 for i := range workPriorityToLockPriMap { 126 if priToArrayIndex(workPriorityToLockPriMap[i]) < i { 127 panic(errors.AssertionFailedf("workPriorityToLockPriMap at index %d has value %d", 128 i, workPriorityToLockPriMap[i])) 129 } 130 if priToArrayIndex(workPriorityToLockPriMap[i]) == i && i != priToArrayIndex(math.MaxInt8) { 131 panic(errors.AssertionFailedf( 132 "workPriorityToLockPriMap at index %d has no change for locking", i)) 133 } 134 } 135 } 136 137 // AdjustedPriorityWhenHoldingLocks takes the original priority of a 138 // transaction and updates it under the knowledge that the transaction is 139 // holding locks. 140 // 141 // This broader context of locking is technically not in scope of the 142 // admission package, but we define this function here as the WorkPriority 143 // enum values are defined here. 144 // 145 // For example, UserLowPri should map to BulkNormalPri (see the hack below), 146 // NormalPri maps to LockingNormalPri, and UserHighPri maps to 147 // LockingUserHighPri. Say users are running at these different priorities in 148 // different parts of the key space, say key-low, key-normal, key-high, then 149 // even after the mapping, a txn holding locks (or resolving intents) in 150 // key-low will have lower priority (BulkNormalPri) than the non-adjusted 151 // priority in key-normal (NormalPri). The same holds true for txn holding 152 // locks in key-normal, since LockingNormalPri is lower priority than 153 // UserHighPri. 154 // 155 // Adjusting the priority can also be beneficial when all txns have the same 156 // QoS requirements, but there is lock contention. In tpcc with 3000 157 // warehouses, it halved the number of lock waiters, and increased the 158 // transaction throughput by 10+%. In that experiment 40% of the BatchRequests 159 // evaluated by KV had been assigned a higher priority due to locking. 160 func AdjustedPriorityWhenHoldingLocks(pri WorkPriority) WorkPriority { 161 // TODO(sumeer): this is a temporary hack since index backfill and TTL can 162 // be running on tables that have user-facing work. We want these background 163 // transactions (when holding locks) to run at the priority of user-facing 164 // work + 1, but we don't know what that value is. This could be solved by 165 // providing the user-facing work priority in a SpanConfig, but for now we 166 // just assume that all user-facing work is running at NormalPri. See the 167 // examples in intentresolver/admission.go. 168 if pri < NormalPri { 169 pri = NormalPri 170 } 171 return workPriorityToLockPriMap[priToArrayIndex(pri)] 172 } 173 174 // WorkClass represents the class of work, which is defined entirely by its 175 // WorkPriority. Namely, everything less than NormalPri is defined to be 176 // "Elastic", while everything above and including NormalPri is considered 177 // "Regular. 178 type WorkClass int8 179 180 const ( 181 // RegularWorkClass is for work corresponding to workloads that are 182 // throughput and latency sensitive. 183 RegularWorkClass WorkClass = iota 184 // ElasticWorkClass is for work corresponding to workloads that can handle 185 // reduced throughput, possibly by taking longer to finish a workload. It is 186 // not latency sensitive. 187 ElasticWorkClass 188 // NumWorkClasses is the number of work classes. 189 NumWorkClasses 190 ) 191 192 // WorkClassFromPri translates a WorkPriority to its given WorkClass. 193 func WorkClassFromPri(pri WorkPriority) WorkClass { 194 class := RegularWorkClass 195 if pri < NormalPri { 196 class = ElasticWorkClass 197 } 198 return class 199 } 200 201 func (w WorkClass) String() string { 202 return redact.StringWithoutMarkers(w) 203 } 204 205 // SafeFormat implements the redact.SafeFormatter interface. 206 func (w WorkClass) SafeFormat(p redact.SafePrinter, verb rune) { 207 switch w { 208 case RegularWorkClass: 209 p.Printf("regular") 210 case ElasticWorkClass: 211 p.Printf("elastic") 212 default: 213 p.Printf("<unknown-class>") 214 } 215 } 216 217 // Prevent the linter from emitting unused warnings. 218 var _ = LowPri 219 var _ = TTLLowPri 220 var _ = UserLowPri 221 var _ = NormalPri 222 var _ = UserHighPri 223 var _ = LockingUserHighPri 224 var _ = HighPri