retry.go
func (ch *Channel) RunWithRetry(runCtx context.Context, f RetriableFunc) error {
	var err error

	opts := getRetryOptions(runCtx)
	rs := ch.getRequestState(opts)
	defer requestStatePool.Put(rs)

	for i := 0; i < opts.MaxAttempts; i++ {
		rs.Attempt++

		if opts.TimeoutPerAttempt == 0 {
			err = f(runCtx, rs)
		} else {
			attemptCtx, cancel := context.WithTimeout(runCtx, opts.TimeoutPerAttempt)
			err = f(attemptCtx, rs)
			cancel()
		}

		if err == nil {
			return nil
		}
		if !opts.RetryOn.CanRetry(err) {
			if ch.log.Enabled(LogLevelInfo) {
				ch.log.WithFields(ErrField(err)).Info("Failed after non-retriable error.")
			}
			return err
		}

		ch.log.WithFields(
			ErrField(err),
			LogField{"attempt", rs.Attempt},
			LogField{"maxAttempts", opts.MaxAttempts},
		).Info("Retrying request after retryable error.")
	}

	// Too many retries, return the last error
	return err
}
func (ch *Channel) RunWithRetry(runCtx context.Context, f RetriableFunc) error {
	var err error

	opts := getRetryOptions(runCtx)
	rs := ch.getRequestState(opts)
	defer requestStatePool.Put(rs)

	for i := 0; i < opts.MaxAttempts; i++ {
		rs.Attempt++

		if opts.TimeoutPerAttempt == 0 {
			err = f(runCtx, rs)
		} else {
			attemptCtx, cancel := context.WithTimeout(runCtx, opts.TimeoutPerAttempt)
			err = f(attemptCtx, rs)
			cancel()
		}

		if err == nil {
			return nil
		}
		if !opts.RetryOn.CanRetry(err) {
			if ch.log.Enabled(LogLevelInfo) {
				ch.log.WithFields(ErrField(err)).Info("Failed after non-retriable error.")
			}
			return err
		}

		ch.log.WithFields(
			ErrField(err),
			LogField{"attempt", rs.Attempt},
			LogField{"maxAttempts", opts.MaxAttempts},
		).Info("Retrying request after retryable error.")
	}

	// Too many retries, return the last error
	return err
}
// RunWithRetry will take a function that makes the TChannel call, and will
// rerun it as specifed in the RetryOptions in the Context.