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  }