github.com/ecodeclub/eorm@v0.0.2-0.20231001112437-dae71da914d0/internal/datasource/transaction/delay_transaction_test.go (about)

     1  // Copyright 2021 ecodeclub
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package transaction_test
    16  
    17  import (
    18  	"context"
    19  	"database/sql"
    20  	"errors"
    21  	"regexp"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/ecodeclub/eorm/internal/datasource"
    26  	"github.com/ecodeclub/eorm/internal/datasource/cluster"
    27  	"github.com/ecodeclub/eorm/internal/datasource/shardingsource"
    28  	"github.com/ecodeclub/eorm/internal/errs"
    29  	"github.com/ecodeclub/eorm/internal/model"
    30  	"go.uber.org/multierr"
    31  
    32  	"github.com/DATA-DOG/go-sqlmock"
    33  	"github.com/ecodeclub/eorm"
    34  	"github.com/ecodeclub/eorm/internal/datasource/masterslave"
    35  	"github.com/ecodeclub/eorm/internal/datasource/transaction"
    36  	"github.com/ecodeclub/eorm/internal/test"
    37  	"github.com/stretchr/testify/assert"
    38  	"github.com/stretchr/testify/require"
    39  	"github.com/stretchr/testify/suite"
    40  )
    41  
    42  type TestDelayTxTestSuite struct {
    43  	ShardingTransactionSuite
    44  }
    45  
    46  func (s *TestDelayTxTestSuite) TestExecute_Commit_Or_Rollback() {
    47  	t := s.T()
    48  	testCases := []struct {
    49  		name         string
    50  		wantAffected int64
    51  		wantErr      error
    52  		values       []*test.OrderDetail
    53  		querySet     []*test.OrderDetail
    54  		txFunc       func() (*eorm.Tx, error)
    55  		mockOrder    func(mock1, mock2 sqlmock.Sqlmock)
    56  		afterFunc    func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail)
    57  	}{
    58  		{
    59  			name:    "begin err",
    60  			wantErr: errors.New("begin err"),
    61  			querySet: []*test.OrderDetail{
    62  				{OrderId: 8, ItemId: 6, UsingCol1: "Kobe", UsingCol2: "Bryant"},
    63  				{OrderId: 11, ItemId: 8, UsingCol1: "James", UsingCol2: "Harden"},
    64  				{OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"},
    65  				{OrderId: 253, ItemId: 8, UsingCol1: "Stephen", UsingCol2: "Curry"},
    66  				{OrderId: 181, ItemId: 11, UsingCol1: "Kawhi", UsingCol2: "Leonard"},
    67  			},
    68  			mockOrder: func(mock1, mock2 sqlmock.Sqlmock) {
    69  				mock2.ExpectBegin().WillReturnError(errors.New("begin err"))
    70  				mock1.ExpectBegin().WillReturnError(errors.New("begin err"))
    71  			},
    72  			txFunc: func() (*eorm.Tx, error) {
    73  				return s.shardingDB.BeginTx(transaction.UsingTxType(context.Background(), transaction.Delay), &sql.TxOptions{})
    74  			},
    75  			afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) {},
    76  		},
    77  		{
    78  			name:      "not find data source err",
    79  			wantErr:   errs.NewErrNotFoundTargetDataSource("0.db.cluster.company.com:3306"),
    80  			mockOrder: func(mock1, mock2 sqlmock.Sqlmock) {},
    81  			afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) {},
    82  			txFunc: func() (*eorm.Tx, error) {
    83  				ds := shardingsource.NewShardingDataSource(map[string]datasource.DataSource{
    84  					"1.db.cluster.company.com:3306": s.clusterDB,
    85  				})
    86  				r := model.NewMetaRegistry()
    87  				_, err := r.Register(&test.OrderDetail{},
    88  					model.WithTableShardingAlgorithm(s.algorithm))
    89  				require.NoError(t, err)
    90  				db, err := eorm.OpenDS("mysql", ds, eorm.DBWithMetaRegistry(r))
    91  				require.NoError(t, err)
    92  				return db.BeginTx(transaction.UsingTxType(context.Background(), transaction.Delay), &sql.TxOptions{})
    93  			},
    94  		},
    95  		{
    96  			name:      "not complete Finder err",
    97  			wantErr:   errs.NewErrNotCompleteFinder("0.db.cluster.company.com:3306"),
    98  			mockOrder: func(mock1, mock2 sqlmock.Sqlmock) {},
    99  			afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) {},
   100  			txFunc: func() (*eorm.Tx, error) {
   101  				ds := shardingsource.NewShardingDataSource(map[string]datasource.DataSource{
   102  					"0.db.cluster.company.com:3306": masterslave.NewMasterSlavesDB(s.mockMaster1DB, masterslave.MasterSlavesWithSlaves(
   103  						newSlaves(t, s.mockSlave1DB, s.mockSlave2DB, s.mockSlave3DB))),
   104  				})
   105  				r := model.NewMetaRegistry()
   106  				_, err := r.Register(&test.OrderDetail{},
   107  					model.WithTableShardingAlgorithm(s.algorithm))
   108  				require.NoError(t, err)
   109  				db, err := eorm.OpenDS("mysql", ds, eorm.DBWithMetaRegistry(r))
   110  				require.NoError(t, err)
   111  				return db.BeginTx(transaction.UsingTxType(context.Background(), transaction.Delay), &sql.TxOptions{})
   112  			},
   113  		},
   114  		{
   115  			name:    "not find target db err",
   116  			wantErr: errs.NewErrNotFoundTargetDB("order_detail_db_1"),
   117  			mockOrder: func(mock1, mock2 sqlmock.Sqlmock) {
   118  				mock1.ExpectBegin()
   119  			},
   120  			afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) {},
   121  			txFunc: func() (*eorm.Tx, error) {
   122  				clusterDB := cluster.NewClusterDB(map[string]*masterslave.MasterSlavesDB{
   123  					"order_detail_db_0": masterslave.NewMasterSlavesDB(s.mockMaster1DB, masterslave.MasterSlavesWithSlaves(
   124  						newSlaves(t, s.mockSlave1DB, s.mockSlave2DB, s.mockSlave3DB))),
   125  				})
   126  				ds := shardingsource.NewShardingDataSource(map[string]datasource.DataSource{
   127  					"0.db.cluster.company.com:3306": clusterDB,
   128  				})
   129  				r := model.NewMetaRegistry()
   130  				_, err := r.Register(&test.OrderDetail{},
   131  					model.WithTableShardingAlgorithm(s.algorithm))
   132  				require.NoError(t, err)
   133  				db, err := eorm.OpenDS("mysql", ds, eorm.DBWithMetaRegistry(r))
   134  				require.NoError(t, err)
   135  				return db.BeginTx(transaction.UsingTxType(context.Background(), transaction.Delay), &sql.TxOptions{})
   136  			},
   137  		},
   138  		{
   139  			name:         "select insert all commit err",
   140  			wantAffected: 2,
   141  			values: []*test.OrderDetail{
   142  				{OrderId: 288, ItemId: 101, UsingCol1: "Jimmy", UsingCol2: "Butler"},
   143  				{OrderId: 33, ItemId: 100, UsingCol1: "Nikolai", UsingCol2: "Jokic"},
   144  			},
   145  			querySet: []*test.OrderDetail{
   146  				{OrderId: 8, ItemId: 6, UsingCol1: "Kobe", UsingCol2: "Bryant"},
   147  				{OrderId: 11, ItemId: 8, UsingCol1: "James", UsingCol2: "Harden"},
   148  				{OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"},
   149  				{OrderId: 253, ItemId: 8, UsingCol1: "Stephen", UsingCol2: "Curry"},
   150  				{OrderId: 181, ItemId: 11, UsingCol1: "Kawhi", UsingCol2: "Leonard"},
   151  			},
   152  			mockOrder: func(mock1, mock2 sqlmock.Sqlmock) {
   153  				mock1.MatchExpectationsInOrder(false)
   154  				mock2.MatchExpectationsInOrder(false)
   155  				mock1.ExpectBegin()
   156  				mock2.ExpectBegin()
   157  
   158  				mock1.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   159  					WithArgs(123, 123, 123).
   160  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(234, 12, "Kevin", "Durant").AddRow(8, 6, "Kobe", "Bryant"))
   161  
   162  				mock2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   163  					WithArgs(123, 123, 123).
   164  					WillReturnRows(mock2.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(253, 8, "Stephen", "Curry").AddRow(181, 11, "Kawhi", "Leonard").AddRow(11, 8, "James", "Harden"))
   165  
   166  				mock1.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_0`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   167  					WithArgs(288, 101, "Jimmy", "Butler").WillReturnResult(sqlmock.NewResult(1, 1))
   168  				mock2.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   169  					WithArgs(33, 100, "Nikolai", "Jokic").WillReturnResult(sqlmock.NewResult(1, 1))
   170  
   171  				commitErr := errors.New("commit fail")
   172  				mock1.ExpectCommit().WillReturnError(commitErr)
   173  				mock2.ExpectCommit().WillReturnError(commitErr)
   174  			},
   175  			txFunc: func() (*eorm.Tx, error) {
   176  				return s.shardingDB.BeginTx(transaction.UsingTxType(context.Background(), transaction.Delay), &sql.TxOptions{})
   177  			},
   178  			afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) {
   179  				err := tx.Commit()
   180  				newErr := errors.New("commit fail")
   181  				errSlice := strings.Split(err.Error(), "; ")
   182  				wantErrSlice := []string{
   183  					newMockCommitErr("order_detail_db_0", newErr).Error(),
   184  					newMockCommitErr("order_detail_db_1", newErr).Error()}
   185  				assert.ElementsMatch(t, wantErrSlice, errSlice)
   186  
   187  				s.mockMaster.MatchExpectationsInOrder(false)
   188  				s.mockMaster2.MatchExpectationsInOrder(false)
   189  				rows := s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"})
   190  				s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")).
   191  					WithArgs(288, 33).WillReturnRows(rows)
   192  
   193  				s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")).
   194  					WithArgs(288, 33).WillReturnRows(rows)
   195  
   196  				queryVal := s.findTgt(t, values)
   197  				var wantOds []*test.OrderDetail
   198  				assert.ElementsMatch(t, wantOds, queryVal)
   199  			},
   200  		},
   201  		{
   202  			name:         "select insert part commit err",
   203  			wantAffected: 2,
   204  			values: []*test.OrderDetail{
   205  				{OrderId: 288, ItemId: 101, UsingCol1: "Jimmy", UsingCol2: "Butler"},
   206  				{OrderId: 33, ItemId: 100, UsingCol1: "Nikolai", UsingCol2: "Jokic"},
   207  			},
   208  			querySet: []*test.OrderDetail{
   209  				{OrderId: 8, ItemId: 6, UsingCol1: "Kobe", UsingCol2: "Bryant"},
   210  				{OrderId: 11, ItemId: 8, UsingCol1: "James", UsingCol2: "Harden"},
   211  				{OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"},
   212  				{OrderId: 253, ItemId: 8, UsingCol1: "Stephen", UsingCol2: "Curry"},
   213  				{OrderId: 181, ItemId: 11, UsingCol1: "Kawhi", UsingCol2: "Leonard"},
   214  			},
   215  			mockOrder: func(mock1, mock2 sqlmock.Sqlmock) {
   216  				mock1.MatchExpectationsInOrder(false)
   217  				mock2.MatchExpectationsInOrder(false)
   218  				mock1.ExpectBegin()
   219  				mock2.ExpectBegin()
   220  
   221  				mock1.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   222  					WithArgs(123, 123, 123).
   223  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(234, 12, "Kevin", "Durant").AddRow(8, 6, "Kobe", "Bryant"))
   224  
   225  				mock2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   226  					WithArgs(123, 123, 123).
   227  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(253, 8, "Stephen", "Curry").AddRow(181, 11, "Kawhi", "Leonard").AddRow(11, 8, "James", "Harden"))
   228  
   229  				mock1.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_0`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   230  					WithArgs(288, 101, "Jimmy", "Butler").WillReturnResult(sqlmock.NewResult(1, 1))
   231  				mock2.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   232  					WithArgs(33, 100, "Nikolai", "Jokic").WillReturnResult(sqlmock.NewResult(1, 1))
   233  
   234  				mock1.ExpectCommit()
   235  				mock2.ExpectCommit().WillReturnError(errors.New("commit fail"))
   236  			},
   237  			txFunc: func() (*eorm.Tx, error) {
   238  				return s.shardingDB.BeginTx(transaction.UsingTxType(context.Background(), transaction.Delay), &sql.TxOptions{})
   239  			},
   240  			afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) {
   241  				err := tx.Commit()
   242  				wantErr := multierr.Combine(newMockCommitErr("order_detail_db_1", errors.New("commit fail")))
   243  				assert.Equal(t, wantErr, err)
   244  
   245  				s.mockMaster.MatchExpectationsInOrder(false)
   246  				s.mockMaster2.MatchExpectationsInOrder(false)
   247  
   248  				rows := s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"})
   249  				s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")).
   250  					WithArgs(288, 33).WillReturnRows(rows)
   251  
   252  				s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")).
   253  					WithArgs(288, 33).WillReturnRows(rows)
   254  
   255  				queryVal := s.findTgt(t, values)
   256  				var wantVal []*test.OrderDetail
   257  				assert.ElementsMatch(t, wantVal, queryVal)
   258  			},
   259  		},
   260  		{
   261  			name:         "select insert all rollback err",
   262  			wantAffected: 2,
   263  			values: []*test.OrderDetail{
   264  				{OrderId: 288, ItemId: 101, UsingCol1: "Jimmy", UsingCol2: "Butler"},
   265  				{OrderId: 33, ItemId: 100, UsingCol1: "Nikolai", UsingCol2: "Jokic"},
   266  			},
   267  			querySet: []*test.OrderDetail{
   268  				{OrderId: 8, ItemId: 6, UsingCol1: "Kobe", UsingCol2: "Bryant"},
   269  				{OrderId: 11, ItemId: 8, UsingCol1: "James", UsingCol2: "Harden"},
   270  				{OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"},
   271  				{OrderId: 253, ItemId: 8, UsingCol1: "Stephen", UsingCol2: "Curry"},
   272  				{OrderId: 181, ItemId: 11, UsingCol1: "Kawhi", UsingCol2: "Leonard"},
   273  			},
   274  			mockOrder: func(mock1, mock2 sqlmock.Sqlmock) {
   275  				mock1.MatchExpectationsInOrder(false)
   276  				mock2.MatchExpectationsInOrder(false)
   277  				mock1.ExpectBegin()
   278  				mock2.ExpectBegin()
   279  
   280  				mock1.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   281  					WithArgs(123, 123, 123).
   282  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(234, 12, "Kevin", "Durant").AddRow(8, 6, "Kobe", "Bryant"))
   283  
   284  				mock2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   285  					WithArgs(123, 123, 123).
   286  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(253, 8, "Stephen", "Curry").AddRow(181, 11, "Kawhi", "Leonard").AddRow(11, 8, "James", "Harden"))
   287  
   288  				mock1.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_0`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   289  					WithArgs(288, 101, "Jimmy", "Butler").WillReturnResult(sqlmock.NewResult(1, 1))
   290  				mock2.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   291  					WithArgs(33, 100, "Nikolai", "Jokic").WillReturnResult(sqlmock.NewResult(1, 1))
   292  
   293  				rollbackErr := errors.New("rollback fail")
   294  				mock1.ExpectRollback().WillReturnError(rollbackErr)
   295  				mock2.ExpectRollback().WillReturnError(rollbackErr)
   296  			},
   297  			txFunc: func() (*eorm.Tx, error) {
   298  				return s.shardingDB.BeginTx(transaction.UsingTxType(context.Background(), transaction.Delay), &sql.TxOptions{})
   299  			},
   300  			afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) {
   301  				err := tx.Rollback()
   302  				newErr := errors.New("rollback fail")
   303  				errSlice := strings.Split(err.Error(), "; ")
   304  				wantErrSlice := []string{
   305  					newMockRollbackErr("order_detail_db_0", newErr).Error(),
   306  					newMockRollbackErr("order_detail_db_1", newErr).Error()}
   307  				assert.ElementsMatch(t, wantErrSlice, errSlice)
   308  
   309  				s.mockMaster.MatchExpectationsInOrder(false)
   310  				s.mockMaster2.MatchExpectationsInOrder(false)
   311  
   312  				rows := s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"})
   313  				s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")).
   314  					WithArgs(288, 33).WillReturnRows(rows)
   315  
   316  				s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")).
   317  					WithArgs(288, 33).WillReturnRows(rows)
   318  
   319  				queryVal := s.findTgt(t, values)
   320  				var wantOds []*test.OrderDetail
   321  				assert.ElementsMatch(t, wantOds, queryVal)
   322  			},
   323  		},
   324  		{
   325  			name:         "select insert part rollback err",
   326  			wantAffected: 2,
   327  			values: []*test.OrderDetail{
   328  				{OrderId: 288, ItemId: 101, UsingCol1: "Jimmy", UsingCol2: "Butler"},
   329  				{OrderId: 33, ItemId: 100, UsingCol1: "Nikolai", UsingCol2: "Jokic"},
   330  			},
   331  			querySet: []*test.OrderDetail{
   332  				{OrderId: 8, ItemId: 6, UsingCol1: "Kobe", UsingCol2: "Bryant"},
   333  				{OrderId: 11, ItemId: 8, UsingCol1: "James", UsingCol2: "Harden"},
   334  				{OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"},
   335  				{OrderId: 253, ItemId: 8, UsingCol1: "Stephen", UsingCol2: "Curry"},
   336  				{OrderId: 181, ItemId: 11, UsingCol1: "Kawhi", UsingCol2: "Leonard"},
   337  			},
   338  			mockOrder: func(mock1, mock2 sqlmock.Sqlmock) {
   339  				mock1.MatchExpectationsInOrder(false)
   340  				mock2.MatchExpectationsInOrder(false)
   341  				mock1.ExpectBegin()
   342  				mock2.ExpectBegin()
   343  
   344  				mock1.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   345  					WithArgs(123, 123, 123).
   346  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(234, 12, "Kevin", "Durant").AddRow(8, 6, "Kobe", "Bryant"))
   347  
   348  				mock2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   349  					WithArgs(123, 123, 123).
   350  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(253, 8, "Stephen", "Curry").AddRow(181, 11, "Kawhi", "Leonard").AddRow(11, 8, "James", "Harden"))
   351  
   352  				mock1.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_0`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   353  					WithArgs(288, 101, "Jimmy", "Butler").WillReturnResult(sqlmock.NewResult(1, 1))
   354  				mock2.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   355  					WithArgs(33, 100, "Nikolai", "Jokic").WillReturnResult(sqlmock.NewResult(1, 1))
   356  
   357  				mock1.ExpectRollback().WillReturnError(errors.New("rollback fail"))
   358  				mock2.ExpectRollback()
   359  			},
   360  			txFunc: func() (*eorm.Tx, error) {
   361  				return s.shardingDB.BeginTx(transaction.UsingTxType(context.Background(), transaction.Delay), &sql.TxOptions{})
   362  			},
   363  			afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) {
   364  				err := tx.Rollback()
   365  				wantErr := multierr.Combine(newMockRollbackErr("order_detail_db_0", errors.New("rollback fail")))
   366  				assert.Equal(t, wantErr, err)
   367  
   368  				s.mockMaster.MatchExpectationsInOrder(false)
   369  				s.mockMaster2.MatchExpectationsInOrder(false)
   370  
   371  				rows := s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"})
   372  				s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")).
   373  					WithArgs(288, 33).WillReturnRows(rows)
   374  
   375  				s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")).
   376  					WithArgs(288, 33).WillReturnRows(rows)
   377  
   378  				queryVal := s.findTgt(t, values)
   379  				var wantOds []*test.OrderDetail
   380  				assert.ElementsMatch(t, wantOds, queryVal)
   381  			},
   382  		},
   383  		{
   384  			name:         "select insert commit",
   385  			wantAffected: 2,
   386  			values: []*test.OrderDetail{
   387  				{OrderId: 288, ItemId: 101, UsingCol1: "Jimmy", UsingCol2: "Butler"},
   388  				{OrderId: 33, ItemId: 100, UsingCol1: "Nikolai", UsingCol2: "Jokic"},
   389  			},
   390  			querySet: []*test.OrderDetail{
   391  				{OrderId: 8, ItemId: 6, UsingCol1: "Kobe", UsingCol2: "Bryant"},
   392  				{OrderId: 11, ItemId: 8, UsingCol1: "James", UsingCol2: "Harden"},
   393  				{OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"},
   394  				{OrderId: 253, ItemId: 8, UsingCol1: "Stephen", UsingCol2: "Curry"},
   395  				{OrderId: 181, ItemId: 11, UsingCol1: "Kawhi", UsingCol2: "Leonard"},
   396  			},
   397  			mockOrder: func(mock1, mock2 sqlmock.Sqlmock) {
   398  				mock1.MatchExpectationsInOrder(false)
   399  				mock2.MatchExpectationsInOrder(false)
   400  				mock1.ExpectBegin()
   401  				mock2.ExpectBegin()
   402  
   403  				mock1.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   404  					WithArgs(123, 123, 123).
   405  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(234, 12, "Kevin", "Durant").AddRow(8, 6, "Kobe", "Bryant"))
   406  
   407  				mock2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   408  					WithArgs(123, 123, 123).
   409  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(253, 8, "Stephen", "Curry").AddRow(181, 11, "Kawhi", "Leonard").AddRow(11, 8, "James", "Harden"))
   410  
   411  				mock1.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_0`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   412  					WithArgs(288, 101, "Jimmy", "Butler").WillReturnResult(sqlmock.NewResult(1, 1))
   413  				mock2.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   414  					WithArgs(33, 100, "Nikolai", "Jokic").WillReturnResult(sqlmock.NewResult(1, 1))
   415  
   416  				mock1.ExpectCommit()
   417  				mock2.ExpectCommit()
   418  			},
   419  			txFunc: func() (*eorm.Tx, error) {
   420  				return s.shardingDB.BeginTx(transaction.UsingTxType(context.Background(), transaction.Delay), &sql.TxOptions{})
   421  			},
   422  			afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) {
   423  				err := tx.Commit()
   424  				require.NoError(t, err)
   425  
   426  				s.mockMaster.MatchExpectationsInOrder(false)
   427  				s.mockMaster2.MatchExpectationsInOrder(false)
   428  
   429  				s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")).
   430  					WithArgs(288, 33).WillReturnRows(s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(288, 101, "Jimmy", "Butler"))
   431  
   432  				s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")).
   433  					WithArgs(288, 33).WillReturnRows(s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(33, 100, "Nikolai", "Jokic"))
   434  
   435  				queryVal := s.findTgt(t, values)
   436  				assert.ElementsMatch(t, values, queryVal)
   437  			},
   438  		},
   439  		{
   440  			name:         "select insert rollback",
   441  			wantAffected: 2,
   442  			values: []*test.OrderDetail{
   443  				{OrderId: 199, ItemId: 100, UsingCol1: "Jason", UsingCol2: "Tatum"},
   444  				{OrderId: 299, ItemId: 101, UsingCol1: "Paul", UsingCol2: "George"},
   445  			},
   446  			querySet: []*test.OrderDetail{
   447  				{OrderId: 8, ItemId: 6, UsingCol1: "Kobe", UsingCol2: "Bryant"},
   448  				{OrderId: 11, ItemId: 8, UsingCol1: "James", UsingCol2: "Harden"},
   449  				{OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"},
   450  				{OrderId: 253, ItemId: 8, UsingCol1: "Stephen", UsingCol2: "Curry"},
   451  				{OrderId: 181, ItemId: 11, UsingCol1: "Kawhi", UsingCol2: "Leonard"},
   452  			},
   453  			mockOrder: func(mock1, mock2 sqlmock.Sqlmock) {
   454  				mock1.MatchExpectationsInOrder(false)
   455  				mock2.MatchExpectationsInOrder(false)
   456  				mock1.ExpectBegin()
   457  				mock2.ExpectBegin()
   458  
   459  				mock1.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   460  					WithArgs(123, 123, 123).
   461  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(234, 12, "Kevin", "Durant").AddRow(8, 6, "Kobe", "Bryant"))
   462  
   463  				mock2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE `order_id`!=?;SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_2` WHERE `order_id`!=?;")).
   464  					WithArgs(123, 123, 123).
   465  					WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(253, 8, "Stephen", "Curry").AddRow(181, 11, "Kawhi", "Leonard").AddRow(11, 8, "James", "Harden"))
   466  
   467  				mock2.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_1`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   468  					WithArgs(199, 100, "Jason", "Tatum").WillReturnResult(sqlmock.NewResult(1, 1))
   469  				mock2.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_2`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")).
   470  					WithArgs(299, 101, "Paul", "George").WillReturnResult(sqlmock.NewResult(1, 1))
   471  
   472  				mock1.ExpectRollback()
   473  				mock2.ExpectRollback()
   474  			},
   475  			txFunc: func() (*eorm.Tx, error) {
   476  				return s.shardingDB.BeginTx(transaction.UsingTxType(context.Background(), transaction.Delay), &sql.TxOptions{})
   477  			},
   478  			afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) {
   479  				err := tx.Rollback()
   480  				require.NoError(t, err)
   481  				s.mockMaster2.MatchExpectationsInOrder(false)
   482  
   483  				rows := s.mockMaster2.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"})
   484  				s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_2` WHERE (`order_id`=?) OR (`order_id`=?);SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE (`order_id`=?) OR (`order_id`=?);")).
   485  					WithArgs(199, 299, 199, 299).WillReturnRows(rows)
   486  			},
   487  		},
   488  	}
   489  	for _, tc := range testCases {
   490  		t.Run(tc.name, func(t *testing.T) {
   491  			tc.mockOrder(s.mockMaster, s.mockMaster2)
   492  			tx, err := tc.txFunc()
   493  			require.NoError(t, err)
   494  
   495  			querySet, err := eorm.NewShardingSelector[test.OrderDetail](tx).
   496  				Where(eorm.C("OrderId").NEQ(123)).
   497  				GetMulti(masterslave.UseMaster(context.Background()))
   498  			assert.Equal(t, tc.wantErr, err)
   499  			if err != nil {
   500  				return
   501  			}
   502  			assert.ElementsMatch(t, tc.querySet, querySet)
   503  
   504  			values := tc.values
   505  			res := eorm.NewShardingInsert[test.OrderDetail](tx).
   506  				Values(values).Exec(context.Background())
   507  			affected, err := res.RowsAffected()
   508  			require.NoError(t, err)
   509  			assert.Equal(t, tc.wantAffected, affected)
   510  			tc.afterFunc(t, tx, values)
   511  		})
   512  	}
   513  }
   514  
   515  func TestDelayTransactionSuite(t *testing.T) {
   516  	suite.Run(t, &TestDelayTxTestSuite{
   517  		ShardingTransactionSuite: newShardingTransactionSuite(),
   518  	})
   519  }