vitess.io/vitess@v0.16.2/go/vt/concurrency/error_recorder.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package concurrency 18 19 import ( 20 "fmt" 21 "strings" 22 "sync" 23 24 "vitess.io/vitess/go/vt/log" 25 ) 26 27 // ErrorRecorder offers a way to record errors during complex 28 // asynchronous operations. Various implementation will offer 29 // different services. 30 type ErrorRecorder interface { 31 RecordError(error) 32 HasErrors() bool 33 Error() error 34 } 35 36 // FirstErrorRecorder records the first error, logs the others. 37 // Error() will return the first recorded error or nil. 38 type FirstErrorRecorder struct { 39 mu sync.Mutex 40 errorCount int 41 firstError error 42 } 43 44 // RecordError records a possible error: 45 // - does nothing if err is nil 46 // - only records the first error reported 47 // - the rest is just logged 48 func (fer *FirstErrorRecorder) RecordError(err error) { 49 if err == nil { 50 return 51 } 52 fer.mu.Lock() 53 fer.errorCount++ 54 if fer.errorCount == 1 { 55 fer.firstError = err 56 } else { 57 log.Errorf("FirstErrorRecorder: error[%v]: %v", fer.errorCount, err) 58 } 59 fer.mu.Unlock() 60 } 61 62 // HasErrors returns true if we ever recorded an error 63 func (fer *FirstErrorRecorder) HasErrors() bool { 64 fer.mu.Lock() 65 defer fer.mu.Unlock() 66 return fer.errorCount > 0 67 } 68 69 // Error returns the first error we saw, or nil 70 func (fer *FirstErrorRecorder) Error() error { 71 fer.mu.Lock() 72 defer fer.mu.Unlock() 73 return fer.firstError 74 } 75 76 // AllErrorRecorder records all the errors. 77 type AllErrorRecorder struct { 78 mu sync.Mutex 79 Errors []error 80 } 81 82 // RecordError records a possible error: 83 // - does nothing if err is nil 84 func (aer *AllErrorRecorder) RecordError(err error) { 85 if err == nil { 86 return 87 } 88 aer.mu.Lock() 89 aer.Errors = append(aer.Errors, err) 90 aer.mu.Unlock() 91 } 92 93 // HasErrors returns true if we ever recorded an error 94 func (aer *AllErrorRecorder) HasErrors() bool { 95 aer.mu.Lock() 96 defer aer.mu.Unlock() 97 return len(aer.Errors) > 0 98 } 99 100 // AllErrorAggregator aggregates errors. 101 type AllErrorAggregator func(errors []error) error 102 103 // AggrError runs the provided aggregator over all errors 104 // and returns the error from aggregator. 105 func (aer *AllErrorRecorder) AggrError(aggr AllErrorAggregator) error { 106 aer.mu.Lock() 107 defer aer.mu.Unlock() 108 if len(aer.Errors) == 0 { 109 return nil 110 } 111 return aggr(aer.Errors) 112 } 113 114 // Error returns an aggregate of all errors by concatenation. 115 func (aer *AllErrorRecorder) Error() error { 116 return aer.AggrError(func(errors []error) error { 117 errs := make([]string, 0, len(errors)) 118 for _, e := range errors { 119 errs = append(errs, e.Error()) 120 } 121 return fmt.Errorf("%v", strings.Join(errs, ";")) 122 }) 123 } 124 125 // ErrorStrings returns all errors as string array. 126 func (aer *AllErrorRecorder) ErrorStrings() []string { 127 aer.mu.Lock() 128 defer aer.mu.Unlock() 129 if len(aer.Errors) == 0 { 130 return nil 131 } 132 errs := make([]string, 0, len(aer.Errors)) 133 for _, e := range aer.Errors { 134 errs = append(errs, e.Error()) 135 } 136 return errs 137 } 138 139 // GetErrors returns a reference to the internal errors array. 140 // 141 // Note that the array is not copied, so this should only be used 142 // once the recording is complete. 143 func (aer *AllErrorRecorder) GetErrors() []error { 144 aer.mu.Lock() 145 defer aer.mu.Unlock() 146 return aer.Errors 147 }