github.com/qiniu/go-sdk/v7@v7.27.0/sandbox/poll.go (about)

     1  package sandbox
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  )
     7  
     8  // PollOption 配置轮询行为的选项。
     9  type PollOption func(*pollOpts)
    10  
    11  type pollOpts struct {
    12  	interval    time.Duration
    13  	maxInterval time.Duration
    14  	backoff     float64 // 退避倍数,默认 1.0(无退避)
    15  	onPoll      func(attempt int)
    16  }
    17  
    18  func defaultPollOpts(defaultInterval time.Duration) *pollOpts {
    19  	return &pollOpts{
    20  		interval:    defaultInterval,
    21  		maxInterval: 0,
    22  		backoff:     1.0,
    23  	}
    24  }
    25  
    26  // WithPollInterval 设置轮询间隔。
    27  func WithPollInterval(d time.Duration) PollOption {
    28  	return func(o *pollOpts) { o.interval = d }
    29  }
    30  
    31  // WithBackoff 设置指数退避倍数和最大间隔。
    32  // multiplier 为每次轮询后间隔的乘数(如 1.5 表示每次增加 50%),
    33  // maxInterval 为间隔上限(0 表示不限制)。
    34  func WithBackoff(multiplier float64, maxInterval time.Duration) PollOption {
    35  	return func(o *pollOpts) {
    36  		o.backoff = multiplier
    37  		o.maxInterval = maxInterval
    38  	}
    39  }
    40  
    41  // WithOnPoll 设置每次轮询时的回调函数。
    42  // attempt 从 1 开始递增。
    43  func WithOnPoll(fn func(attempt int)) PollOption {
    44  	return func(o *pollOpts) { o.onPoll = fn }
    45  }
    46  
    47  // pollLoop 是 WaitForReady 和 WaitForBuild 共享的轮询循环。
    48  // pollFn 在每次轮询时被调用,返回 (done, result, error)。
    49  func pollLoop[T any](ctx context.Context, opts *pollOpts, pollFn func() (bool, T, error)) (T, error) {
    50  	if opts.interval <= 0 {
    51  		opts.interval = time.Second
    52  	}
    53  
    54  	interval := opts.interval
    55  	var timer *time.Timer
    56  	defer func() {
    57  		if timer != nil {
    58  			timer.Stop()
    59  		}
    60  	}()
    61  
    62  	attempt := 0
    63  	for {
    64  		attempt++
    65  		if opts.onPoll != nil {
    66  			opts.onPoll(attempt)
    67  		}
    68  
    69  		done, result, err := pollFn()
    70  		if err != nil {
    71  			return result, err
    72  		}
    73  		if done {
    74  			return result, nil
    75  		}
    76  
    77  		// 计算下次间隔(退避)
    78  		if opts.backoff > 1.0 {
    79  			interval = time.Duration(float64(interval) * opts.backoff)
    80  			if opts.maxInterval > 0 && interval > opts.maxInterval {
    81  				interval = opts.maxInterval
    82  			}
    83  		}
    84  
    85  		if timer == nil {
    86  			timer = time.NewTimer(interval)
    87  		} else {
    88  			timer.Reset(interval)
    89  		}
    90  		select {
    91  		case <-ctx.Done():
    92  			var zero T
    93  			return zero, ctx.Err()
    94  		case <-timer.C:
    95  		}
    96  	}
    97  }