147 lines
6.6 KiB
Go
147 lines
6.6 KiB
Go
|
|
package common
|
||
|
|
|
||
|
|
import (
|
||
|
|
"fmt"
|
||
|
|
"net/http"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ErrorCode is unified definition of numerical error codes
|
||
|
|
type ErrorCode int32
|
||
|
|
|
||
|
|
// pre-defined error codes
|
||
|
|
const (
|
||
|
|
// System Wide 20000 - 20199
|
||
|
|
KinesisClientLibError ErrorCode = 20000
|
||
|
|
|
||
|
|
// KinesisClientLibrary Retryable Errors 20001 - 20099
|
||
|
|
KinesisClientLibRetryableError ErrorCode = 20001
|
||
|
|
|
||
|
|
KinesisClientLibIOError ErrorCode = 20002
|
||
|
|
BlockedOnParentShardError ErrorCode = 20003
|
||
|
|
KinesisClientLibDependencyError ErrorCode = 20004
|
||
|
|
ThrottlingError ErrorCode = 20005
|
||
|
|
|
||
|
|
// KinesisClientLibrary NonRetryable Errors 20100 - 20149
|
||
|
|
KinesisClientLibNonRetryableException ErrorCode = 20000
|
||
|
|
|
||
|
|
InvalidStateError ErrorCode = 20101
|
||
|
|
ShutdownError ErrorCode = 20102
|
||
|
|
|
||
|
|
// Kinesis Lease Errors 20150 - 20199
|
||
|
|
LeasingError ErrorCode = 20150
|
||
|
|
|
||
|
|
LeasingInvalidStateError ErrorCode = 20151
|
||
|
|
LeasingDependencyError ErrorCode = 20152
|
||
|
|
LeasingProvisionedThroughputError ErrorCode = 20153
|
||
|
|
|
||
|
|
// Error indicates passing illegal or inappropriate argument
|
||
|
|
IllegalArgumentError ErrorCode = 20198
|
||
|
|
|
||
|
|
// NotImplemented
|
||
|
|
KinesisClientLibNotImplemented ErrorCode = 20199
|
||
|
|
)
|
||
|
|
|
||
|
|
var errorMap = map[ErrorCode]ClientLibraryError{
|
||
|
|
KinesisClientLibError: {ErrorCode: KinesisClientLibError, Retryable: true, Status: http.StatusServiceUnavailable, Msg: "Top level error of Kinesis Client Library"},
|
||
|
|
|
||
|
|
// Retryable
|
||
|
|
KinesisClientLibRetryableError: {ErrorCode: KinesisClientLibRetryableError, Retryable: true, Status: http.StatusServiceUnavailable, Msg: "Retryable exceptions (e.g. transient errors). The request/operation is expected to succeed upon (back off and) retry."},
|
||
|
|
KinesisClientLibIOError: {ErrorCode: KinesisClientLibIOError, Retryable: true, Status: http.StatusServiceUnavailable, Msg: "Error in reading/writing information (e.g. shard information from Kinesis may not be current/complete)."},
|
||
|
|
BlockedOnParentShardError: {ErrorCode: BlockedOnParentShardError, Retryable: true, Status: http.StatusServiceUnavailable, Msg: "Cannot start processing data for a shard because the data from the parent shard has not been completely processed (yet)."},
|
||
|
|
KinesisClientLibDependencyError: {ErrorCode: KinesisClientLibDependencyError, Retryable: true, Status: http.StatusServiceUnavailable, Msg: "Cannot talk to its dependencies (e.g. fetching data from Kinesis, DynamoDB table reads/writes, emitting metrics to CloudWatch)."},
|
||
|
|
ThrottlingError: {ErrorCode: ThrottlingError, Retryable: true, Status: http.StatusTooManyRequests, Msg: "Requests are throttled by a service (e.g. DynamoDB when storing a checkpoint)."},
|
||
|
|
|
||
|
|
// Non-Retryable
|
||
|
|
KinesisClientLibNonRetryableException: {ErrorCode: KinesisClientLibNonRetryableException, Retryable: false, Status: http.StatusServiceUnavailable, Msg: "Non-retryable exceptions. Simply retrying the same request/operation is not expected to succeed."},
|
||
|
|
InvalidStateError: {ErrorCode: InvalidStateError, Retryable: false, Status: http.StatusServiceUnavailable, Msg: "Kinesis Library has issues with internal state (e.g. DynamoDB table is not found)."},
|
||
|
|
ShutdownError: {ErrorCode: ShutdownError, Retryable: false, Status: http.StatusServiceUnavailable, Msg: "The RecordProcessor instance has been shutdown (e.g. and attempts a checkpiont)."},
|
||
|
|
|
||
|
|
// Leasing
|
||
|
|
LeasingError: {ErrorCode: LeasingError, Retryable: true, Status: http.StatusServiceUnavailable, Msg: "Top-level error type for the leasing code."},
|
||
|
|
LeasingInvalidStateError: {ErrorCode: LeasingInvalidStateError, Retryable: true, Status: http.StatusServiceUnavailable, Msg: "Error in a lease operation has failed because DynamoDB is an invalid state"},
|
||
|
|
LeasingDependencyError: {ErrorCode: LeasingDependencyError, Retryable: true, Status: http.StatusServiceUnavailable, Msg: "Error in a lease operation has failed because a dependency of the leasing system has failed."},
|
||
|
|
LeasingProvisionedThroughputError: {ErrorCode: LeasingProvisionedThroughputError, Retryable: false, Status: http.StatusServiceUnavailable, Msg: "Error in a lease operation has failed due to lack of provisioned throughput for a DynamoDB table."},
|
||
|
|
|
||
|
|
// IllegalArgumentError
|
||
|
|
IllegalArgumentError: {ErrorCode: IllegalArgumentError, Retryable: false, Status: http.StatusBadRequest, Msg: "Error indicates that a method has been passed an illegal or inappropriate argument."},
|
||
|
|
|
||
|
|
// Not Implemented
|
||
|
|
KinesisClientLibNotImplemented: {ErrorCode: KinesisClientLibNotImplemented, Retryable: false, Status: http.StatusNotImplemented, Msg: "Not Implemented"},
|
||
|
|
}
|
||
|
|
|
||
|
|
// Message returns the message of the error code
|
||
|
|
func (c ErrorCode) Message() string {
|
||
|
|
return errorMap[c].Msg
|
||
|
|
}
|
||
|
|
|
||
|
|
// MakeErr makes an error with default message
|
||
|
|
func (c ErrorCode) MakeErr() *ClientLibraryError {
|
||
|
|
e := errorMap[c]
|
||
|
|
return &e
|
||
|
|
}
|
||
|
|
|
||
|
|
// MakeError makes an error with message and data
|
||
|
|
func (c ErrorCode) MakeError(detail string) error {
|
||
|
|
e := errorMap[c]
|
||
|
|
return e.WithDetail(detail)
|
||
|
|
}
|
||
|
|
|
||
|
|
// ClientLibraryError is unified error
|
||
|
|
type ClientLibraryError struct {
|
||
|
|
// ErrorCode is the numerical error code.
|
||
|
|
ErrorCode `json:"code"`
|
||
|
|
// Retryable is a bool flag to indicate the whether the error is retryable or not.
|
||
|
|
Retryable bool `json:"tryable"`
|
||
|
|
// Status is the HTTP status code.
|
||
|
|
Status int `json:"status"`
|
||
|
|
// Msg provides a terse description of the error. Its value is defined in errorMap.
|
||
|
|
Msg string `json:"msg"`
|
||
|
|
// Detail provides a detailed description of the error. Its value is set using WithDetail.
|
||
|
|
Detail string `json:"detail"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// Error implements error
|
||
|
|
func (e *ClientLibraryError) Error() string {
|
||
|
|
var prefix string
|
||
|
|
if e.Retryable {
|
||
|
|
prefix = "Retryable"
|
||
|
|
} else {
|
||
|
|
prefix = "NonRetryable"
|
||
|
|
}
|
||
|
|
msg := fmt.Sprintf("%v Error [%d]: %s", prefix, int32(e.ErrorCode), e.Msg)
|
||
|
|
if e.Detail != "" {
|
||
|
|
msg = fmt.Sprintf("%s, detail: %s", msg, e.Detail)
|
||
|
|
}
|
||
|
|
return msg
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithMsg overwrites the default error message
|
||
|
|
func (e *ClientLibraryError) WithMsg(format string, v ...interface{}) *ClientLibraryError {
|
||
|
|
e.Msg = fmt.Sprintf(format, v...)
|
||
|
|
return e
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithDetail adds a detailed message to error
|
||
|
|
func (e *ClientLibraryError) WithDetail(format string, v ...interface{}) *ClientLibraryError {
|
||
|
|
if len(e.Detail) == 0 {
|
||
|
|
e.Detail = fmt.Sprintf(format, v...)
|
||
|
|
} else {
|
||
|
|
e.Detail += ", " + fmt.Sprintf(format, v...)
|
||
|
|
}
|
||
|
|
return e
|
||
|
|
}
|
||
|
|
|
||
|
|
// WithCause adds CauseBy to error
|
||
|
|
func (e *ClientLibraryError) WithCause(err error) *ClientLibraryError {
|
||
|
|
if err != nil {
|
||
|
|
// Store error message in Detail, so the info can be preserved
|
||
|
|
// when CascadeError is marshaled to json.
|
||
|
|
if len(e.Detail) == 0 {
|
||
|
|
e.Detail = err.Error()
|
||
|
|
} else {
|
||
|
|
e.Detail += ", cause: " + err.Error()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return e
|
||
|
|
}
|