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 }