实现意外获取Task后重新放回去

This commit is contained in:
2025-09-17 15:45:40 +08:00
parent 2402c206dc
commit c750ef350d
2 changed files with 41 additions and 34 deletions

View File

@@ -11,8 +11,11 @@ import (
// PendingTaskRepository 定义了与待执行任务队列交互的接口。
type PendingTaskRepository interface {
CreatePendingTasksInBatch(tasks []*models.PendingTask) error
ClaimNextAvailableTask(excludePlanIDs []uint) (*models.TaskExecutionLog, error)
RequeueTask(log *models.TaskExecutionLog) error
// ClaimNextAvailableTask 原子地认领下一个可用的任务。
// 它会同时返回被认领任务对应的日志对象,以及被删除的待办任务对象的内存副本。
ClaimNextAvailableTask(excludePlanIDs []uint) (*models.TaskExecutionLog, *models.PendingTask, error)
// RequeueTask 安全地将一个任务重新放回队列。
RequeueTask(originalPendingTask *models.PendingTask) error
}
// pendingTaskRepository 是使用 GORM 的具体实现。
@@ -31,11 +34,11 @@ func (r *pendingTaskRepository) CreatePendingTasksInBatch(tasks []*models.Pendin
}
// ClaimNextAvailableTask 以原子方式认领下一个可用的任务。
func (r *pendingTaskRepository) ClaimNextAvailableTask(excludePlanIDs []uint) (*models.TaskExecutionLog, error) {
func (r *pendingTaskRepository) ClaimNextAvailableTask(excludePlanIDs []uint) (*models.TaskExecutionLog, *models.PendingTask, error) {
var log models.TaskExecutionLog
var pendingTask models.PendingTask
err := r.db.Transaction(func(tx *gorm.DB) error {
var pendingTask models.PendingTask
query := tx.Clauses(clause.Locking{Strength: "UPDATE"}).
Where("execute_at <= ?", time.Now()).
Order("execute_at ASC")
@@ -68,27 +71,26 @@ func (r *pendingTaskRepository) ClaimNextAvailableTask(excludePlanIDs []uint) (*
})
if err != nil {
return nil, err
return nil, nil, err
}
return &log, nil
return &log, &pendingTask, nil
}
// RequeueTask 安全地将一个已被认领但无法执行的任务放回队列。
// 它在一个事务中原子地将日志状态恢复为 'waiting',并重新创建待办任务
func (r *pendingTaskRepository) RequeueTask(log *models.TaskExecutionLog) error {
// RequeueTask 安全地将一个任务重新放回队列。
// 它通过将原始 PendingTask 的 ID 重置为 0,并重新创建它来实现
func (r *pendingTaskRepository) RequeueTask(originalPendingTask *models.PendingTask) error {
return r.db.Transaction(func(tx *gorm.DB) error {
// 1. 将日志状态恢复为 waiting
if err := tx.Model(log).Update("status", models.ExecutionStatusWaiting).Error; err != nil {
if err := tx.Model(&models.TaskExecutionLog{}).Where("id = ?", originalPendingTask.TaskExecutionLogID).Update("status", models.ExecutionStatusWaiting).Error; err != nil {
return err
}
// 2. 重新创建待办任务,立即执行
newPendingTask := models.PendingTask{
TaskID: log.TaskID,
TaskExecutionLogID: log.ID,
ExecuteAt: time.Now(),
}
return tx.Create(&newPendingTask).Error
// 2. 关键:将传入的 PendingTask 的 ID 重置为 0。
// 这会告诉 GORM这是一个需要创建INSERT的新记录而不是更新。
originalPendingTask.ID = 0
// 3. 重新创建待办任务。GORM 会忽略掉已被重置的 ID并让数据库生成一个新的主键。
return tx.Create(originalPendingTask).Error
})
}