fix: 修复文章赋能死锁导致请求卡住的问题
- 事务内操作 users 表(扣域能)后,调用 LevelService.AddExp 也操作同一 users 表行,不同 DB 连接间产生行锁死锁 - 将 AddExp 和 UpsertDailyExp 移出事务,等事务提交释放行锁后再执行 - 经验计算仍在事务内读取以保证一致性
This commit is contained in:
@ -63,6 +63,9 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
|||||||
}
|
}
|
||||||
|
|
||||||
var expGained int
|
var expGained int
|
||||||
|
var needExpUpdate bool
|
||||||
|
var expToday time.Time
|
||||||
|
var expTotal int
|
||||||
|
|
||||||
err := s.db.Transaction(func(tx *gorm.DB) error {
|
err := s.db.Transaction(func(tx *gorm.DB) error {
|
||||||
txRepo := s.repo.WithTx(tx)
|
txRepo := s.repo.WithTx(tx)
|
||||||
@ -153,7 +156,7 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
|||||||
return fmt.Errorf("创建被赋能者流水失败: %w", err)
|
return fmt.Errorf("创建被赋能者流水失败: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 10. 计算经验值(溢出截断)
|
// 10. 计算经验值(溢出截断)—— 在事务内读取以保证一致性
|
||||||
loc, _ := time.LoadLocation("Asia/Shanghai")
|
loc, _ := time.LoadLocation("Asia/Shanghai")
|
||||||
now := time.Now().In(loc)
|
now := time.Now().In(loc)
|
||||||
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
today := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, loc)
|
||||||
@ -173,30 +176,38 @@ func (s *EnergyService) Energize(energizerID uint, postID uint, amount int) (int
|
|||||||
expGain = expRoom
|
expGain = expRoom
|
||||||
}
|
}
|
||||||
|
|
||||||
// 11. 增加经验 + 更新每日经验汇总
|
|
||||||
if expGain > 0 {
|
|
||||||
if s.expSvc != nil {
|
|
||||||
_, _, err = s.expSvc.AddExp(energizerID, expGain)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("增加经验失败: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
newSummary := &model.DailyExpSummary{
|
|
||||||
UserID: energizerID,
|
|
||||||
Date: today,
|
|
||||||
ExpEarned: currentDaily + expGain,
|
|
||||||
}
|
|
||||||
if err := txRepo.UpsertDailyExp(newSummary); err != nil {
|
|
||||||
return fmt.Errorf("更新每日经验汇总失败: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
expGained = expGain
|
expGained = expGain
|
||||||
|
if expGain > 0 {
|
||||||
|
needExpUpdate = true
|
||||||
|
expToday = today
|
||||||
|
expTotal = currentDaily + expGain
|
||||||
|
}
|
||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
return expGained, err
|
// 11. 事务提交后,在事务外增加经验 + 更新每日汇总
|
||||||
|
// 必须在事务外执行:LevelService.AddExp 也操作 users 表,
|
||||||
|
// 若在事务内调用会造成连接池不同连接间行锁死锁。
|
||||||
|
if needExpUpdate {
|
||||||
|
if s.expSvc != nil {
|
||||||
|
if _, _, err := s.expSvc.AddExp(energizerID, expGained); err != nil {
|
||||||
|
return expGained, fmt.Errorf("赋能成功,但增加经验失败: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newSummary := &model.DailyExpSummary{
|
||||||
|
UserID: energizerID,
|
||||||
|
Date: expToday,
|
||||||
|
ExpEarned: expTotal,
|
||||||
|
}
|
||||||
|
if err := s.repo.UpsertDailyExp(newSummary); err != nil {
|
||||||
|
return expGained, fmt.Errorf("赋能成功,但更新经验汇总失败: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return expGained, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// CheckInEnergy 签到获得域能(LV0 用户不调用此方法)
|
// CheckInEnergy 签到获得域能(LV0 用户不调用此方法)
|
||||||
|
|||||||
Reference in New Issue
Block a user