github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdcv2/metadata/sql/client_orm.go (about)

     1  // Copyright 2023 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 sql
    15  
    16  import (
    17  	"context"
    18  	"time"
    19  
    20  	"github.com/pingcap/log"
    21  	"github.com/pingcap/tiflow/cdc/model"
    22  	"github.com/pingcap/tiflow/cdcv2/metadata"
    23  	"github.com/pingcap/tiflow/pkg/errors"
    24  	"go.uber.org/zap"
    25  	"gorm.io/gorm"
    26  	"gorm.io/gorm/clause"
    27  )
    28  
    29  var (
    30  	_ checker[*gorm.DB] = &ormClient{}
    31  
    32  	_ upstreamClient[*gorm.DB]        = &ormClient{}
    33  	_ changefeedInfoClient[*gorm.DB]  = &ormClient{}
    34  	_ changefeedStateClient[*gorm.DB] = &ormClient{}
    35  	_ scheduleClient[*gorm.DB]        = &ormClient{}
    36  	_ progressClient[*gorm.DB]        = &ormClient{}
    37  )
    38  
    39  type ormClient struct {
    40  	selfID model.CaptureID
    41  	db     *gorm.DB
    42  }
    43  
    44  // NewORMClient creates a new ORM client.
    45  func NewORMClient(selfID model.CaptureID, db *gorm.DB) *ormClient {
    46  	return &ormClient{
    47  		selfID: selfID,
    48  		db:     db,
    49  	}
    50  }
    51  
    52  // Txn executes the given transaction action in a transaction.
    53  func (c *ormClient) Txn(ctx context.Context, fn ormTxnAction) error {
    54  	return c.db.WithContext(ctx).Transaction(fn)
    55  }
    56  
    57  // TxnWithOwnerLock executes the given transaction action in a transaction with owner lock.
    58  func (c *ormClient) TxnWithOwnerLock(ctx context.Context, uuid metadata.ChangefeedUUID, fn ormTxnAction) error {
    59  	return c.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
    60  		sc := &ScheduleDO{}
    61  		ret := tx.Where("changefeed_uuid = ? and owner = ? and owner_state != ?", uuid, c.selfID, metadata.SchedRemoved).
    62  			Clauses(clause.Locking{
    63  				Strength: "SHARE",
    64  				Table:    clause.Table{Name: clause.CurrentTable},
    65  			}).Limit(1).Find(&sc)
    66  		if err := handleSingleOpErr(ret, 1, "TxnWithOwnerLock"); err != nil {
    67  			return errors.Trace(err)
    68  		}
    69  		return fn(tx)
    70  	})
    71  }
    72  
    73  // ================================ Upstream Client =================================
    74  
    75  // createUpstream implements the upstreamClient interface.
    76  func (c *ormClient) createUpstream(tx *gorm.DB, up *UpstreamDO) error {
    77  	ret := tx.Create(up)
    78  	if err := handleSingleOpErr(ret, 1, "CreateUpstream"); err != nil {
    79  		return errors.Trace(err)
    80  	}
    81  	return nil
    82  }
    83  
    84  // deleteUpstream implements the upstreamClient interface.
    85  func (c *ormClient) deleteUpstream(tx *gorm.DB, up *UpstreamDO) error {
    86  	ret := tx.Delete(up)
    87  	if err := handleSingleOpErr(ret, 1, "DeleteUpstream"); err != nil {
    88  		return errors.Trace(err)
    89  	}
    90  	return nil
    91  }
    92  
    93  // updateUpstream implements the upstreamClient interface.
    94  func (c *ormClient) updateUpstream(tx *gorm.DB, up *UpstreamDO) error {
    95  	ret := tx.Where("id = ? and version = ?", up.ID, up.Version).
    96  		Updates(UpstreamDO{
    97  			Endpoints: up.Endpoints,
    98  			Config:    up.Config,
    99  			Version:   up.Version + 1,
   100  		})
   101  	if err := handleSingleOpErr(ret, 1, "UpdateUpstream"); err != nil {
   102  		return errors.Trace(err)
   103  	}
   104  	return nil
   105  }
   106  
   107  // queryUpstreams implements the upstreamClient interface.
   108  //
   109  //nolint:unused
   110  func (c *ormClient) queryUpstreams(tx *gorm.DB) ([]*UpstreamDO, error) {
   111  	ups := []*UpstreamDO{}
   112  	ret := tx.Find(&ups)
   113  	if err := handleSingleOpErr(ret, -1, "QueryUpstreams"); err != nil {
   114  		return nil, errors.Trace(err)
   115  	}
   116  	return ups, nil
   117  }
   118  
   119  // queryUpstreamsByUpdateAt implements the upstreamClient interface.
   120  //
   121  //nolint:unused
   122  func (c *ormClient) queryUpstreamsByUpdateAt(tx *gorm.DB, lastUpdateAt time.Time) ([]*UpstreamDO, error) {
   123  	ups := []*UpstreamDO{}
   124  	ret := tx.Where("update_at > ?", lastUpdateAt).Find(&ups)
   125  	if err := handleSingleOpErr(ret, -1, "QueryUpstreamsByUpdateAt"); err != nil {
   126  		return nil, errors.Trace(err)
   127  	}
   128  	return ups, nil
   129  }
   130  
   131  // queryUpstreamByID implements the upstreamClient interface.
   132  //
   133  //nolint:unused
   134  func (c *ormClient) queryUpstreamByID(tx *gorm.DB, id uint64) (*UpstreamDO, error) {
   135  	up := &UpstreamDO{}
   136  	ret := tx.Where("id = ?", id).Limit(1).Find(up)
   137  	if err := handleSingleOpErr(ret, 1, "QueryUpstreamsByUpdateAt"); err != nil {
   138  		return nil, errors.Trace(err)
   139  	}
   140  	return up, nil
   141  }
   142  
   143  // ================================ ChangefeedInfo Client =================================
   144  
   145  // createChangefeedInfo implements the changefeedInfoClient interface.
   146  func (c *ormClient) createChangefeedInfo(tx *gorm.DB, info *ChangefeedInfoDO) error {
   147  	ret := tx.Create(info)
   148  	if err := handleSingleOpErr(ret, 1, "CreateChangefeedInfo"); err != nil {
   149  		return errors.Trace(err)
   150  	}
   151  	return nil
   152  }
   153  
   154  // deleteChangefeedInfo implements the changefeedInfoClient interface.
   155  func (c *ormClient) deleteChangefeedInfo(tx *gorm.DB, info *ChangefeedInfoDO) error {
   156  	ret := tx.Delete(info)
   157  	if err := handleSingleOpErr(ret, 1, "DeleteChangefeedInfo"); err != nil {
   158  		return errors.Trace(err)
   159  	}
   160  	return nil
   161  }
   162  
   163  // markChangefeedRemoved implements the changefeedInfoClient interface.
   164  //
   165  //nolint:unused
   166  func (c *ormClient) markChangefeedRemoved(tx *gorm.DB, info *ChangefeedInfoDO) error {
   167  	// TODO: maybe we should usethe mysql function `now(6)` to get the current time.
   168  	removeTime := time.Now()
   169  	ret := tx.Where("uuid = ? and version = ?", info.UUID, info.Version).
   170  		Updates(ChangefeedInfoDO{
   171  			RemovedAt: &removeTime,
   172  			Version:   info.Version + 1,
   173  		})
   174  	if err := handleSingleOpErr(ret, 1, "markChangefeedRemoved"); err != nil {
   175  		return errors.Trace(err)
   176  	}
   177  	return nil
   178  }
   179  
   180  // updateChangefeedInfo implements the changefeedInfoClient interface.
   181  func (c *ormClient) updateChangefeedInfo(tx *gorm.DB, info *ChangefeedInfoDO) error {
   182  	ret := tx.Where("uuid = ? and version = ?", info.UUID, info.Version).
   183  		Updates(&ChangefeedInfoDO{
   184  			ChangefeedInfo: metadata.ChangefeedInfo{
   185  				SinkURI:  info.SinkURI,
   186  				StartTs:  info.StartTs,
   187  				TargetTs: info.TargetTs,
   188  				Config:   info.Config,
   189  			},
   190  			Version: info.Version + 1,
   191  		})
   192  	if err := handleSingleOpErr(ret, 1, "UpdateChangefeedInfo"); err != nil {
   193  		return errors.Trace(err)
   194  	}
   195  	return nil
   196  }
   197  
   198  // queryChangefeedInfos implements the changefeedInfoClient interface.
   199  //
   200  //nolint:unused
   201  func (c *ormClient) queryChangefeedInfos(tx *gorm.DB) ([]*ChangefeedInfoDO, error) {
   202  	infos := []*ChangefeedInfoDO{}
   203  	ret := tx.Find(&infos)
   204  	if err := handleSingleOpErr(ret, -1, "QueryChangefeedInfos"); err != nil {
   205  		return nil, errors.Trace(err)
   206  	}
   207  	return infos, nil
   208  }
   209  
   210  // queryChangefeedInfosByUpdateAt implements the changefeedInfoClient interface.
   211  // TODO(CharlesCheung): query data before lastUpdateAt to avoid data loss.
   212  //
   213  //nolint:unused
   214  func (c *ormClient) queryChangefeedInfosByUpdateAt(tx *gorm.DB, lastUpdateAt time.Time) ([]*ChangefeedInfoDO, error) {
   215  	infos := []*ChangefeedInfoDO{}
   216  	ret := tx.Where("update_at > ?", lastUpdateAt).Find(&infos)
   217  	if err := handleSingleOpErr(ret, -1, "QueryChangefeedInfosByUpdateAt"); err != nil {
   218  		return nil, errors.Trace(err)
   219  	}
   220  	return infos, nil
   221  }
   222  
   223  // queryChangefeedInfosByUUIDs implements the changefeedInfoClient interface.
   224  // nolint:unused
   225  func (c *ormClient) queryChangefeedInfosByUUIDs(tx *gorm.DB, uuids ...uint64) ([]*ChangefeedInfoDO, error) {
   226  	infos := []*ChangefeedInfoDO{}
   227  	ret := tx.Where("uuid IN (?)", uuids).Find(&infos)
   228  	if err := handleSingleOpErr(ret, int64(len(uuids)), "QueryChangefeedInfosByUUIDs"); err != nil {
   229  		// TODO: optimize the behavior when some uuids are not found.
   230  		return infos, errors.Trace(err)
   231  	}
   232  	return infos, nil
   233  }
   234  
   235  // queryChangefeedInfoByUUID implements the changefeedInfoClient interface.
   236  //
   237  //nolint:unused
   238  func (c *ormClient) queryChangefeedInfoByUUID(tx *gorm.DB, uuid uint64) (*ChangefeedInfoDO, error) {
   239  	info := &ChangefeedInfoDO{}
   240  	ret := tx.Where("uuid = ?", uuid).Limit(1).Find(info)
   241  
   242  	// TODO(CharlesCheung): handle record not found error.
   243  	if err := handleSingleOpErr(ret, 1, "QueryChangefeedInfoByUUID"); err != nil {
   244  		return nil, errors.Trace(err)
   245  	}
   246  	return info, nil
   247  }
   248  
   249  // ================================ ChangefeedState Client =================================
   250  
   251  // createChangefeedState implements the changefeedStateClient interface.
   252  func (c *ormClient) createChangefeedState(tx *gorm.DB, state *ChangefeedStateDO) error {
   253  	ret := tx.Create(state)
   254  	if err := handleSingleOpErr(ret, 1, "CreateChangefeedState"); err != nil {
   255  		return errors.Trace(err)
   256  	}
   257  	return nil
   258  }
   259  
   260  // deleteChangefeedState implements the changefeedStateClient interface.
   261  func (c *ormClient) deleteChangefeedState(tx *gorm.DB, state *ChangefeedStateDO) error {
   262  	ret := tx.Delete(state)
   263  	if err := handleSingleOpErr(ret, 1, "DeleteChangefeedState"); err != nil {
   264  		return errors.Trace(err)
   265  	}
   266  	return nil
   267  }
   268  
   269  // changefeedStateModel is used to update changefeed state, which prevents
   270  // nil values (error and warning) from being ignored.
   271  var changefeedStateModel = &ChangefeedStateDO{}
   272  
   273  // updateChangefeedState implements the changefeedStateClient interface.
   274  func (c *ormClient) updateChangefeedState(tx *gorm.DB, state *ChangefeedStateDO) error {
   275  	ret := tx.Model(changefeedStateModel).Select("state", "warning", "error", "version").
   276  		Where("changefeed_uuid = ? and version = ?", state.ChangefeedUUID, state.Version).
   277  		Updates(ChangefeedStateDO{
   278  			ChangefeedState: metadata.ChangefeedState{
   279  				State:   state.State,
   280  				Warning: state.Warning,
   281  				Error:   state.Error,
   282  			},
   283  			Version: state.Version + 1,
   284  		})
   285  	if err := handleSingleOpErr(ret, 1, "updateChangefeedState"); err != nil {
   286  		return errors.Trace(err)
   287  	}
   288  	return nil
   289  }
   290  
   291  // queryChangefeedStates implements the changefeedStateClient interface.
   292  //
   293  //nolint:unused
   294  func (c *ormClient) queryChangefeedStates(tx *gorm.DB) ([]*ChangefeedStateDO, error) {
   295  	states := []*ChangefeedStateDO{}
   296  	ret := tx.Find(&states)
   297  	if err := handleSingleOpErr(ret, -1, "QueryChangefeedStates"); err != nil {
   298  		return nil, errors.Trace(err)
   299  	}
   300  	return states, nil
   301  }
   302  
   303  // queryChangefeedStatesByUpdateAt implements the changefeedStateClient interface.
   304  //
   305  //nolint:unused
   306  func (c *ormClient) queryChangefeedStatesByUpdateAt(tx *gorm.DB, lastUpdateAt time.Time) ([]*ChangefeedStateDO, error) {
   307  	states := []*ChangefeedStateDO{}
   308  	ret := tx.Where("update_at > ?", lastUpdateAt).Find(&states)
   309  	if err := handleSingleOpErr(ret, -1, "QueryChangefeedStatesByUpdateAt"); err != nil {
   310  		return nil, errors.Trace(err)
   311  	}
   312  	return states, nil
   313  }
   314  
   315  // queryChangefeedStateByUUID implements the changefeedStateClient interface.
   316  //
   317  //nolint:unused
   318  func (c *ormClient) queryChangefeedStateByUUID(tx *gorm.DB, uuid uint64) (*ChangefeedStateDO, error) {
   319  	state := &ChangefeedStateDO{}
   320  	ret := tx.Where("changefeed_uuid = ?", uuid).Limit(1).Find(state)
   321  	if err := handleSingleOpErr(ret, 1, "QueryChangefeedStateByUUID"); err != nil {
   322  		return nil, errors.Trace(err)
   323  	}
   324  	return state, nil
   325  }
   326  
   327  // queryChangefeedStateByUUIDs implements the changefeedStateClient interface.
   328  // nolint:unused
   329  func (c *ormClient) queryChangefeedStateByUUIDs(tx *gorm.DB, uuids ...uint64) ([]*ChangefeedStateDO, error) {
   330  	states := []*ChangefeedStateDO{}
   331  	ret := tx.Where("changefeed_uuid in (?)", uuids).Find(&states)
   332  	if err := handleSingleOpErr(ret, int64(len(uuids)), "QueryChangefeedInfosByUUIDs"); err != nil {
   333  		// TODO: optimize the behavior when some uuids are not found.
   334  		return states, errors.Trace(err)
   335  	}
   336  	return states, nil
   337  }
   338  
   339  // queryChangefeedStateByUUIDWithLock implements the changefeedStateClient interface.
   340  // nolint:unused
   341  func (c *ormClient) queryChangefeedStateByUUIDWithLock(tx *gorm.DB, uuid uint64) (*ChangefeedStateDO, error) {
   342  	state := &ChangefeedStateDO{}
   343  	ret := tx.Where("changefeed_uuid = ?", uuid).
   344  		Clauses(clause.Locking{
   345  			Strength: "SHARE",
   346  			Table:    clause.Table{Name: clause.CurrentTable},
   347  		}).Limit(1).Find(state)
   348  	if err := handleSingleOpErr(ret, 1, "QueryChangefeedStateByUUIDWithLock"); err != nil {
   349  		return nil, errors.Trace(err)
   350  	}
   351  	return state, nil
   352  }
   353  
   354  // ================================ Schedule Client =================================
   355  
   356  // createSchedule implements the scheduleClient interface.
   357  func (c *ormClient) createSchedule(tx *gorm.DB, sc *ScheduleDO) error {
   358  	ret := tx.Create(sc)
   359  	if err := handleSingleOpErr(ret, 1, "CreateSchedule"); err != nil {
   360  		return errors.Trace(err)
   361  	}
   362  	return nil
   363  }
   364  
   365  // deleteSchedule implements the scheduleClient interface.
   366  func (c *ormClient) deleteSchedule(tx *gorm.DB, sc *ScheduleDO) error {
   367  	ret := tx.Delete(sc)
   368  	if err := handleSingleOpErr(ret, 1, "DeleteSchedule"); err != nil {
   369  		return errors.Trace(err)
   370  	}
   371  	return nil
   372  }
   373  
   374  // updateSchedule implements the scheduleClient interface.
   375  func (c *ormClient) updateSchedule(tx *gorm.DB, sc *ScheduleDO) error {
   376  	ret := tx.Where("changefeed_uuid = ? and version = ?", sc.ChangefeedUUID, sc.Version).
   377  		Updates(ScheduleDO{
   378  			ScheduledChangefeed: metadata.ScheduledChangefeed{
   379  				Owner:        sc.Owner,
   380  				OwnerState:   sc.OwnerState,
   381  				Processors:   sc.Processors,
   382  				TaskPosition: sc.TaskPosition,
   383  			},
   384  			Version: sc.Version + 1,
   385  		})
   386  	if err := handleSingleOpErr(ret, 1, "UpdateSchedule"); err != nil {
   387  		return errors.Trace(err)
   388  	}
   389  	return nil
   390  }
   391  
   392  // updateScheduleOwnerState implements the scheduleClient interface.
   393  // TODO(CharlesCheung): check if the version needs to be checked.
   394  func (c *ormClient) updateScheduleOwnerState(tx *gorm.DB, sc *ScheduleDO) error {
   395  	ret := tx.Where("changefeed_uuid = ? and version = ?", sc.ChangefeedUUID, sc.Version).
   396  		Updates(ScheduleDO{
   397  			ScheduledChangefeed: metadata.ScheduledChangefeed{
   398  				OwnerState: sc.OwnerState,
   399  			},
   400  			Version: sc.Version + 1,
   401  		})
   402  	if err := handleSingleOpErr(ret, 1, "UpdateScheduleOwnerState"); err != nil {
   403  		return errors.Trace(err)
   404  	}
   405  	return nil
   406  }
   407  
   408  // updateScheduleOwnerStateByOwnerID implements the scheduleClient interface.
   409  func (c *ormClient) updateScheduleOwnerStateByOwnerID(tx *gorm.DB, state metadata.SchedState, ownerID model.CaptureID) error {
   410  	ret := tx.Model(&ScheduleDO{}).Select("owner", "owner_state", "processors", "version").
   411  		Where("owner = ?", ownerID).
   412  		Updates(map[string]interface{}{
   413  			"owner":       nil,
   414  			"owner_state": state,
   415  			"processors":  nil,
   416  			"version":     gorm.Expr("version + ?", 1),
   417  		})
   418  	if err := handleSingleOpErr(ret, -1, "UpdateScheduleOwnerStateByOwnerID"); err != nil {
   419  		return errors.Trace(err)
   420  	}
   421  	return nil
   422  }
   423  
   424  // querySchedules implements the scheduleClient interface.
   425  //
   426  //nolint:unused
   427  func (c *ormClient) querySchedules(tx *gorm.DB) ([]*ScheduleDO, error) {
   428  	schedules := []*ScheduleDO{}
   429  	ret := tx.Find(&schedules)
   430  	if err := handleSingleOpErr(ret, -1, "QuerySchedules"); err != nil {
   431  		return nil, errors.Trace(err)
   432  	}
   433  	return schedules, nil
   434  }
   435  
   436  // querySchedulesByUpdateAt implements the scheduleClient interface.
   437  //
   438  //nolint:unused
   439  func (c *ormClient) querySchedulesByUpdateAt(tx *gorm.DB, lastUpdateAt time.Time) ([]*ScheduleDO, error) {
   440  	schedules := []*ScheduleDO{}
   441  	ret := tx.Where("update_at > ?", lastUpdateAt).Find(&schedules)
   442  	if err := handleSingleOpErr(ret, -1, "QuerySchedulesByUpdateAt"); err != nil {
   443  		return nil, errors.Trace(err)
   444  	}
   445  	return schedules, nil
   446  }
   447  
   448  // querySchedulesByOwnerIDAndUpdateAt implements the scheduleClient interface.
   449  //
   450  //nolint:unused
   451  func (c *ormClient) querySchedulesByOwnerIDAndUpdateAt(tx *gorm.DB, captureID string, lastUpdateAt time.Time) ([]*ScheduleDO, error) {
   452  	schedules := []*ScheduleDO{}
   453  	ret := tx.Where("owner = ? and update_at > ?", captureID, lastUpdateAt).Find(&schedules)
   454  	if err := handleSingleOpErr(ret, -1, "QuerySchedulesByOwnerIDAndUpdateAt"); err != nil {
   455  		return nil, errors.Trace(err)
   456  	}
   457  	return schedules, nil
   458  }
   459  
   460  // queryScheduleByUUID implements the scheduleClient interface.
   461  //
   462  //nolint:unused
   463  func (c *ormClient) queryScheduleByUUID(tx *gorm.DB, uuid uint64) (*ScheduleDO, error) {
   464  	schedule := &ScheduleDO{}
   465  	ret := tx.Where("changefeed_uuid = ?", uuid).Limit(1).Find(schedule)
   466  	if err := handleSingleOpErr(ret, 1, "QueryScheduleByUUID"); err != nil {
   467  		return nil, errors.Trace(err)
   468  	}
   469  	return schedule, nil
   470  }
   471  
   472  // querySchedulesUinqueOwnerIDs implements the scheduleClient interface.
   473  // nolint:unused
   474  func (c *ormClient) querySchedulesUinqueOwnerIDs(tx *gorm.DB) ([]model.CaptureID, error) {
   475  	captureIDs := []model.CaptureID{}
   476  	ret := tx.Model(&ScheduleDO{}).Select("owner").Where("owner IS NOT NULL").Distinct().Find(&captureIDs)
   477  	if err := handleSingleOpErr(ret, -1, "QuerySchedulesUinqueOwnerIDs"); err != nil {
   478  		return nil, errors.Trace(err)
   479  	}
   480  	return captureIDs, nil
   481  }
   482  
   483  // ================================ Progress Client =================================
   484  
   485  // createProgress implements the progressClient interface.
   486  func (c *ormClient) createProgress(tx *gorm.DB, pr *ProgressDO) error {
   487  	ret := tx.Create(pr)
   488  	if err := handleSingleOpErr(ret, 1, "CreateProgress"); err != nil {
   489  		return errors.Trace(err)
   490  	}
   491  	return nil
   492  }
   493  
   494  // deleteProgress implements the progressClient interface.
   495  func (c *ormClient) deleteProgress(tx *gorm.DB, pr *ProgressDO) error {
   496  	ret := tx.Delete(pr)
   497  	if err := handleSingleOpErr(ret, 1, "DeleteProgress"); err != nil {
   498  		return errors.Trace(err)
   499  	}
   500  	return nil
   501  }
   502  
   503  // updateProgress implements the progressClient interface.
   504  func (c *ormClient) updateProgress(tx *gorm.DB, pr *ProgressDO) error {
   505  	ret := tx.Where("capture_id = ? and version = ?", pr.CaptureID, pr.Version).
   506  		Updates(ProgressDO{
   507  			Progress: pr.Progress,
   508  			Version:  pr.Version + 1,
   509  		})
   510  	if err := handleSingleOpErr(ret, 1, "UpdateProgress"); err != nil {
   511  		return errors.Trace(err)
   512  	}
   513  	return nil
   514  }
   515  
   516  // queryProgresses implements the progressClient interface.
   517  //
   518  //nolint:unused
   519  func (c *ormClient) queryProgresses(tx *gorm.DB) ([]*ProgressDO, error) {
   520  	progresses := []*ProgressDO{}
   521  	ret := tx.Find(&progresses)
   522  	if err := handleSingleOpErr(ret, -1, "queryProgresses"); err != nil {
   523  		return nil, errors.Trace(err)
   524  	}
   525  	return progresses, nil
   526  }
   527  
   528  // queryProgressesByUpdateAt implements the progressClient interface.
   529  //
   530  //nolint:unused
   531  func (c *ormClient) queryProgressesByUpdateAt(tx *gorm.DB, lastUpdateAt time.Time) ([]*ProgressDO, error) {
   532  	progresses := []*ProgressDO{}
   533  	ret := tx.Where("update_at > ?", lastUpdateAt).Find(&progresses)
   534  	if err := handleSingleOpErr(ret, -1, "queryProgressesByUpdateAt"); err != nil {
   535  		return nil, errors.Trace(err)
   536  	}
   537  	return progresses, nil
   538  }
   539  
   540  // queryProgressByCaptureID implements the progressClient interface.
   541  //
   542  //nolint:unused
   543  func (c *ormClient) queryProgressByCaptureID(tx *gorm.DB, id string) (*ProgressDO, error) {
   544  	progress := &ProgressDO{}
   545  	ret := tx.Where("capture_id = ?", id).Limit(1).Find(progress)
   546  	if err := handleSingleOpErr(ret, 1, "QueryProgressByCaptureID"); err != nil {
   547  		return nil, errors.Trace(err)
   548  	}
   549  	return progress, nil
   550  }
   551  
   552  // queryProgressByCaptureIDsWithLock implements the progressClient interface.
   553  //
   554  //nolint:unused
   555  func (c *ormClient) queryProgressByCaptureIDsWithLock(tx *gorm.DB, ids []string) ([]*ProgressDO, error) {
   556  	progresses := []*ProgressDO{}
   557  	ret := tx.Where("capture_id in (?)", ids).Clauses(clause.Locking{
   558  		Strength: "SHARE",
   559  		Table:    clause.Table{Name: clause.CurrentTable},
   560  	}).Find(&progresses)
   561  	if err := handleSingleOpErr(ret, -1, "QueryProgressByCaptureIDsWithLock"); err != nil {
   562  		return nil, errors.Trace(err)
   563  	}
   564  	return progresses, nil
   565  }
   566  
   567  func handleSingleOpErr(ret *gorm.DB, targetRows int64, opName string) (err error) {
   568  	defer func() {
   569  		if err != nil {
   570  			log.Error("run unary meta operation failed", zap.Error(err))
   571  		}
   572  	}()
   573  
   574  	if ret.Error != nil {
   575  		return errors.WrapError(errors.ErrMetaOpFailed, ret.Error, opName)
   576  	}
   577  	if targetRows >= 0 && ret.RowsAffected != targetRows {
   578  		return errors.ErrMetaRowsAffectedNotMatch.GenWithStackByArgs(opName, targetRows, ret.RowsAffected)
   579  	}
   580  	return nil
   581  }