|
|
package storage
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
api "github.com/qiniu/go-sdk/v7"
|
|
|
"github.com/qiniu/go-sdk/v7/internal/clientv2"
|
|
|
"github.com/qiniu/go-sdk/v7/internal/hostprovider"
|
|
|
"strings"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
// ParsePutTime 提供了将PutTime转换为 time.Time 的功能
|
|
|
func ParsePutTime(putTime int64) (t time.Time) {
|
|
|
t = time.Unix(0, putTime*100)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// IsContextExpired 检查分片上传的ctx是否过期,提前一天让它过期
|
|
|
// 因为我们认为如果断点继续上传的话,最长需要1天时间
|
|
|
func IsContextExpired(blkPut BlkputRet) bool {
|
|
|
if blkPut.Ctx == "" {
|
|
|
return false
|
|
|
}
|
|
|
target := time.Unix(blkPut.ExpiredAt, 0).AddDate(0, 0, -1)
|
|
|
now := time.Now()
|
|
|
return now.After(target)
|
|
|
}
|
|
|
|
|
|
func isUploadContextExpired(expiredAt int64) bool {
|
|
|
target := time.Unix(expiredAt, 0).Add(-2 * time.Hour)
|
|
|
return time.Now().After(target)
|
|
|
}
|
|
|
|
|
|
func shouldUploadAgain(err error) bool {
|
|
|
if err == nil {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
if isCancelErr(err) {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
switch t := err.(type) {
|
|
|
case *ErrorInfo:
|
|
|
// 4xx 不重试
|
|
|
return t.Code < 400 || t.Code > 499
|
|
|
case *api.QError:
|
|
|
return t.Code == ErrMaxUpRetry
|
|
|
default:
|
|
|
// 网络异常可重试
|
|
|
return shouldUploadRetryWithOtherHost(err)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func isContextExpiredError(err error) bool {
|
|
|
if err == nil {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
errInfo, ok := err.(*ErrorInfo)
|
|
|
if !ok {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
return errInfo.Code == 701 || (errInfo.Code == 612 && strings.Contains(errInfo.Error(), "no such uploadId"))
|
|
|
}
|
|
|
|
|
|
func shouldUploadRetryWithOtherHost(err error) bool {
|
|
|
return clientv2.IsErrorRetryable(err)
|
|
|
}
|
|
|
|
|
|
func doUploadAction(hostProvider hostprovider.HostProvider, retryMax int, freezeDuration time.Duration, action func(host string) error) error {
|
|
|
for {
|
|
|
host, err := hostProvider.Provider()
|
|
|
if err != nil {
|
|
|
return api.NewError(ErrMaxUpRetry, err.Error())
|
|
|
}
|
|
|
|
|
|
for i := 0; ; i++ {
|
|
|
err = action(host)
|
|
|
|
|
|
// 请求成功
|
|
|
if err == nil {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
// 不可重试错误
|
|
|
if !shouldUploadRetryWithOtherHost(err) {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
// 超过重试次数退出
|
|
|
if i >= retryMax {
|
|
|
break
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 单个 host 失败,冻结此 host,换其他 host
|
|
|
_ = hostProvider.Freeze(host, err, freezeDuration)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func isCancelErr(err error) bool {
|
|
|
if err == context.Canceled {
|
|
|
return true
|
|
|
}
|
|
|
|
|
|
if err == nil {
|
|
|
return false
|
|
|
}
|
|
|
|
|
|
return strings.Contains(err.Error(), "context canceled")
|
|
|
}
|
|
|
|
|
|
func removeRepeatStringItem(slc []string) []string {
|
|
|
var result []string
|
|
|
tempMap := map[string]uint8{}
|
|
|
for _, e := range slc {
|
|
|
l := len(tempMap)
|
|
|
tempMap[e] = 0
|
|
|
if len(tempMap) != l {
|
|
|
result = append(result, e)
|
|
|
}
|
|
|
}
|
|
|
return result
|
|
|
}
|
|
|
|
|
|
func removeHostScheme(host string) string {
|
|
|
host = strings.TrimLeft(host, "http://")
|
|
|
host = strings.TrimLeft(host, "https://")
|
|
|
return host
|
|
|
}
|