Skip to content

Commit 0fb66fa

Browse files
committed
classify changestream creation error
1 parent a4181da commit 0fb66fa

4 files changed

Lines changed: 53 additions & 1 deletion

File tree

flow/alerting/classifier.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ var (
6363
PostgresSpillFileMissingRe = regexp.MustCompile(`Unable to restore changes for xid \d+`)
6464
MySqlRdsBinlogFileNotFoundRe = regexp.MustCompile(`File '/rdsdbdata/log/binlog/mysql-bin-changelog.\d+' not found`)
6565
MongoPoolClearedErrorRe = regexp.MustCompile(`connection pool for .+ was cleared because another operation failed with`)
66+
MongoServerSideTimeoutRe = regexp.MustCompile(`calculated server-side timeout \(.*\) is less than or equal to \d+`)
6667
)
6768

6869
func (e ErrorAction) String() string {
@@ -221,6 +222,10 @@ var (
221222
ErrorNotifyChangeStreamHistoryLost = ErrorClass{
222223
Class: "NOTIFY_CHANGE_STREAM_HISTORY_LOST", action: NotifyUser,
223224
}
225+
// ErrorNotifyMongoServerSideTimeout fires when the Mongo driver rejects Watch because the computed maxTimeMS is <= 0
226+
ErrorNotifyMongoServerSideTimeout = ErrorClass{
227+
Class: "NOTIFY_MONGO_INVALID_TIMEOUT", action: NotifyUser,
228+
}
224229
ErrorNotifyPostgresLogicalMessageProcessing = ErrorClass{
225230
Class: "NOTIFY_POSTGRES_LOGICAL_MESSAGE_PROCESSING_ERROR", action: NotifyUser,
226231
}
@@ -1136,6 +1141,19 @@ func GetErrorClass(ctx context.Context, err error) (ErrorClass, ErrorInfo) {
11361141
}
11371142
}
11381143

1144+
if mongoCreateChangeStreamErr, ok := errors.AsType[*exceptions.MongoCreateChangeStreamError](err); ok {
1145+
if MongoServerSideTimeoutRe.MatchString(mongoCreateChangeStreamErr.Error()) {
1146+
return ErrorNotifyMongoServerSideTimeout, ErrorInfo{
1147+
Source: ErrorSourceMongoDB,
1148+
Code: "SERVER_SIDE_TIMEOUT",
1149+
}
1150+
}
1151+
return ErrorOther, ErrorInfo{
1152+
Source: ErrorSourceMongoDB,
1153+
Code: "UNKNOWN",
1154+
}
1155+
}
1156+
11391157
return ErrorOther, ErrorInfo{
11401158
Source: ErrorSourceOther,
11411159
Code: "UNKNOWN",

flow/alerting/classifier_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,20 @@ func TestMongoPoolErrorShouldBeRecoverable(t *testing.T) {
757757
}, errInfo, "Unexpected error info")
758758
}
759759

760+
func TestMongoCreateChangeStreamServerSideTimeoutShouldNotifyUser(t *testing.T) {
761+
e := errors.New("calculated server-side timeout (0 ms) is less than or equal to 0" +
762+
"(network round-trip time stats: moving avg: 20.14653ms, min: 20.054326ms, moving " +
763+
"stddev: 105.47µs): operation not sent to server, as Timeout would be exceeded: " +
764+
"context deadline exceeded")
765+
err := exceptions.NewMongoCreateChangeStreamError(e)
766+
errorClass, errInfo := GetErrorClass(t.Context(), err)
767+
assert.Equal(t, ErrorNotifyMongoServerSideTimeout, errorClass)
768+
assert.Equal(t, ErrorInfo{
769+
Source: ErrorSourceMongoDB,
770+
Code: "SERVER_SIDE_TIMEOUT",
771+
}, errInfo)
772+
}
773+
760774
func TestMongoCursorErrors(t *testing.T) {
761775
err := mongo.CommandError{
762776
Code: 6,

flow/connectors/mongo/cdc.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,11 @@ func createChangeStream(
476476
) (*mongo.ChangeStream, error) {
477477
watchCtx, cancel := context.WithTimeout(parent, 5*time.Minute)
478478
defer cancel()
479-
return client.Watch(watchCtx, pipeline, opts)
479+
cs, err := client.Watch(watchCtx, pipeline, opts)
480+
if err != nil {
481+
return nil, exceptions.NewMongoCreateChangeStreamError(err)
482+
}
483+
return cs, nil
480484
}
481485

482486
// This can happen if the resumeToken we are attempting to `ResumeAfter` refers to a table that has been

flow/shared/exceptions/mongo.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,19 @@ func NewInvalidIdValueError(table string) *MongoInvalidIdValueError {
1515
func (e *MongoInvalidIdValueError) Error() string {
1616
return fmt.Sprintf("_id field is missing or null in table %s; _id must be present and non-null", e.Table)
1717
}
18+
19+
type MongoCreateChangeStreamError struct {
20+
error
21+
}
22+
23+
func NewMongoCreateChangeStreamError(err error) *MongoCreateChangeStreamError {
24+
return &MongoCreateChangeStreamError{err}
25+
}
26+
27+
func (e *MongoCreateChangeStreamError) Error() string {
28+
return e.error.Error()
29+
}
30+
31+
func (e *MongoCreateChangeStreamError) Unwrap() error {
32+
return e.error
33+
}

0 commit comments

Comments
 (0)