현재 golang을 사용하면서 타임아웃의 경우 아래와 같이 에러 핸들링을 하고 있다.
// timeout.go
func IsTimeout(err error) bool {
if timeout, ok := err.(interface{ Timeout() bool }); ok || errors.As(err, &timeout) {
if timeout.Timeout() {
return true
}
}
return errors.Is(err, context.Canceled)
}
timeout 에러의 경우 err에 Timeout()
method 를 갖고 있는게 일반적이지만 aws-sdk-go
의 경우 Timeout()
method 를 사용하지 않아 이를 사용하는 라이브러리에서 발생하는 에러의 경우 위 메소드로 타임아웃 체크가 되지 않는다.
context canceled 이라고 나오지만 errors.Is(err, context.Canceled)
에도 잡히지 않았다
// Override the error with a context canceled error, if that was canceled.
ctx := r.Context()
select {
case <-ctx.Done():
r.Error = awserr.New(request.CanceledErrorCode,
"request context canceled", ctx.Err())
r.Retryable = aws.Bool(false)
default:
}
// ref: https://github.com/aws/aws-sdk-go/blob/02c1f723b528251a45068001bee8b56d904d7484/aws/corehandlers/handlers.go#L170-L172
aws-sdk-go
내부 코드를 보면 context cancel 이 발생하면 새로운 error new로 생성하면서 error value 값에 override한다. context나 Timeout()으로 핸들링하는 구조가 아니기 때문이다. (코드)
aws-sdk-go
에서 에러는 별도 인터페이스 를 사용해서 timeout 에러를 확인 후 핸들링이 필요하다.
// https://github.com/aws/aws-sdk-go/blob/02c1f723b528251a45068001bee8b56d904d7484/aws/request/request.go#L23-L43
const (
// ErrCodeResponseTimeout is the connection timeout error that is received
// during body reads.
ErrCodeResponseTimeout = "ResponseTimeout"
// CanceledErrorCode is the error code that will be returned by an
// API request that was canceled. Requests given a aws.Context may
// return this error when canceled.
CanceledErrorCode = "RequestCanceled"
)
지금 핸들링 하려는 에러는 여기 코드 에 있었다.
import "github.com/aws/aws-sdk-go/aws/request"
// aws-sdk-go v1 에서 err 에 Timeout() method가 없어서 사용. v2로 전환 시 Timeout() method 사용
func isErrorTimeout(err error) bool {
if v, ok := err.(interface{ Code() string }); ok {
code := v.Code()
if code == request.CanceledErrorCode || code == request.ErrCodeResponseTimeout {
return true
}
}
return false
}
이렇게 원하는 CanceledErrorCode
나 ErrCodeResponseTimeout
를 확인하는 isErrorTimeout
함수를 aws-sdk-go를 사용하는 인프라레이어에 생성했다.
아래와 같이 사용한다.
if err != nil {
if isErrorTimeout(err) {
return nil, EnsureTimeoutError(err)
}
이렇게 하면 어느 레이어에서나 본 글 가장 위 IsTimeout
메소드로 타임아웃을 체크할 수 있다.
EnsureTimeoutError
는 에 IsTimeout
에러로 핸들링 할 수 있게 IsTimeout()를 구현해둔 에러를 래핑하는 메소드다.
// timeout.go
type timeoutError struct {
err error
}
func (e *timeoutError) Error() string {
return e.err.Error()
}
func (e *timeoutError) Unwrap() error {
return e.err
}
func (e *timeoutError) Timeout() bool {
return true
}
func IsTimeout(err error) bool { ... }
// EnsureTimeoutError returns a timeout error if the given error is a timeout error.
func EnsureTimeoutError(err error) error {
if err == nil {
return nil
}
if IsTimeout(err) {
return err
}
return &timeoutError{err: err}
}
aws-sdk-go-v2
의 경우 코드를 보면 Timeout()
을 지원한다. 고로 위의 내용들이 필요없다.
직접 aws-sdk-go 사용하는 코드들의 경우 v2를 사용하면 되지만 사용하는 일부 라이브러리들이 여전히 v1을 사용하고 있거나 v2로 전환한지 얼마되지 않아 위 핸들링 과정이 필요했다.