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  }