go.temporal.io/server@v1.23.0/common/persistence/client/targeted_fault_injection.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 "context" 29 "fmt" 30 "math/rand" 31 "runtime" 32 "strings" 33 "time" 34 35 enumspb "go.temporal.io/api/enums/v1" 36 "go.temporal.io/api/serviceerror" 37 38 "go.temporal.io/server/common/config" 39 "go.temporal.io/server/common/persistence" 40 ) 41 42 // NewTargetedDataStoreErrorGenerator returns a new instance of a data store error generator that will inject errors 43 // into the persistence layer based on the provided configuration. 44 func NewTargetedDataStoreErrorGenerator(cfg *config.FaultInjectionDataStoreConfig) ErrorGenerator { 45 methods := make(map[string]ErrorGenerator, len(cfg.Methods)) 46 for methodName, methodConfig := range cfg.Methods { 47 var faultWeights []FaultWeight 48 methodErrRate := 0.0 49 for errName, errRate := range methodConfig.Errors { 50 err := newError(errName, errRate) 51 faultWeights = append(faultWeights, FaultWeight{ 52 errFactory: func(data string) error { 53 return err 54 }, 55 weight: errRate, 56 }) 57 methodErrRate += errRate 58 } 59 errGenerator := NewDefaultErrorGenerator(methodErrRate, faultWeights) 60 seed := methodConfig.Seed 61 if seed == 0 { 62 seed = time.Now().UnixNano() 63 } 64 errGenerator.r = rand.New(rand.NewSource(seed)) 65 methods[methodName] = errGenerator 66 } 67 return &dataStoreErrorGenerator{MethodErrorGenerators: methods} 68 } 69 70 // dataStoreErrorGenerator is an implementation of ErrorGenerator that will inject errors into the persistence layer 71 // using a per-method configuration. 72 type dataStoreErrorGenerator struct { 73 MethodErrorGenerators map[string]ErrorGenerator 74 } 75 76 // Generate returns an error from the configured error types and rates for this method. 77 // This method infers the fault injection target's method name from the function name of the caller. 78 // As a result, this method should only be called from the persistence layer. 79 // This method will panic if the method name cannot be inferred. 80 // If no errors are configured for the method, or if there are some errors configured for this method, 81 // but no error is sampled, then this method returns nil. 82 // When this method returns nil, this causes the persistence layer to use the real implementation. 83 func (d *dataStoreErrorGenerator) Generate() error { 84 pc, _, _, ok := runtime.Caller(1) 85 if !ok { 86 panic("failed to get caller info") 87 } 88 runtimeFunc := runtime.FuncForPC(pc) 89 if runtimeFunc == nil { 90 panic("failed to get runtime function") 91 } 92 parts := strings.Split(runtimeFunc.Name(), ".") 93 methodName := parts[len(parts)-1] 94 methodErrorGenerator, ok := d.MethodErrorGenerators[methodName] 95 if !ok { 96 return nil 97 } 98 return methodErrorGenerator.Generate() 99 } 100 101 // newError returns an error based on the provided name. If the name is not recognized, then this method will 102 // panic. 103 func newError(errName string, errRate float64) error { 104 switch errName { 105 case "ShardOwnershipLost": 106 return &persistence.ShardOwnershipLostError{Msg: fmt.Sprintf("fault injection error (%f): persistence.ShardOwnershipLostError", errRate)} 107 case "DeadlineExceeded": 108 return fmt.Errorf("fault injection error (%f): %w", errRate, context.DeadlineExceeded) 109 case "ResourceExhausted": 110 return serviceerror.NewResourceExhausted(enumspb.RESOURCE_EXHAUSTED_CAUSE_SYSTEM_OVERLOADED, fmt.Sprintf("fault injection error (%f): serviceerror.ResourceExhausted", errRate)) 111 case "Unavailable": 112 return serviceerror.NewUnavailable(fmt.Sprintf("fault injection error (%f): serviceerror.Unavailable", errRate)) 113 default: 114 panic(fmt.Sprintf("unknown error type: %v", errName)) 115 } 116 } 117 118 // UpdateRate should not be called for the data store error generator since the rate is defined on a per-method basis. 119 func (d *dataStoreErrorGenerator) UpdateRate(rate float64) { 120 panic("UpdateRate not supported for data store error generators") 121 } 122 123 // UpdateWeights should not be called for the data store error generator since the weights are defined on a per-method 124 // basis. 125 func (d *dataStoreErrorGenerator) UpdateWeights(weights []FaultWeight) { 126 panic("UpdateWeights not supported for data store error generators") 127 } 128 129 // Rate should not be called for the data store error generator since there is no global rate for the data store, only 130 // per-method rates. 131 func (d *dataStoreErrorGenerator) Rate() float64 { 132 panic("Rate not supported for data store error generators") 133 }