code.gitea.io/gitea@v1.22.3/modules/queue/backoff.go (about)

     1  // Copyright 2023 The Gitea Authors. All rights reserved.
     2  // SPDX-License-Identifier: MIT
     3  
     4  package queue
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  )
    10  
    11  var (
    12  	backoffBegin = 50 * time.Millisecond
    13  	backoffUpper = 2 * time.Second
    14  )
    15  
    16  type (
    17  	backoffFuncRetErr[T any] func() (retry bool, ret T, err error)
    18  	backoffFuncErr           func() (retry bool, err error)
    19  )
    20  
    21  func mockBackoffDuration(d time.Duration) func() {
    22  	oldBegin, oldUpper := backoffBegin, backoffUpper
    23  	backoffBegin, backoffUpper = d, d
    24  	return func() {
    25  		backoffBegin, backoffUpper = oldBegin, oldUpper
    26  	}
    27  }
    28  
    29  func backoffRetErr[T any](ctx context.Context, begin, upper time.Duration, end <-chan time.Time, fn backoffFuncRetErr[T]) (ret T, err error) {
    30  	d := begin
    31  	for {
    32  		// check whether the context has been cancelled or has reached the deadline, return early
    33  		select {
    34  		case <-ctx.Done():
    35  			return ret, ctx.Err()
    36  		case <-end:
    37  			return ret, context.DeadlineExceeded
    38  		default:
    39  		}
    40  
    41  		// call the target function
    42  		retry, ret, err := fn()
    43  		if err != nil {
    44  			return ret, err
    45  		}
    46  		if !retry {
    47  			return ret, nil
    48  		}
    49  
    50  		// wait for a while before retrying, and also respect the context & deadline
    51  		select {
    52  		case <-ctx.Done():
    53  			return ret, ctx.Err()
    54  		case <-time.After(d):
    55  			d *= 2
    56  			if d > upper {
    57  				d = upper
    58  			}
    59  		case <-end:
    60  			return ret, context.DeadlineExceeded
    61  		}
    62  	}
    63  }
    64  
    65  func backoffErr(ctx context.Context, begin, upper time.Duration, end <-chan time.Time, fn backoffFuncErr) error {
    66  	_, err := backoffRetErr(ctx, begin, upper, end, func() (retry bool, ret any, err error) {
    67  		retry, err = fn()
    68  		return retry, nil, err
    69  	})
    70  	return err
    71  }