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 }