fix: search-now dedupes against an active job (match worker enqueue invariant)
The worker's enqueue_due refuses to enqueue when a non-terminal job already exists for a release. The search-now route enqueued unconditionally, so two clicks (or a click racing the worker sweep) could create two concurrent pipelines for the same album, both importing and colliding on LibraryItem's @@unique([artist,album]). Mirror the worker's guard: no-op if a job in a non-terminal state (anything other than imported/needs_attention) already exists for the release.
This commit is contained in:
@@ -23,4 +23,17 @@ describe("POST /api/releases/[id]/search", () => {
|
|||||||
it("404s an unknown release", async () => {
|
it("404s an unknown release", async () => {
|
||||||
expect((await POST(new Request("http://localhost/x", { method: "POST" }), ctx("nope"))).status).toBe(404);
|
expect((await POST(new Request("http://localhost/x", { method: "POST" }), ctx("nope"))).status).toBe(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("does not enqueue a duplicate while a non-terminal job is active", async () => {
|
||||||
|
const r = await prisma.monitoredRelease.create({
|
||||||
|
data: { artistMbid: "a1", artistName: "John Mayer", rgMbid: "rg-dup", album: "Continuum", secondaryTypes: [], monitored: true },
|
||||||
|
});
|
||||||
|
const first = await POST(new Request("http://localhost/x", { method: "POST" }), ctx(r.id));
|
||||||
|
expect((await first.json()).enqueued).toBe(true);
|
||||||
|
// the first job is in a non-terminal state ('requested'); a second search must no-op
|
||||||
|
const second = await POST(new Request("http://localhost/x", { method: "POST" }), ctx(r.id));
|
||||||
|
expect(second.status).toBe(202);
|
||||||
|
expect((await second.json()).enqueued).toBe(false);
|
||||||
|
expect(await prisma.request.count({ where: { monitoredReleaseId: r.id } })).toBe(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,6 +5,16 @@ export async function POST(_request: Request, { params }: { params: Promise<{ id
|
|||||||
const release = await prisma.monitoredRelease.findUnique({ where: { id } });
|
const release = await prisma.monitoredRelease.findUnique({ where: { id } });
|
||||||
if (!release) return Response.json({ error: "not found" }, { status: 404 });
|
if (!release) return Response.json({ error: "not found" }, { status: 404 });
|
||||||
|
|
||||||
|
const activeJob = await prisma.job.findFirst({
|
||||||
|
where: {
|
||||||
|
request: { monitoredReleaseId: release.id },
|
||||||
|
state: { notIn: ["imported", "needs_attention"] },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (activeJob) {
|
||||||
|
return Response.json({ enqueued: false }, { status: 202 });
|
||||||
|
}
|
||||||
|
|
||||||
await prisma.request.create({
|
await prisma.request.create({
|
||||||
data: {
|
data: {
|
||||||
artist: release.artistName,
|
artist: release.artistName,
|
||||||
|
|||||||
Reference in New Issue
Block a user