feat(api): add per-chapter requeue scope for dead jobs
Lets the admin manga page requeue a single failed chapter's dead job(s) inline, without a job id. Adds RequeueScope::Chapter + the matching request variant and a repo test. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -297,6 +297,7 @@ async fn list_dead_jobs(
|
|||||||
enum RequeueRequest {
|
enum RequeueRequest {
|
||||||
All,
|
All,
|
||||||
Manga { manga_id: Uuid },
|
Manga { manga_id: Uuid },
|
||||||
|
Chapter { chapter_id: Uuid },
|
||||||
Job { job_id: Uuid },
|
Job { job_id: Uuid },
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -313,6 +314,7 @@ async fn requeue_dead_jobs(
|
|||||||
let scope = match &body {
|
let scope = match &body {
|
||||||
RequeueRequest::All => RequeueScope::All,
|
RequeueRequest::All => RequeueScope::All,
|
||||||
RequeueRequest::Manga { manga_id } => RequeueScope::Manga(*manga_id),
|
RequeueRequest::Manga { manga_id } => RequeueScope::Manga(*manga_id),
|
||||||
|
RequeueRequest::Chapter { chapter_id } => RequeueScope::Chapter(*chapter_id),
|
||||||
RequeueRequest::Job { job_id } => RequeueScope::Job(*job_id),
|
RequeueRequest::Job { job_id } => RequeueScope::Job(*job_id),
|
||||||
};
|
};
|
||||||
let requeued = repo::crawler::requeue_dead_jobs(&state.db, scope).await?;
|
let requeued = repo::crawler::requeue_dead_jobs(&state.db, scope).await?;
|
||||||
@@ -332,6 +334,7 @@ fn scope_label(r: &RequeueRequest) -> &'static str {
|
|||||||
match r {
|
match r {
|
||||||
RequeueRequest::All => "all",
|
RequeueRequest::All => "all",
|
||||||
RequeueRequest::Manga { .. } => "manga",
|
RequeueRequest::Manga { .. } => "manga",
|
||||||
|
RequeueRequest::Chapter { .. } => "chapter",
|
||||||
RequeueRequest::Job { .. } => "job",
|
RequeueRequest::Job { .. } => "job",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -706,6 +706,8 @@ pub enum RequeueScope {
|
|||||||
All,
|
All,
|
||||||
/// Dead jobs whose chapter belongs to this manga.
|
/// Dead jobs whose chapter belongs to this manga.
|
||||||
Manga(Uuid),
|
Manga(Uuid),
|
||||||
|
/// Dead jobs for a single chapter.
|
||||||
|
Chapter(Uuid),
|
||||||
/// A single dead job by its id.
|
/// A single dead job by its id.
|
||||||
Job(Uuid),
|
Job(Uuid),
|
||||||
}
|
}
|
||||||
@@ -751,6 +753,18 @@ pub async fn requeue_dead_jobs(pool: &PgPool, scope: RequeueScope) -> sqlx::Resu
|
|||||||
.await?
|
.await?
|
||||||
.rows_affected()
|
.rows_affected()
|
||||||
}
|
}
|
||||||
|
RequeueScope::Chapter(chapter_id) => {
|
||||||
|
sqlx::query(&format!(
|
||||||
|
"UPDATE crawler_jobs {SET} \
|
||||||
|
WHERE state = 'dead' \
|
||||||
|
AND (payload->>'chapter_id')::uuid = $1 \
|
||||||
|
{NO_LIVE_DUP}"
|
||||||
|
))
|
||||||
|
.bind(chapter_id)
|
||||||
|
.execute(pool)
|
||||||
|
.await?
|
||||||
|
.rows_affected()
|
||||||
|
}
|
||||||
RequeueScope::Job(job_id) => {
|
RequeueScope::Job(job_id) => {
|
||||||
sqlx::query(&format!(
|
sqlx::query(&format!(
|
||||||
"UPDATE crawler_jobs {SET} WHERE state = 'dead' AND id = $1 {NO_LIVE_DUP}"
|
"UPDATE crawler_jobs {SET} WHERE state = 'dead' AND id = $1 {NO_LIVE_DUP}"
|
||||||
|
|||||||
@@ -126,6 +126,21 @@ async fn requeue_by_manga_scopes_to_that_manga(pool: PgPool) {
|
|||||||
assert_eq!(state_of(&pool, j2).await, "dead", "other manga untouched");
|
assert_eq!(state_of(&pool, j2).await, "dead", "other manga untouched");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[sqlx::test(migrations = "./migrations")]
|
||||||
|
async fn requeue_by_chapter_scopes_to_that_chapter(pool: PgPool) {
|
||||||
|
let (_m, c1) = seed_chapter(&pool, "A", 1).await;
|
||||||
|
let (_m2, c2) = seed_chapter(&pool, "A", 2).await;
|
||||||
|
let j1 = insert_job(&pool, c1, "dead", 5).await;
|
||||||
|
let j2 = insert_job(&pool, c2, "dead", 5).await;
|
||||||
|
|
||||||
|
let n = crawler::requeue_dead_jobs(&pool, RequeueScope::Chapter(c1))
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(n, 1);
|
||||||
|
assert_eq!(state_of(&pool, j1).await, "pending");
|
||||||
|
assert_eq!(state_of(&pool, j2).await, "dead", "other chapter untouched");
|
||||||
|
}
|
||||||
|
|
||||||
#[sqlx::test(migrations = "./migrations")]
|
#[sqlx::test(migrations = "./migrations")]
|
||||||
async fn requeue_single_job(pool: PgPool) {
|
async fn requeue_single_job(pool: PgPool) {
|
||||||
let (_m, c1) = seed_chapter(&pool, "A", 1).await;
|
let (_m, c1) = seed_chapter(&pool, "A", 1).await;
|
||||||
|
|||||||
Reference in New Issue
Block a user