vmware-go-kcl-v2/clientlibrary/common/errors.go
Aurélien Rainone 21980a54e3 Expose monitoring service (#49)
* Remove MonitoringConfiguration and export no-op service

MonitoringConfiguration is not needed anymore as the user directly
implements its monitoring service or use one the default constructors.

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>

* Provide a constructor for CloudWatchMonitoringService

Unexport all fields

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>

* Provide a constructor to PrometheusMonitoringService

Unexport fields

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>

* Remove all CloudWatch specific-stuff from config package

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>

* NewWorker accepts a metrics.MonitoringService

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>

* Fix tests

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>

* Add WithMonitoringService to config

Instead of having an additional parameter to NewWorker so that the
user can provide its own MonitoringService, WithMonitoringService
is added to the configuration. This is much cleaner and remains
in-line with the rest of the current API.

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>

* Fix tests after introduction of WithMonitoringService

Also, fix tests that should have been fixed in earlier commits.

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>

* Move Prometheus into its own package

Also rename it to prometheus.MonitoringService to not have to repeat
Prometheus twice when using.

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>

* Move CloudWatch metrics into its own package

Also rename it to cloudwatch.MonitoringService to not have to repeat
Cloudwatch twice when using.

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>

* Remove references to Cloudwatch in comments

Signed-off-by: Aurélien Rainone <aurelien.rainone@gmail.com>
2021-12-20 21:21:15 -06:00

165 lines
7.6 KiB
Go

/*
* Copyright (c) 2018 VMware, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is furnished to do
* so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
* NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package common
import (
"fmt"
"net/http"
)
// ErrorCode is unified definition of numerical error codes
type ErrorCode int32
// pre-defined error codes
const (
// System Wide 41000 - 42000
KinesisClientLibError ErrorCode = 41000
// KinesisClientLibrary Retryable Errors 41001 - 41100
KinesisClientLibRetryableError ErrorCode = 41001
KinesisClientLibIOError ErrorCode = 41002
BlockedOnParentShardError ErrorCode = 41003
KinesisClientLibDependencyError ErrorCode = 41004
ThrottlingError ErrorCode = 41005
// KinesisClientLibrary NonRetryable Errors 41100 - 41200
KinesisClientLibNonRetryableException ErrorCode = 41100
InvalidStateError ErrorCode = 41101
ShutdownError ErrorCode = 41102
// Kinesis Lease Errors 41200 - 41300
LeasingError ErrorCode = 41200
LeasingInvalidStateError ErrorCode = 41201
LeasingDependencyError ErrorCode = 41202
LeasingProvisionedThroughputError ErrorCode = 41203
// Misc Errors 41300 - 41400
// NotImplemented
KinesisClientLibNotImplemented ErrorCode = 41301
// Error indicates passing illegal or inappropriate argument
IllegalArgumentError ErrorCode = 41302
)
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)."},
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
}