[backend/queue] Add abandon button for delayed jobs to the queue dashboard

This commit is contained in:
Laura Hausmann 2024-09-27 03:39:45 +02:00
parent 1883f426a7
commit 7532d5c52a
No known key found for this signature in database
GPG key ID: D044E84C5BE01605
4 changed files with 47 additions and 2 deletions

View file

@ -133,6 +133,16 @@ public class AdminController(
await queueSvc.RetryJobAsync(job); await queueSvc.RetryJobAsync(job);
} }
[HttpPost("queue/jobs/{id::guid}/abandon")]
[ProducesResults(HttpStatusCode.OK)]
[ProducesErrors(HttpStatusCode.BadRequest, HttpStatusCode.NotFound)]
public async Task AbandonQueueJob(Guid id)
{
var job = await db.Jobs.FirstOrDefaultAsync(p => p.Id == id) ??
throw GracefulException.NotFound($"Job {id} was not found.");
await queueSvc.AbandonJobAsync(job);
}
[UseNewtonsoftJson] [UseNewtonsoftJson]
[HttpGet("activities/notes/{id}")] [HttpGet("activities/notes/{id}")]

View file

@ -127,10 +127,11 @@ public class QueueService(
public async Task RetryJobAsync(Job job) public async Task RetryJobAsync(Job job)
{ {
await using var scope = scopeFactory.CreateAsyncScope();
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
if (job.Status == Job.JobStatus.Failed) if (job.Status == Job.JobStatus.Failed)
{ {
await using var scope = scopeFactory.CreateAsyncScope();
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
var cnt = await db.Jobs.Where(p => p.Id == job.Id && p.Status == Job.JobStatus.Failed) var cnt = await db.Jobs.Where(p => p.Id == job.Id && p.Status == Job.JobStatus.Failed)
.ExecuteUpdateAsync(p => p.SetProperty(i => i.Status, _ => Job.JobStatus.Queued) .ExecuteUpdateAsync(p => p.SetProperty(i => i.Status, _ => Job.JobStatus.Queued)
.SetProperty(i => i.QueuedAt, _ => DateTime.UtcNow) .SetProperty(i => i.QueuedAt, _ => DateTime.UtcNow)
@ -146,6 +147,26 @@ public class QueueService(
_queues.FirstOrDefault(p => p.Name == job.Queue)?.RaiseJobQueuedEvent(); _queues.FirstOrDefault(p => p.Name == job.Queue)?.RaiseJobQueuedEvent();
} }
} }
public async Task AbandonJobAsync(Job job)
{
if (job.Status == Job.JobStatus.Delayed)
{
await using var scope = scopeFactory.CreateAsyncScope();
await using var db = scope.ServiceProvider.GetRequiredService<DatabaseContext>();
const string message = "Job was abandoned on user request";
await db.Jobs.Where(p => p.Id == job.Id && p.Status == Job.JobStatus.Delayed)
.ExecuteUpdateAsync(p => p.SetProperty(i => i.Status, _ => Job.JobStatus.Failed)
.SetProperty(i => i.DelayedUntil, _ => null)
.SetProperty(i => i.StartedAt, _ => DateTime.UtcNow)
.SetProperty(i => i.FinishedAt, _ => DateTime.UtcNow)
.SetProperty(i => i.Exception, _ => message)
.SetProperty(i => i.ExceptionMessage, _ => message)
.SetProperty(i => i.ExceptionSource, _ => "QueueService")
.SetProperty(i => i.StackTrace, _ => null));
}
}
} }
public interface IPostgresJobQueue public interface IPostgresJobQueue

View file

@ -47,6 +47,15 @@
</td> </td>
</tr> </tr>
} }
else if (Model.Job.Status.Equals(Job.JobStatus.Delayed))
{
<tr>
<td>Actions</td>
<td>
<a class="fake-link" onclick="abandon('@Model.Job.Id.ToStringLower()')">Abandon</a>
</td>
</tr>
}
<tr> <tr>
<td>Queued at</td> <td>Queued at</td>
<td>@Model.Job.QueuedAt.ToLocalTime().ToDisplayStringTz()</td> <td>@Model.Job.QueuedAt.ToLocalTime().ToDisplayStringTz()</td>

View file

@ -63,3 +63,8 @@ async function retryAllOnPage(queue) {
await callApiMethod(`/api/iceshrimp/admin/queue/${queue}/retry-range/${first}/${last}`); await callApiMethod(`/api/iceshrimp/admin/queue/${queue}/retry-range/${first}/${last}`);
window.location.reload(); window.location.reload();
} }
async function abandon(id) {
await callApiMethod(`/api/iceshrimp/admin/queue/jobs/${id}/abandon`);
window.location.reload();
}