/
githubmirror
/
ydb-go-sdk
Обзор
Документация
Войти
/
githubmirror
/
ydb-go-sdk
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
internal/xerrors/operation.go
244 строки
6 KB
Aleksey Myasnikov
Changed the retry behavior for operation error `TIMEOUT` from `NonRetryable` to `ConditionallyRetryable` (#2153)
13 май 2026, 00:15
Не верифицирован
13 май 2026, 00:15
20ae4ec
Код
Авторство
О чём код?
package xerrors import ( "errors" "fmt" "slices" "strconv" "strings" "github.com/ydb-platform/ydb-go-genproto/protos/Ydb" "github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Issue" "github.com/ydb-platform/ydb-go-sdk/v3/internal/backoff" "github.com/ydb-platform/ydb-go-sdk/v3/internal/operation" "github.com/ydb-platform/ydb-go-sdk/v3/pkg/xstring" ) // operationError reports about operation fail. type operationError struct { code Ydb.StatusIds_StatusCode nodeID uint32 issues issues address string traceID string } func (e *operationError) isYdbError() {} func (e *operationError) Code() int32 { return int32(e.code) } func (e *operationError) Name() string { return "operation/" + e.code.String() } type issuesOption []*Ydb_Issue.IssueMessage func (issues issuesOption) applyToOperationError(oe *operationError) { oe.issues = []*Ydb_Issue.IssueMessage(issues) } // WithIssues is an option for construct operation error with issues list // WithIssues must use as `Operation(WithIssues(issues))` func WithIssues(issues []*Ydb_Issue.IssueMessage) issuesOption { return issues } type statusCodeOption Ydb.StatusIds_StatusCode func (code statusCodeOption) applyToOperationError(oe *operationError) { oe.code = Ydb.StatusIds_StatusCode(code) } // WithStatusCode is an option for construct operation error with reason code // WithStatusCode must use as `Operation(WithStatusCode(reason))` func WithStatusCode(code Ydb.StatusIds_StatusCode) statusCodeOption { return statusCodeOption(code) } func (address addressOption) applyToOperationError(oe *operationError) { oe.address = string(address) } func (nodeID nodeIDOption) applyToOperationError(oe *operationError) { oe.nodeID = uint32(nodeID) } type traceIDOption string func (traceID traceIDOption) applyToTransportError(te *transportError) { te.traceID = string(traceID) } func (traceID traceIDOption) applyToOperationError(oe *operationError) { oe.traceID = string(traceID) } // WithTraceID is an option for construct operation error with traceID func WithTraceID(traceID string) traceIDOption { return traceIDOption(traceID) } type operationOption struct { code Ydb.StatusIds_StatusCode issues issues } func (e *operationOption) applyToOperationError(oe *operationError) { oe.code = e.code oe.issues = e.issues } // FromOperation is an option for construct operation error from operation.Status // FromOperation must use as `Operation(FromOperation(operation.Status))` func FromOperation(operation operation.Status) *operationOption { return &operationOption{ code: operation.GetStatus(), issues: operation.GetIssues(), } } type oeOpt interface { applyToOperationError(oe *operationError) } func Operation(opts ...oeOpt) error { oe := &operationError{ code: Ydb.StatusIds_STATUS_CODE_UNSPECIFIED, } for _, opt := range opts { if opt != nil { opt.applyToOperationError(oe) } } return oe } func (e *operationError) Issues() []*Ydb_Issue.IssueMessage { return e.issues } func (e *operationError) Error() string { b := xstring.Buffer() defer b.Free() b.WriteString(e.Name()) fmt.Fprintf(b, " (code = %d", e.code) if len(e.address) > 0 { b.WriteString(", address = ") b.WriteString(e.address) } if e.nodeID > 0 { b.WriteString(", nodeID = ") b.WriteString(strconv.FormatUint(uint64(e.nodeID), 10)) } if len(e.issues) > 0 { b.WriteString(", issues = ") b.WriteString(e.issues.String()) } b.WriteString(")") return b.String() } // IsOperationError reports whether err is operationError with given errType codes. func IsOperationError(err error, codes ...Ydb.StatusIds_StatusCode) bool { var op *operationError if !errors.As(err, &op) { return false } if len(codes) == 0 { return true } return slices.Contains(codes, op.code) } func IsOperationErrorTransactionLocksInvalidated(err error) (isTLI bool) { return IsOperationError(err, Ydb.StatusIds_ABORTED) && iterateByIssues(err, func(message string, code Ydb.StatusIds_StatusCode, severity uint32) (stop bool) { return code == IssueCodeTransactionLocksInvalidated }) } func (e *operationError) Type() Type { switch e.code { case Ydb.StatusIds_UNAVAILABLE, Ydb.StatusIds_OVERLOADED, Ydb.StatusIds_BAD_SESSION, Ydb.StatusIds_SESSION_BUSY: return TypeRetryable case Ydb.StatusIds_UNDETERMINED, Ydb.StatusIds_SESSION_EXPIRED, Ydb.StatusIds_TIMEOUT: return TypeConditionallyRetryable case Ydb.StatusIds_UNAUTHORIZED: return TypeNonRetryable case Ydb.StatusIds_ABORTED: if e.hasIssueCodes(IssueCodeDatashardProgramSizeLimitExceeded) { return TypeNonRetryable } return TypeRetryable case Ydb.StatusIds_GENERIC_ERROR: if e.hasSchemaOperationsLimitExceeded() { return TypeRetryable } return TypeUndefined default: return TypeUndefined } } func (e *operationError) hasIssueCodes(codes ...Ydb.StatusIds_StatusCode) bool { return iterateByIssues(e, func(message string, code Ydb.StatusIds_StatusCode, severity uint32) (stop bool) { return slices.Contains(codes, code) }) } func (e *operationError) hasSchemaOperationsLimitExceeded() bool { return iterateByIssues(e, func(message string, code Ydb.StatusIds_StatusCode, severity uint32) (stop bool) { return strings.Contains(message, "Request exceeded a limit on the number of schema operations, try again later") }) } func (e *operationError) BackoffType() backoff.Type { switch e.code { case Ydb.StatusIds_OVERLOADED: return backoff.TypeSlow case Ydb.StatusIds_UNAVAILABLE, Ydb.StatusIds_CANCELLED, Ydb.StatusIds_SESSION_BUSY, Ydb.StatusIds_UNDETERMINED: return backoff.TypeFast case Ydb.StatusIds_ABORTED: if e.hasIssueCodes(IssueCodeDatashardProgramSizeLimitExceeded) { return backoff.TypeInstant } return backoff.TypeFast case Ydb.StatusIds_GENERIC_ERROR: if e.hasSchemaOperationsLimitExceeded() { return backoff.TypeSlow } return backoff.TypeInstant default: return backoff.TypeInstant } } func OperationError(err error) Error { var o *operationError if errors.As(err, &o) { return o } return nil }