| 25 | } |
| 26 | |
| 27 | func (r *routineThatKeepsRunningOneInstance) Run(ctx context.Context) { |
| 28 | // The retry interval is used if we did not get the lock because some |
| 29 | // other caller got it. The exponential backoff is used if we encounter |
| 30 | // problems with obtaining the lock, like the Redis not being available. |
| 31 | // The retry interval is also used if the routine returned regularly, to |
| 32 | // avoid uncontrollably short restart cycles. If the routine panicked we |
| 33 | // use exponential backoff as well. |
| 34 | r.lockTTL = cfg.RedisLockTTL |
| 35 | r.retryInterval = r.lockTTL / 5 |
| 36 | r.backoff = combinedExponentialBackoff{ |
| 37 | "lock": &exponential.Backoff{Min: r.retryInterval, Max: 10 * time.Minute}, |
| 38 | "routine": &exponential.Backoff{Min: r.retryInterval, Max: 10 * time.Minute}, |
| 39 | } |
| 40 | |
| 41 | r.num = ctx.Value(ctxNumKey{}).(int64) |
| 42 | var tryAgainIn time.Duration // zero on first run |
| 43 | for { |
| 44 | select { |
| 45 | case <-ctx.Done(): |
| 46 | return |
| 47 | case <-time.After(tryAgainIn): |
| 48 | } |
| 49 | // Make sure to cancel the singleRunCtx so that the lock is released |
| 50 | // after the routine returned. |
| 51 | singleRunCtx, cancel := context.WithCancel(ctx) |
| 52 | tryAgainIn = r.singleRun(singleRunCtx) |
| 53 | cancel() |
| 54 | } |
| 55 | } |
| 56 | |
| 57 | // Performs a single run. That is, to try to obtain the lock and run the routine |
| 58 | // until it returns. Return the backoff duration after which another single run |