gitee.com/quant1x/gox@v1.21.2/cron/chain.go (about) 1 package cron 2 3 import ( 4 "fmt" 5 "runtime" 6 "sync" 7 "time" 8 ) 9 10 // JobWrapper decorates the given Job with some behavior. 11 type JobWrapper func(Job) Job 12 13 // Chain is a sequence of JobWrappers that decorates submitted jobs with 14 // cross-cutting behaviors like logging or synchronization. 15 type Chain struct { 16 wrappers []JobWrapper 17 } 18 19 // NewChain returns a Chain consisting of the given JobWrappers. 20 func NewChain(c ...JobWrapper) Chain { 21 return Chain{c} 22 } 23 24 // Then decorates the given job with all JobWrappers in the chain. 25 // 26 // This: 27 // 28 // NewChain(m1, m2, m3).Then(job) 29 // 30 // is equivalent to: 31 // 32 // m1(m2(m3(job))) 33 func (c Chain) Then(j Job) Job { 34 for i := range c.wrappers { 35 j = c.wrappers[len(c.wrappers)-i-1](j) 36 } 37 return j 38 } 39 40 // Recover panics in wrapped jobs and log them with the provided logger. 41 func Recover(logger Logger) JobWrapper { 42 return func(j Job) Job { 43 return FuncJob(func() { 44 defer func() { 45 if r := recover(); r != nil { 46 const size = 64 << 10 47 buf := make([]byte, size) 48 buf = buf[:runtime.Stack(buf, false)] 49 err, ok := r.(error) 50 if !ok { 51 err = fmt.Errorf("%v", r) 52 } 53 logger.Error(err, "panic", "stack", "...\n"+string(buf)) 54 } 55 }() 56 j.Run() 57 }) 58 } 59 } 60 61 // DelayIfStillRunningWithLogger serializes jobs, delaying subsequent runs until the 62 // previous one is complete. Jobs running after a delay of more than a minute 63 // have the delay logged at Info. 64 func DelayIfStillRunningWithLogger(logger Logger) JobWrapper { 65 return func(j Job) Job { 66 var mu sync.Mutex 67 return FuncJob(func() { 68 start := time.Now() 69 mu.Lock() 70 defer mu.Unlock() 71 if dur := time.Since(start); dur > time.Minute { 72 logger.Info("delay", "duration", dur) 73 } 74 j.Run() 75 }) 76 } 77 } 78 79 // SkipIfStillRunningWithLogger skips an invocation of the Job if a previous invocation is 80 // still running. It logs skips to the given logger at Info level. 81 func SkipIfStillRunningWithLogger(logger Logger) JobWrapper { 82 return func(j Job) Job { 83 var ch = make(chan struct{}, 1) 84 ch <- struct{}{} 85 return FuncJob(func() { 86 select { 87 case v := <-ch: 88 defer func() { ch <- v }() 89 j.Run() 90 default: 91 logger.Info("skip") 92 } 93 }) 94 } 95 } 96 97 // SkipIfStillRunning skips an invocation of the Job if a previous invocation is 98 // still running. It logs skips to the given logger at Info level. 99 func SkipIfStillRunning() JobWrapper { 100 return func(j Job) Job { 101 var ch = make(chan struct{}, 1) 102 ch <- struct{}{} 103 return FuncJob(func() { 104 select { 105 case v := <-ch: 106 defer func() { ch <- v }() 107 j.Run() 108 default: 109 // 110 } 111 }) 112 } 113 }