go.temporal.io/server@v1.23.0/common/persistence/client/fault_injection_errorgenerator.go (about) 1 // The MIT License 2 // 3 // Copyright (c) 2020 Temporal Technologies Inc. All rights reserved. 4 // 5 // Copyright (c) 2020 Uber Technologies, Inc. 6 // 7 // Permission is hereby granted, free of charge, to any person obtaining a copy 8 // of this software and associated documentation files (the "Software"), to deal 9 // in the Software without restriction, including without limitation the rights 10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 // copies of the Software, and to permit persons to whom the Software is 12 // furnished to do so, subject to the following conditions: 13 // 14 // The above copyright notice and this permission notice shall be included in 15 // all copies or substantial portions of the Software. 16 // 17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 // THE SOFTWARE. 24 25 package client 26 27 import ( 28 "fmt" 29 "math/rand" 30 "sort" 31 "sync" 32 "time" 33 34 "go.uber.org/atomic" 35 ) 36 37 type ( 38 DefaultErrorGenerator struct { 39 sync.Mutex 40 rate atomic.Float64 // chance for error to be returned 41 r *rand.Rand // rand is not thread-safe 42 faultMetadata []FaultMetadata // 43 faultWeights []FaultWeight 44 } 45 46 ErrorGenerator interface { 47 Generate() error 48 UpdateRate(rate float64) 49 UpdateWeights(weights []FaultWeight) 50 Rate() float64 51 } 52 53 FaultWeight struct { 54 errFactory ErrorFactory 55 weight float64 56 } 57 58 ErrorFactory func(string) error 59 60 FaultMetadata struct { 61 errFactory ErrorFactory 62 threshold float64 63 } 64 65 NoopErrorGenerator struct{} 66 ) 67 68 func calculateErrorRates(rate float64, weights []FaultWeight) []FaultMetadata { 69 totalCount := 0.0 70 for _, w := range weights { 71 totalCount += w.weight 72 } 73 74 faultMeta := make([]FaultMetadata, len(weights)) 75 count := 0 76 weightSum := 0.0 77 for _, w := range weights { 78 weightSum += w.weight 79 faultMeta[count] = FaultMetadata{ 80 errFactory: w.errFactory, 81 threshold: weightSum / totalCount * rate, 82 } 83 count++ 84 } 85 86 sort.Slice( 87 faultMeta, 88 func(left int, right int) bool { return faultMeta[left].threshold < faultMeta[right].threshold }, 89 ) 90 91 return faultMeta 92 } 93 94 func (p *DefaultErrorGenerator) Rate() float64 { 95 return p.rate.Load() 96 } 97 98 func (p *DefaultErrorGenerator) UpdateRate(rate float64) { 99 if rate > 1 { 100 rate = 1 101 } 102 103 if rate < 0 { 104 rate = 0 105 } 106 107 newFaultMetadata := calculateErrorRates(rate, p.faultWeights) 108 109 p.Lock() 110 defer p.Unlock() 111 112 p.rate.Store(rate) 113 p.faultMetadata = newFaultMetadata 114 } 115 116 func (p *DefaultErrorGenerator) UpdateWeights(errorWeights []FaultWeight) { 117 updatedRates := calculateErrorRates(p.rate.Load(), errorWeights) 118 p.Lock() 119 defer p.Unlock() 120 p.faultMetadata = updatedRates 121 p.faultWeights = errorWeights 122 } 123 124 func NewDefaultErrorGenerator(rate float64, errorWeights []FaultWeight) *DefaultErrorGenerator { 125 result := &DefaultErrorGenerator{ 126 r: rand.New(rand.NewSource(time.Now().UnixNano())), 127 } 128 result.UpdateRate(rate) 129 result.UpdateWeights(errorWeights) 130 return result 131 } 132 133 func (p *DefaultErrorGenerator) Generate() error { 134 if p.rate.Load() <= 0 { 135 return nil 136 } 137 138 p.Lock() 139 defer p.Unlock() 140 141 if roll := p.r.Float64(); roll < p.rate.Load() { 142 var result error 143 for _, fm := range p.faultMetadata { 144 if roll < fm.threshold { 145 msg := fmt.Sprintf( 146 "FaultInjectionGenerator. rate %f, roll: %f, treshold: %f", 147 p.rate.Load(), 148 roll, 149 fm.threshold, 150 ) 151 result = fm.errFactory(msg) 152 break 153 } 154 } 155 return result 156 } 157 return nil 158 } 159 160 func NewNoopErrorGenerator() *NoopErrorGenerator { 161 return &NoopErrorGenerator{} 162 } 163 164 func (p *NoopErrorGenerator) Generate() error { 165 return nil 166 } 167 168 func (p *NoopErrorGenerator) Rate() float64 { return 0 } 169 170 func (p *NoopErrorGenerator) UpdateRate(rate float64) {} 171 172 func (p *NoopErrorGenerator) UpdateWeights(weights []FaultWeight) {}