Fix deadlock on image downloading

This commit is contained in:
Zoe Roux 2025-11-23 23:18:40 +01:00
parent 20ab1dae6c
commit a4f5ef33ff
No known key found for this signature in database
4 changed files with 83 additions and 7 deletions

View File

@ -152,8 +152,7 @@ async function processOne() {
span.setStatus({ code: SpanStatusCode.ERROR });
}
console.error("Failed to download image", img.url, err.message);
// don't use the transaction here, it can be aborted.
await db
await tx
.update(mqueue)
.set({ attempt: sql`${mqueue.attempt}+1` })
.where(eq(mqueue.id, item.id));

View File

@ -0,0 +1,48 @@
import { buildUrl } from "tests/utils";
import { handlers } from "~/base";
import { getJwtHeaders } from "./jwt";
export const getCollection = async (
id: string,
{
langs,
...query
}: { langs?: string; preferOriginal?: boolean; with?: string[] },
) => {
const resp = await handlers.handle(
new Request(buildUrl(`collections/${id}`, query), {
method: "GET",
headers: langs
? {
"Accept-Language": langs,
...(await getJwtHeaders()),
}
: await getJwtHeaders(),
}),
);
const body = await resp.json();
return [resp, body] as const;
};
export const getCollections = async ({
langs,
...query
}: {
langs?: string;
preferOriginal?: boolean;
with?: string[];
}) => {
const resp = await handlers.handle(
new Request(buildUrl("collections", query), {
method: "GET",
headers: langs
? {
"Accept-Language": langs,
...(await getJwtHeaders()),
}
: await getJwtHeaders(),
}),
);
const body = await resp.json();
return [resp, body] as const;
};

View File

@ -1,4 +1,5 @@
export * from "~/base";
export * from "./collections-helper";
export * from "./movies-helper";
export * from "./series-helper";
export * from "./shows-helper";

View File

@ -1,19 +1,22 @@
import { describe, expect, it } from "bun:test";
import { eq } from "drizzle-orm";
import { beforeAll, describe, expect, it } from "bun:test";
import { and, eq, sql } from "drizzle-orm";
import { createMovie, createSerie } from "tests/helpers";
import { expectStatus } from "tests/utils";
import { defaultBlurhash, processImages } from "~/controllers/seed/images";
import { db } from "~/db";
import { mqueue, shows, staff, studios, videos } from "~/db/schema";
import { madeInAbyss } from "~/models/examples";
import { createSerie } from "../helpers";
import { dune, madeInAbyss } from "~/models/examples";
describe("images", () => {
it("Create a serie download images", async () => {
beforeAll(async () => {
await db.delete(shows);
await db.delete(studios);
await db.delete(staff);
await db.delete(videos);
await db.delete(mqueue);
});
it("Create a serie download images", async () => {
await createSerie(madeInAbyss);
const release = await processImages();
// remove notifications to prevent other images to be downloaded (do not curl 20000 images for nothing)
@ -26,4 +29,29 @@ describe("images", () => {
expect(ret!.original.poster!.blurhash).toBeString();
expect(ret!.original.poster!.blurhash).not.toBe(defaultBlurhash);
});
it("Download 404 image", async () => {
const [ret, body] = await createMovie({
...dune,
translations: {
en: {
...dune.translations.en,
poster: "https://www.google.com/404",
},
},
});
expectStatus(ret, body).toBe(201);
const release = await processImages();
// remove notifications to prevent other images to be downloaded (do not curl 20000 images for nothing)
release();
const failed = await db.query.mqueue.findFirst({
where: and(
eq(mqueue.kind, "image"),
eq(sql`${mqueue.message}->>'url'`, "https://www.google.com/404"),
),
});
expect(failed!.attempt).toBe(5);
});
});