github.com/pachyderm/pachyderm@v1.13.4/src/server/pkg/storage/renew/renewer.go (about)

     1  package renew
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"time"
     7  )
     8  
     9  // Func is a function called to renew something for ttl time.
    10  type Func func(ctx context.Context, ttl time.Duration) error
    11  
    12  // Renewer manages renewing something in the background
    13  type Renewer struct {
    14  	renewFunc Func
    15  	ttl       time.Duration
    16  
    17  	ctx    context.Context
    18  	cancel context.CancelFunc
    19  	done   chan struct{}
    20  	err    error
    21  }
    22  
    23  // NewRenewer returns a renewer that will call renewFunc with ttl every period;
    24  // where period will be some fraction ttl.
    25  // If ctx is cancelled the renewer will be closed.
    26  func NewRenewer(ctx context.Context, ttl time.Duration, renewFunc Func) *Renewer {
    27  	ctx, cancel := context.WithCancel(ctx)
    28  	r := &Renewer{
    29  		renewFunc: renewFunc,
    30  		ttl:       ttl,
    31  
    32  		ctx:    ctx,
    33  		cancel: cancel,
    34  		done:   make(chan struct{}),
    35  	}
    36  	go func() {
    37  		r.err = r.renewLoop(ctx)
    38  		r.cancel()
    39  		close(r.done)
    40  	}()
    41  	return r
    42  }
    43  
    44  // Context returns a context which will be cancelled when the renewer is closed
    45  func (r *Renewer) Context() context.Context {
    46  	return r.ctx
    47  }
    48  
    49  // Close closes the renewer, stopping the background renewal. Close is idempotent.
    50  func (r *Renewer) Close() error {
    51  	r.cancel()
    52  	<-r.done
    53  	return r.err
    54  }
    55  
    56  func (r *Renewer) renewLoop(ctx context.Context) (retErr error) {
    57  	defer func() {
    58  		if errors.Is(ctx.Err(), context.Canceled) {
    59  			retErr = nil
    60  		}
    61  	}()
    62  	ticker := time.NewTicker(r.ttl / 3)
    63  	defer ticker.Stop()
    64  	for {
    65  		if err := func() error {
    66  			ctx, cf := context.WithTimeout(ctx, r.ttl/3)
    67  			defer cf()
    68  			return r.renewFunc(ctx, r.ttl)
    69  		}(); err != nil {
    70  			return err
    71  		}
    72  		select {
    73  		case <-ticker.C:
    74  		case <-ctx.Done():
    75  			return ctx.Err()
    76  		}
    77  	}
    78  }