mirror of
https://github.com/seaweedfs/seaweedfs.git
synced 2026-05-23 10:11:28 +00:00
adding retry button
This commit is contained in:
@@ -167,6 +167,7 @@ func (h *AdminHandlers) SetupRoutes(r *gin.Engine, authRequired bool, username,
|
||||
maintenanceApi.GET("/tasks/:id", h.adminServer.GetMaintenanceTask)
|
||||
maintenanceApi.GET("/tasks/:id/detail", h.adminServer.GetMaintenanceTaskDetailAPI)
|
||||
maintenanceApi.POST("/tasks/:id/cancel", h.adminServer.CancelMaintenanceTask)
|
||||
maintenanceApi.POST("/tasks/:taskId/retry", h.maintenanceHandlers.RetryTask)
|
||||
maintenanceApi.GET("/workers", h.adminServer.GetMaintenanceWorkersAPI)
|
||||
maintenanceApi.GET("/workers/:id", h.adminServer.GetMaintenanceWorker)
|
||||
maintenanceApi.GET("/workers/:id/logs", h.adminServer.GetWorkerLogs)
|
||||
@@ -293,6 +294,7 @@ func (h *AdminHandlers) SetupRoutes(r *gin.Engine, authRequired bool, username,
|
||||
maintenanceApi.GET("/tasks/:id", h.adminServer.GetMaintenanceTask)
|
||||
maintenanceApi.GET("/tasks/:id/detail", h.adminServer.GetMaintenanceTaskDetailAPI)
|
||||
maintenanceApi.POST("/tasks/:id/cancel", h.adminServer.CancelMaintenanceTask)
|
||||
maintenanceApi.POST("/tasks/:taskId/retry", h.maintenanceHandlers.RetryTask)
|
||||
maintenanceApi.GET("/workers", h.adminServer.GetMaintenanceWorkersAPI)
|
||||
maintenanceApi.GET("/workers/:id", h.adminServer.GetMaintenanceWorker)
|
||||
maintenanceApi.GET("/workers/:id/logs", h.adminServer.GetWorkerLogs)
|
||||
|
||||
@@ -469,6 +469,32 @@ func (h *MaintenanceHandlers) UpdateMaintenanceConfig(c *gin.Context) {
|
||||
c.Redirect(http.StatusSeeOther, "/maintenance/config")
|
||||
}
|
||||
|
||||
// RetryTask manually retries a maintenance task
|
||||
func (h *MaintenanceHandlers) RetryTask(c *gin.Context) {
|
||||
taskID := c.Param("taskId")
|
||||
if taskID == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "Task ID is required"})
|
||||
return
|
||||
}
|
||||
|
||||
manager := h.adminServer.GetMaintenanceManager()
|
||||
if manager == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Maintenance manager not available"})
|
||||
return
|
||||
}
|
||||
|
||||
err := manager.RetryTask(taskID)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"success": true,
|
||||
"message": fmt.Sprintf("Task %s has been queued for retry", taskID),
|
||||
})
|
||||
}
|
||||
|
||||
// Helper methods that delegate to AdminServer
|
||||
|
||||
func (h *MaintenanceHandlers) getMaintenanceQueueData() (*maintenance.MaintenanceQueueData, error) {
|
||||
|
||||
@@ -568,3 +568,8 @@ func (mm *MaintenanceManager) UpdateTaskProgress(taskID string, progress float64
|
||||
func (mm *MaintenanceManager) UpdateWorkerHeartbeat(workerID string) {
|
||||
mm.queue.UpdateWorkerHeartbeat(workerID)
|
||||
}
|
||||
|
||||
// RetryTask manually retries a failed or pending task
|
||||
func (mm *MaintenanceManager) RetryTask(taskID string) error {
|
||||
return mm.queue.RetryTask(taskID)
|
||||
}
|
||||
|
||||
@@ -639,7 +639,59 @@ func generateTaskID() string {
|
||||
return fmt.Sprintf("%s-%04d", string(b), timestamp)
|
||||
}
|
||||
|
||||
// CleanupOldTasks removes old completed and failed tasks
|
||||
// RetryTask manually retries a failed or pending task
|
||||
func (mq *MaintenanceQueue) RetryTask(taskID string) error {
|
||||
mq.mutex.Lock()
|
||||
defer mq.mutex.Unlock()
|
||||
|
||||
task, exists := mq.tasks[taskID]
|
||||
if !exists {
|
||||
return fmt.Errorf("task %s not found", taskID)
|
||||
}
|
||||
|
||||
// Only allow retry for failed or pending tasks
|
||||
if task.Status != TaskStatusFailed && task.Status != TaskStatusPending {
|
||||
return fmt.Errorf("task %s cannot be retried (status: %s)", taskID, task.Status)
|
||||
}
|
||||
|
||||
// Reset task for retry
|
||||
now := time.Now()
|
||||
task.Status = TaskStatusPending
|
||||
task.WorkerID = ""
|
||||
task.StartedAt = nil
|
||||
task.CompletedAt = nil
|
||||
task.Error = ""
|
||||
task.ScheduledAt = now // Schedule immediately
|
||||
task.Progress = 0
|
||||
|
||||
// Add to assignment history if it was previously assigned
|
||||
if len(task.AssignmentHistory) > 0 {
|
||||
lastAssignment := task.AssignmentHistory[len(task.AssignmentHistory)-1]
|
||||
if lastAssignment.UnassignedAt == nil {
|
||||
unassignedTime := now
|
||||
lastAssignment.UnassignedAt = &unassignedTime
|
||||
lastAssignment.Reason = "Manual retry requested"
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from current pending list if already there to avoid duplicates
|
||||
for i, pendingTask := range mq.pendingTasks {
|
||||
if pendingTask.ID == taskID {
|
||||
mq.pendingTasks = append(mq.pendingTasks[:i], mq.pendingTasks[i+1:]...)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Add back to pending queue
|
||||
mq.pendingTasks = append(mq.pendingTasks, task)
|
||||
|
||||
// Save task state
|
||||
mq.saveTaskState(task)
|
||||
|
||||
glog.Infof("Task manually retried: %s (%s) for volume %d", taskID, task.Type, task.VolumeID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mq *MaintenanceQueue) CleanupOldTasks(retention time.Duration) int {
|
||||
mq.mutex.Lock()
|
||||
defer mq.mutex.Unlock()
|
||||
|
||||
@@ -98,40 +98,47 @@ templ MaintenanceQueue(data *maintenance.MaintenanceQueueData) {
|
||||
<th>Worker</th>
|
||||
<th>Duration</th>
|
||||
<th>Completed</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
for _, task := range data.Tasks {
|
||||
if string(task.Status) == "completed" || string(task.Status) == "failed" || string(task.Status) == "cancelled" {
|
||||
if string(task.Status) == "failed" {
|
||||
<tr class="table-danger clickable-row" data-task-id={task.ID} onclick="navigateToTask(this)" style="cursor: pointer;">
|
||||
<td>
|
||||
<tr class="table-danger">
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}>
|
||||
@TaskTypeIcon(task.Type)
|
||||
{string(task.Type)}
|
||||
</td>
|
||||
<td>@StatusBadge(task.Status)</td>
|
||||
<td>{fmt.Sprintf("%d", task.VolumeID)}</td>
|
||||
<td>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}>@StatusBadge(task.Status)</td>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}>{fmt.Sprintf("%d", task.VolumeID)}</td>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}>
|
||||
if task.WorkerID != "" {
|
||||
<small>{task.WorkerID}</small>
|
||||
} else {
|
||||
<span class="text-muted">-</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}>
|
||||
if task.StartedAt != nil && task.CompletedAt != nil {
|
||||
{formatDuration(task.CompletedAt.Sub(*task.StartedAt))}
|
||||
} else {
|
||||
<span class="text-muted">-</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}>
|
||||
if task.CompletedAt != nil {
|
||||
{task.CompletedAt.Format("2006-01-02 15:04")}
|
||||
} else {
|
||||
<span class="text-muted">-</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-warning" onclick="retryTask('{task.ID}')" title="Retry Failed Task">
|
||||
<i class="fas fa-redo me-1"></i>
|
||||
Retry
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
} else {
|
||||
<tr class="clickable-row" data-task-id={task.ID} onclick="navigateToTask(this)" style="cursor: pointer;">
|
||||
@@ -162,6 +169,9 @@ templ MaintenanceQueue(data *maintenance.MaintenanceQueueData) {
|
||||
<span class="text-muted">-</span>
|
||||
}
|
||||
</td>
|
||||
<td>
|
||||
<span class="text-muted">-</span>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
@@ -203,21 +213,28 @@ templ MaintenanceQueue(data *maintenance.MaintenanceQueueData) {
|
||||
<th>Server</th>
|
||||
<th>Reason</th>
|
||||
<th>Created</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
for _, task := range data.Tasks {
|
||||
if string(task.Status) == "pending" {
|
||||
<tr class="clickable-row" data-task-id={task.ID} onclick="navigateToTask(this)" style="cursor: pointer;">
|
||||
<td>
|
||||
<tr>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}>
|
||||
@TaskTypeIcon(task.Type)
|
||||
{string(task.Type)}
|
||||
</td>
|
||||
<td>@PriorityBadge(task.Priority)</td>
|
||||
<td>{fmt.Sprintf("%d", task.VolumeID)}</td>
|
||||
<td><small>{task.Server}</small></td>
|
||||
<td><small>{task.Reason}</small></td>
|
||||
<td>{task.CreatedAt.Format("2006-01-02 15:04")}</td>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}>@PriorityBadge(task.Priority)</td>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}>{fmt.Sprintf("%d", task.VolumeID)}</td>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}><small>{task.Server}</small></td>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}><small>{task.Reason}</small></td>
|
||||
<td onclick="navigateToTask(this)" style="cursor: pointer;" data-task-id={task.ID}>{task.CreatedAt.Format("2006-01-02 15:04")}</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-warning" onclick="retryTask('{task.ID}')" title="Retry Task">
|
||||
<i class="fas fa-redo me-1"></i>
|
||||
Retry
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
}
|
||||
}
|
||||
@@ -342,6 +359,33 @@ templ MaintenanceQueue(data *maintenance.MaintenanceQueueData) {
|
||||
window.location.href = '/maintenance/tasks/' + taskId;
|
||||
}
|
||||
};
|
||||
|
||||
window.retryTask = function(taskId) {
|
||||
console.log("retryTask called for task:", taskId);
|
||||
|
||||
if (!confirm('Are you sure you want to retry this task?')) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/maintenance/tasks/' + taskId + '/retry', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
alert('Task retried successfully: ' + data.message);
|
||||
setTimeout(() => window.location.reload(), 1000);
|
||||
} else {
|
||||
alert('Failed to retry task: ' + (data.error || 'Unknown error'));
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
alert('Error retrying task: ' + error.message);
|
||||
});
|
||||
};
|
||||
</script>
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user