mirror of
https://github.com/zeromicro/go-zero.git
synced 2026-05-14 18:30:02 +08:00
fix: should not trigger breaker on duplicate key with mongodb (#4238)
This commit is contained in:
@@ -2,6 +2,7 @@ package mon
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"errors"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/zeromicro/go-zero/core/breaker"
|
"github.com/zeromicro/go-zero/core/breaker"
|
||||||
@@ -16,6 +17,7 @@ const (
|
|||||||
defaultSlowThreshold = time.Millisecond * 500
|
defaultSlowThreshold = time.Millisecond * 500
|
||||||
// spanName is the span name of the mongo calls.
|
// spanName is the span name of the mongo calls.
|
||||||
spanName = "mongo"
|
spanName = "mongo"
|
||||||
|
duplicateKeyCode = 11000
|
||||||
|
|
||||||
// mongodb method names
|
// mongodb method names
|
||||||
aggregate = "Aggregate"
|
aggregate = "Aggregate"
|
||||||
@@ -527,10 +529,20 @@ func (p keepablePromise) keep(err error) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func acceptable(err error) bool {
|
func acceptable(err error) bool {
|
||||||
return err == nil || errorx.In(err, mongo.ErrNoDocuments, mongo.ErrNilValue,
|
return err == nil || isDupKeyError(err) ||
|
||||||
|
errorx.In(err, mongo.ErrNoDocuments, mongo.ErrNilValue,
|
||||||
mongo.ErrNilDocument, mongo.ErrNilCursor, mongo.ErrEmptySlice,
|
mongo.ErrNilDocument, mongo.ErrNilCursor, mongo.ErrEmptySlice,
|
||||||
// session errors
|
// session errors
|
||||||
session.ErrSessionEnded, session.ErrNoTransactStarted, session.ErrTransactInProgress,
|
session.ErrSessionEnded, session.ErrNoTransactStarted, session.ErrTransactInProgress,
|
||||||
session.ErrAbortAfterCommit, session.ErrAbortTwice, session.ErrCommitAfterAbort,
|
session.ErrAbortAfterCommit, session.ErrAbortTwice, session.ErrCommitAfterAbort,
|
||||||
session.ErrUnackWCUnsupported, session.ErrSnapshotTransaction)
|
session.ErrUnackWCUnsupported, session.ErrSnapshotTransaction)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isDupKeyError(err error) bool {
|
||||||
|
var e mongo.WriteException
|
||||||
|
if !errors.As(err, &e) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.HasErrorCode(duplicateKeyCode)
|
||||||
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"go.mongodb.org/mongo-driver/mongo"
|
"go.mongodb.org/mongo-driver/mongo"
|
||||||
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
|
"go.mongodb.org/mongo-driver/mongo/integration/mtest"
|
||||||
mopt "go.mongodb.org/mongo-driver/mongo/options"
|
mopt "go.mongodb.org/mongo-driver/mongo/options"
|
||||||
|
"go.mongodb.org/mongo-driver/x/mongo/driver/session"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errDummy = errors.New("dummy")
|
var errDummy = errors.New("dummy")
|
||||||
@@ -572,6 +573,56 @@ func TestDecoratedCollection_LogDuration(t *testing.T) {
|
|||||||
assert.Contains(t, buf.String(), "slowcall")
|
assert.Contains(t, buf.String(), "slowcall")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestAcceptable(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"NilError", nil, true},
|
||||||
|
{"NoDocuments", mongo.ErrNoDocuments, true},
|
||||||
|
{"NilValue", mongo.ErrNilValue, true},
|
||||||
|
{"NilDocument", mongo.ErrNilDocument, true},
|
||||||
|
{"NilCursor", mongo.ErrNilCursor, true},
|
||||||
|
{"EmptySlice", mongo.ErrEmptySlice, true},
|
||||||
|
{"SessionEnded", session.ErrSessionEnded, true},
|
||||||
|
{"NoTransactStarted", session.ErrNoTransactStarted, true},
|
||||||
|
{"TransactInProgress", session.ErrTransactInProgress, true},
|
||||||
|
{"AbortAfterCommit", session.ErrAbortAfterCommit, true},
|
||||||
|
{"AbortTwice", session.ErrAbortTwice, true},
|
||||||
|
{"CommitAfterAbort", session.ErrCommitAfterAbort, true},
|
||||||
|
{"UnackWCUnsupported", session.ErrUnackWCUnsupported, true},
|
||||||
|
{"SnapshotTransaction", session.ErrSnapshotTransaction, true},
|
||||||
|
{"DuplicateKeyError", mongo.WriteException{WriteErrors: []mongo.WriteError{{Code: duplicateKeyCode}}}, true},
|
||||||
|
{"OtherError", errors.New("other error"), false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, acceptable(tt.err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIsDupKeyError(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
err error
|
||||||
|
want bool
|
||||||
|
}{
|
||||||
|
{"NilError", nil, false},
|
||||||
|
{"NonDupKeyError", errors.New("some other error"), false},
|
||||||
|
{"DupKeyError", mongo.WriteException{WriteErrors: []mongo.WriteError{{Code: duplicateKeyCode}}}, true},
|
||||||
|
{"OtherMongoError", mongo.WriteException{WriteErrors: []mongo.WriteError{{Code: 12345}}}, false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assert.Equal(t, tt.want, isDupKeyError(tt.err))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type mockPromise struct {
|
type mockPromise struct {
|
||||||
accepted bool
|
accepted bool
|
||||||
reason string
|
reason string
|
||||||
|
|||||||
Reference in New Issue
Block a user