github.com/wfusion/gofusion@v1.1.14/common/infra/watermill/pubsub/sql/backoff_manager.go (about)

     1  package sql
     2  
     3  import (
     4  	"strings"
     5  	"time"
     6  
     7  	"github.com/wfusion/gofusion/common/infra/watermill"
     8  )
     9  
    10  // BackoffManager handles errors or empty result sets and computes the backoff time.
    11  // You could for example create a stateful version that computes a backoff depending on the error frequency or make errors more or less persistent.
    12  type BackoffManager interface {
    13  	// HandleError handles the error possibly logging it or returning a backoff time depending on the error or the absence of the message.
    14  	HandleError(logger watermill.LoggerAdapter, noMsg bool, err error) time.Duration
    15  }
    16  
    17  func NewDefaultBackoffManager(pollInterval, retryInterval time.Duration) BackoffManager {
    18  	if pollInterval == 0 {
    19  		pollInterval = time.Second
    20  	}
    21  	if retryInterval == 0 {
    22  		retryInterval = time.Second
    23  	}
    24  	return &defaultBackoffManager{
    25  		retryInterval: retryInterval,
    26  		pollInterval:  pollInterval,
    27  		deadlockIndicators: []string{
    28  			// MySQL deadlock indicator
    29  			"deadlock",
    30  
    31  			// PostgreSQL deadlock indicator
    32  			"concurrent update",
    33  		},
    34  	}
    35  }
    36  
    37  type defaultBackoffManager struct {
    38  	pollInterval       time.Duration
    39  	retryInterval      time.Duration
    40  	deadlockIndicators []string
    41  }
    42  
    43  func (d defaultBackoffManager) HandleError(logger watermill.LoggerAdapter, noMsg bool, err error) time.Duration {
    44  	if err != nil {
    45  		var deadlock bool
    46  		for _, indicator := range d.deadlockIndicators {
    47  			if strings.Contains(strings.ToLower(err.Error()), indicator) {
    48  				deadlock = true
    49  				break
    50  			}
    51  		}
    52  		if deadlock {
    53  			logger.Debug("Deadlock during querying message, trying again", watermill.LogFields{
    54  				"err": err.Error(),
    55  			})
    56  			return 0
    57  		} else {
    58  			logger.Error("Error querying for message", err, watermill.LogFields{
    59  				"wait_time": d.retryInterval,
    60  			})
    61  			return d.retryInterval
    62  		}
    63  	}
    64  	if noMsg {
    65  		return d.pollInterval
    66  	}
    67  	return 0
    68  }