github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/retry/strategy_test.go (about) 1 // Copyright 2019 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package retry 15 16 import ( 17 "database/sql/driver" 18 "testing" 19 "time" 20 21 "github.com/pingcap/tidb/pkg/util/dbutil" 22 tcontext "github.com/pingcap/tiflow/dm/pkg/context" 23 "github.com/pingcap/tiflow/dm/pkg/terror" 24 "github.com/stretchr/testify/require" 25 ) 26 27 func TestFiniteRetryStrategy(t *testing.T) { 28 t.Parallel() 29 strategy := &FiniteRetryStrategy{} 30 31 params := Params{ 32 RetryCount: 1, 33 BackoffStrategy: Stable, 34 FirstRetryDuration: time.Second, 35 IsRetryableFn: func(int, error) bool { 36 return false 37 }, 38 } 39 ctx := tcontext.Background() 40 41 operateFn := func(*tcontext.Context) (interface{}, error) { 42 return nil, terror.ErrDBDriverError.Generate("test database error") 43 } 44 45 _, opCount, err := strategy.Apply(ctx, params, operateFn) 46 require.Equal(t, 0, opCount) 47 require.True(t, terror.ErrDBDriverError.Equal(err)) 48 49 params.IsRetryableFn = func(int, error) bool { 50 return true 51 } 52 _, opCount, err = strategy.Apply(ctx, params, operateFn) 53 require.Equal(t, params.RetryCount, opCount) 54 require.True(t, terror.ErrDBDriverError.Equal(err)) 55 56 params.RetryCount = 3 57 58 _, opCount, err = strategy.Apply(ctx, params, operateFn) 59 require.Equal(t, params.RetryCount, opCount) 60 require.True(t, terror.ErrDBDriverError.Equal(err)) 61 62 // invalid connection will return ErrInvalidConn immediately no matter how many retries left 63 params.IsRetryableFn = func(int, error) bool { 64 return dbutil.IsRetryableError(err) 65 } 66 operateFn = func(*tcontext.Context) (interface{}, error) { 67 mysqlErr := driver.ErrBadConn 68 return nil, terror.ErrDBInvalidConn.Delegate(mysqlErr, "test invalid connection") 69 } 70 _, opCount, err = strategy.Apply(ctx, params, operateFn) 71 require.Equal(t, 0, opCount) 72 require.True(t, terror.ErrDBInvalidConn.Equal(err)) 73 74 params.IsRetryableFn = func(int, error) bool { 75 return IsConnectionError(err) 76 } 77 _, opCount, err = strategy.Apply(ctx, params, operateFn) 78 require.Equal(t, 3, opCount) 79 require.True(t, terror.ErrDBInvalidConn.Equal(err)) 80 81 retValue := "success" 82 operateFn = func(*tcontext.Context) (interface{}, error) { 83 return retValue, nil 84 } 85 ret, opCount, err := strategy.Apply(ctx, params, operateFn) 86 require.Equal(t, retValue, ret.(string)) 87 require.Equal(t, 0, opCount) 88 require.NoError(t, err) 89 }