2015-05-06 16:14:00 +00:00
|
|
|
package connector
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"math"
|
|
|
|
|
"net"
|
|
|
|
|
"net/url"
|
|
|
|
|
"regexp"
|
|
|
|
|
"time"
|
|
|
|
|
|
2015-08-17 00:52:10 +00:00
|
|
|
"github.com/aws/aws-sdk-go/aws/awserr"
|
2015-05-06 16:14:00 +00:00
|
|
|
"github.com/lib/pq"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type isRecoverableErrorFunc func(error) bool
|
|
|
|
|
|
2015-08-17 00:52:10 +00:00
|
|
|
var isRecoverableErrors = []isRecoverableErrorFunc{
|
|
|
|
|
kinesisIsRecoverableError,
|
|
|
|
|
netIsRecoverableError,
|
|
|
|
|
redshiftIsRecoverableError,
|
|
|
|
|
urlIsRecoverableError,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// isRecoverableError determines whether the error is recoverable
|
|
|
|
|
func isRecoverableError(err error) bool {
|
|
|
|
|
for _, errF := range isRecoverableErrors {
|
|
|
|
|
if errF(err) {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handle the aws exponential backoff
|
|
|
|
|
// wait up to 5 minutes based on the aws exponential backoff algorithm
|
|
|
|
|
// http://docs.aws.amazon.com/general/latest/gr/api-retries.html
|
|
|
|
|
func handleAwsWaitTimeExp(attempts int) {
|
|
|
|
|
if attempts > 0 {
|
|
|
|
|
waitTime := time.Duration(math.Min(100*math.Pow(2, float64(attempts)), 300000)) * time.Millisecond
|
|
|
|
|
time.Sleep(waitTime)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-06 16:14:00 +00:00
|
|
|
func kinesisIsRecoverableError(err error) bool {
|
|
|
|
|
recoverableErrorCodes := map[string]bool{
|
|
|
|
|
"InternalFailure": true,
|
2015-05-26 04:51:53 +00:00
|
|
|
"ProvisionedThroughputExceededException": true,
|
2015-08-17 00:52:10 +00:00
|
|
|
"RequestError": true,
|
2015-05-06 16:14:00 +00:00
|
|
|
"ServiceUnavailable": true,
|
2015-05-26 04:51:53 +00:00
|
|
|
"Throttling": true,
|
2015-05-06 16:14:00 +00:00
|
|
|
}
|
2015-08-17 00:52:10 +00:00
|
|
|
|
|
|
|
|
if err, ok := err.(awserr.Error); ok {
|
|
|
|
|
if ok && recoverableErrorCodes[err.Code()] == true {
|
|
|
|
|
return true
|
|
|
|
|
}
|
2015-05-06 16:14:00 +00:00
|
|
|
}
|
2015-08-17 00:52:10 +00:00
|
|
|
|
2015-05-26 04:51:53 +00:00
|
|
|
return false
|
2015-05-06 16:14:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func urlIsRecoverableError(err error) bool {
|
|
|
|
|
_, ok := err.(*url.Error)
|
|
|
|
|
if ok {
|
2015-05-26 04:51:53 +00:00
|
|
|
return true
|
2015-05-06 16:14:00 +00:00
|
|
|
}
|
2015-05-26 04:51:53 +00:00
|
|
|
return false
|
2015-05-06 16:14:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func netIsRecoverableError(err error) bool {
|
|
|
|
|
recoverableErrors := map[string]bool{
|
|
|
|
|
"connection reset by peer": true,
|
|
|
|
|
}
|
|
|
|
|
cErr, ok := err.(*net.OpError)
|
|
|
|
|
if ok && recoverableErrors[cErr.Err.Error()] == true {
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func redshiftIsRecoverableError(err error) bool {
|
2015-08-17 00:52:10 +00:00
|
|
|
redshiftRecoverableErrors := []*regexp.Regexp{
|
|
|
|
|
regexp.MustCompile("The specified S3 prefix '.*?' does not exist"),
|
|
|
|
|
}
|
|
|
|
|
|
2015-05-06 16:14:00 +00:00
|
|
|
if cErr, ok := err.(pq.Error); ok {
|
|
|
|
|
for _, re := range redshiftRecoverableErrors {
|
|
|
|
|
if re.MatchString(cErr.Message) {
|
2015-05-26 04:51:53 +00:00
|
|
|
return true
|
2015-05-06 16:14:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2015-05-26 04:51:53 +00:00
|
|
|
return false
|
2015-05-06 16:14:00 +00:00
|
|
|
}
|