Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
* Made error "Request exceeded a limit on the number of schema operations, try again later" retryable
* Fixed deadlock in `Endpoint.String()` method
* Added the `AvailabilityPeriod` to the Consumer type in topics

Expand Down
19 changes: 19 additions & 0 deletions internal/xerrors/operation.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"slices"
"strconv"
"strings"

"github.com/ydb-platform/ydb-go-genproto/protos/Ydb"
"github.com/ydb-platform/ydb-go-genproto/protos/Ydb_Issue"
Expand Down Expand Up @@ -187,6 +188,12 @@ func (e *operationError) Type() Type {
}

return TypeRetryable
case Ydb.StatusIds_GENERIC_ERROR:
if e.hasSchemaOperationsLimitExceeded() {
return TypeRetryable
}

return TypeUndefined
default:
return TypeUndefined
}
Expand All @@ -198,6 +205,12 @@ func (e *operationError) hasIssueCodes(codes ...Ydb.StatusIds_StatusCode) bool {
})
}

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:
Expand All @@ -214,6 +227,12 @@ func (e *operationError) BackoffType() backoff.Type {
}

return backoff.TypeFast
case Ydb.StatusIds_GENERIC_ERROR:
if e.hasSchemaOperationsLimitExceeded() {
return backoff.TypeSlow
}

return backoff.TypeNoBackoff
default:
return backoff.TypeNoBackoff
}
Expand Down
50 changes: 50 additions & 0 deletions internal/xerrors/operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import (
"github.com/stretchr/testify/require"
"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"
)

func TestIsOperationError(t *testing.T) {
Expand Down Expand Up @@ -182,3 +184,51 @@ func Test_operationError_Error(t *testing.T) {
})
}
}

func Test_operationError_SchemaOperationsLimitExceeded(t *testing.T) {
for _, tt := range []struct {
err error
expectedType Type
expectedBackoff backoff.Type
}{
{
err: Operation(
WithStatusCode(Ydb.StatusIds_GENERIC_ERROR),
WithIssues([]*Ydb_Issue.IssueMessage{{
Message: "Request exceeded a limit on the number of schema operations, try again later",
}}),
),
expectedType: TypeRetryable,
expectedBackoff: backoff.TypeSlow,
},
{
err: Operation(
WithStatusCode(Ydb.StatusIds_GENERIC_ERROR),
WithIssues([]*Ydb_Issue.IssueMessage{{
Message: "Some other error message",
}}),
),
expectedType: TypeUndefined,
expectedBackoff: backoff.TypeNoBackoff,
},
{
err: Operation(
WithStatusCode(Ydb.StatusIds_GENERIC_ERROR),
WithIssues([]*Ydb_Issue.IssueMessage{{
Issues: []*Ydb_Issue.IssueMessage{{
Message: "Request exceeded a limit on the number of schema operations, try again later",
}},
}}),
),
expectedType: TypeRetryable,
expectedBackoff: backoff.TypeSlow,
},
} {
t.Run("", func(t *testing.T) {
var op *operationError
require.ErrorAs(t, tt.err, &op)
require.Equal(t, tt.expectedType, op.Type())
require.Equal(t, tt.expectedBackoff, op.BackoffType())
})
}
}
Loading