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 }