/** * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package realis // Using a pattern described by Dave Cheney to differentiate errors // https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully // Timeout errors are returned when a function is unable to continue executing due // to a time constraint or meeting a set number of retries. type timeout interface { Timedout() bool } // IsTimeout returns true if the error being passed as an argument implements the Timeout interface // and the Timedout function returns true. func IsTimeout(err error) bool { temp, ok := err.(timeout) return ok && temp.Timedout() } type timeoutErr struct { error timedout bool } func (r *timeoutErr) Timedout() bool { return r.timedout } func newTimedoutError(err error) *timeoutErr { return &timeoutErr{error: err, timedout: true} } // retryErr is a superset of timeout which includes extra context // with regards to our retry mechanism. This is done in order to make sure // that our retry mechanism works as expected through our tests and should // never be relied on or used directly. It is not made part of the public API // on purpose. type retryErr struct { error timedout bool retryCount int // How many times did the mechanism retry the command } // Retry error is a timeout type error with added context. func (r *retryErr) Timedout() bool { return r.timedout } func (r *retryErr) RetryCount() int { return r.retryCount } // ToRetryCount is a helper function for testing verification to avoid whitebox testing // as well as keeping retryErr as a private. // Should NOT be used under any other context. func ToRetryCount(err error) *retryErr { if retryErr, ok := err.(*retryErr); ok { return retryErr } return nil } func newRetryError(err error, retryCount int) *retryErr { return &retryErr{error: err, timedout: true, retryCount: retryCount} } // Temporary errors indicate that the action may or should be retried. type temporary interface { Temporary() bool } // IsTemporary indicates whether the error passed in as an argument implements the temporary interface // and if the Temporary function returns true. func IsTemporary(err error) bool { temp, ok := err.(temporary) return ok && temp.Temporary() } type temporaryErr struct { error temporary bool } func (t *temporaryErr) Temporary() bool { return t.temporary } // NewTemporaryError creates a new error which satisfies the Temporary interface. func NewTemporaryError(err error) *temporaryErr { return &temporaryErr{error: err, temporary: true} }