gitlab.com/jokerrs1/Sia@v1.3.2/modules/host/errors.go (about)

     1  package host
     2  
     3  // errors.go is responsible for logging the various errors that the host runs
     4  // into related to operations that cannot immediately provide feedback to the
     5  // user. (e.g. network failures, disk failures, etc.). Different errors should
     6  // be handled and logged differently, depending on severity and frequency, such
     7  // that the person reading the logs is able to see all of the major issues
     8  // without having them obstructed by the minor ones.
     9  
    10  import (
    11  	"errors"
    12  	"strings"
    13  	"sync/atomic"
    14  
    15  	"github.com/NebulousLabs/fastrand"
    16  )
    17  
    18  const (
    19  	errorCommunicationProbability = 5
    20  	errorConnectionProbability    = 20
    21  	errorConsensusProbability     = 1
    22  	errorInternalProbability      = 3
    23  	errorNormalProbability        = 20
    24  )
    25  
    26  type (
    27  	// ErrorCommunication errors are meant to be returned if the host and the
    28  	// renter seem to be miscommunicating. For example, if the renter attempts
    29  	// to pay an insufficient price, there has been a communication error.
    30  	ErrorCommunication string
    31  
    32  	// ErrorConnection is meant to be used on errors where the network is
    33  	// returning unexpected errors. For example, sudden disconnects or
    34  	// connection write failures.
    35  	ErrorConnection string
    36  
    37  	// ErrorConsensus errors are meant to be used when there are problems
    38  	// related to consensus, such as an inability to submit a storage proof to
    39  	// the blockchain, or an inability to get a file contract revision on to
    40  	// the blockchain.
    41  	ErrorConsensus string
    42  
    43  	// ErrorInternal errors are meant to be used if an internal process in the
    44  	// host is malfunctioning, for example if the disk is failing.
    45  	ErrorInternal string
    46  )
    47  
    48  // composeErrors will take multiple errors and compose them into a single
    49  // errors with a longer message. Any nil errors used as inputs will be stripped
    50  // out, and if there are zero non-nil inputs then 'nil' will be returned.
    51  //
    52  // The original types of the errors is not preserved at all.
    53  func composeErrors(errs ...error) error {
    54  	// Strip out any nil errors.
    55  	var errStrings []string
    56  	for _, err := range errs {
    57  		if err != nil {
    58  			errStrings = append(errStrings, err.Error())
    59  		}
    60  	}
    61  
    62  	// Return nil if there are no non-nil errors in the input.
    63  	if len(errStrings) <= 0 {
    64  		return nil
    65  	}
    66  
    67  	// Combine all of the non-nil errors into one larger return value.
    68  	return errors.New(strings.Join(errStrings, "; "))
    69  }
    70  
    71  // extendErr will return an error that is the same type as the input error, but
    72  // prefixed with the provided context. This only works for the error types
    73  // defined in the host package. If the input error is nil, the extension is
    74  // ignored and nil will be returned.
    75  func extendErr(s string, err error) error {
    76  	if err == nil {
    77  		return nil
    78  	}
    79  
    80  	switch v := err.(type) {
    81  	case ErrorCommunication:
    82  		return ErrorCommunication(s) + v
    83  	case ErrorConnection:
    84  		return ErrorConnection(s) + v
    85  	case ErrorConsensus:
    86  		return ErrorConsensus(s) + v
    87  	case ErrorInternal:
    88  		return ErrorInternal(s) + v
    89  	default:
    90  		return errors.New(s + err.Error())
    91  	}
    92  
    93  }
    94  
    95  // Error satisfies the Error interface for the ErrorCommunication type.
    96  func (ec ErrorCommunication) Error() string {
    97  	return "communication error: " + string(ec)
    98  }
    99  
   100  // Error satisfies the Error interface for the ErrorConnection type.
   101  func (ec ErrorConnection) Error() string {
   102  	return "connection error: " + string(ec)
   103  }
   104  
   105  // Error satisfies the Error interface for the ErrorConsensus type.
   106  func (ec ErrorConsensus) Error() string {
   107  	return "consensus error: " + string(ec)
   108  }
   109  
   110  // Error satisfies the Error interface for the ErrorInternal type.
   111  func (ec ErrorInternal) Error() string {
   112  	return "internal error: " + string(ec)
   113  }
   114  
   115  // mangedLogError will take an error and log it to the host, depending on the
   116  // type of error and whether or not the DEBUG flag has been set.
   117  func (h *Host) managedLogError(err error) {
   118  	// Determine the type of error and the number of times that this error has
   119  	// been logged.
   120  	var num uint64
   121  	var probability int // Error will be logged with 1/probability chance.
   122  	switch err.(type) {
   123  	case ErrorCommunication:
   124  		num = atomic.LoadUint64(&h.atomicCommunicationErrors)
   125  		probability = errorCommunicationProbability
   126  	case ErrorConnection:
   127  		num = atomic.LoadUint64(&h.atomicConnectionErrors)
   128  		probability = errorConnectionProbability
   129  	case ErrorConsensus:
   130  		num = atomic.LoadUint64(&h.atomicConsensusErrors)
   131  		probability = errorConsensusProbability
   132  	case ErrorInternal:
   133  		num = atomic.LoadUint64(&h.atomicInternalErrors)
   134  		probability = errorInternalProbability
   135  	default:
   136  		num = atomic.LoadUint64(&h.atomicNormalErrors)
   137  		probability = errorNormalProbability
   138  	}
   139  
   140  	// If num > logFewLimit, substantially decrease the probability that the error
   141  	// gets logged.
   142  	if num > logFewLimit {
   143  		probability = probability * 25
   144  	}
   145  
   146  	// If we've seen less than logAllLimit of that type of error before, log
   147  	// the error as a normal logging statement. Otherwise, probabilistically
   148  	// log the statement. In debugging mode, log all statements.
   149  	shouldLog := num < logAllLimit || fastrand.Intn(probability+1) == probability
   150  	if shouldLog {
   151  		h.log.Println(err)
   152  	} else {
   153  		h.log.Debugln(err)
   154  		return
   155  	}
   156  
   157  	// Increment the log counter.
   158  	switch err.(type) {
   159  	case ErrorCommunication:
   160  		atomic.AddUint64(&h.atomicCommunicationErrors, 1)
   161  	case ErrorConnection:
   162  		atomic.AddUint64(&h.atomicConnectionErrors, 1)
   163  	case ErrorConsensus:
   164  		atomic.AddUint64(&h.atomicConsensusErrors, 1)
   165  	case ErrorInternal:
   166  		atomic.AddUint64(&h.atomicInternalErrors, 1)
   167  	default:
   168  		atomic.AddUint64(&h.atomicNormalErrors, 1)
   169  	}
   170  }