Skip to content

Commit 7c9767d

Browse files
[BDGR-198] Do not delete source from Tus until processing job succeeds (#671)
* [BDGR-198] Do not delete source from Tus until processing job succeeds * Add test-force-failure to ProcessMediaJob too * Fix failure check in test
1 parent 2840cdd commit 7c9767d

File tree

5 files changed

+74
-9
lines changed

5 files changed

+74
-9
lines changed

jobrunner/src/jobs/LoadAssetJob.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export class LoadAssetJob extends MediaJobCommon {
8383
},
8484
},
8585
});
86+
await this._cleanupSourceFile(params);
8687
} catch (e) {
8788
await this.db.asset.update({
8889
where: {

jobrunner/src/jobs/MediaJobCommon.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,6 @@ export abstract class MediaJobCommon extends AbstractJob {
2323
filePath,
2424
);
2525
invariant(await dl.wait(), "download did not succeed");
26-
27-
await got.delete(process.env.TUS_ENDPOINT + "/" + params.source, {
28-
headers: {
29-
"Tus-Resumable": "1.0.0",
30-
},
31-
});
3226
break;
3327
}
3428
//fallthrough
@@ -103,4 +97,21 @@ export abstract class MediaJobCommon extends AbstractJob {
10397
await upload.done();
10498
return s3Path;
10599
}
100+
101+
protected async _cleanupSourceFile(params: PrismaJson.JobPayload) {
102+
invariant("sourceType" in params, "sourceType is required");
103+
switch (params.sourceType) {
104+
case "Tus": {
105+
await got.delete(process.env.TUS_ENDPOINT + "/" + params.source, {
106+
headers: {
107+
"Tus-Resumable": "1.0.0",
108+
},
109+
});
110+
break;
111+
}
112+
case "S3":
113+
// Nothing to do here, it will already be at the expected location
114+
break;
115+
}
116+
}
106117
}

jobrunner/src/jobs/ProcessMediaJob.integration.test.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { MediaState } from "@badger/prisma/client";
1+
import { JobState, MediaState } from "@badger/prisma/client";
22
import { it, expect } from "vitest";
33
import { doOneJob } from "../index.js";
44
import { integrate } from "@badger/testing";
@@ -28,7 +28,7 @@ async function uploadTestFileToTus() {
2828
throw new Error("Tus rejected creation");
2929
}
3030

31-
const uploadReq = await got.stream.patch(createRes.headers.location!, {
31+
const uploadReq = got.stream.patch(createRes.headers.location!, {
3232
body: sourceFile,
3333
headers: {
3434
"Tus-Resumable": "1.0.0",
@@ -104,4 +104,49 @@ integrate("ProcessMediaJob", () => {
104104
);
105105
expect(tusRes.statusCode).not.toBe(200);
106106
});
107+
108+
it("handles failure", async () => {
109+
const testMediaPath = await uploadTestFileToTus();
110+
const media = await db.media.create({
111+
data: {
112+
name: "__FAIL__smpte_bars_15s.mp4",
113+
durationSeconds: 0,
114+
rawPath: "",
115+
continuityItems: {
116+
create: {
117+
name: "Test",
118+
durationSeconds: 0,
119+
order: 1,
120+
show: {
121+
create: {
122+
name: "Test",
123+
start: new Date(),
124+
},
125+
},
126+
},
127+
},
128+
},
129+
});
130+
const job = await db.baseJob.create({
131+
data: {
132+
jobType: "ProcessMediaJob",
133+
jobPayload: {
134+
mediaId: media.id,
135+
sourceType: "Tus",
136+
source: testMediaPath,
137+
},
138+
},
139+
});
140+
await doOneJob();
141+
await expect(
142+
db.baseJob.findFirst({ where: { id: job.id } }),
143+
).resolves.toHaveProperty("state", JobState.Failed);
144+
// Check the file is not deleted from Tus
145+
const res = await got.head(process.env.TUS_ENDPOINT + "/" + testMediaPath, {
146+
headers: {
147+
"Tus-Resumable": "1.0.0",
148+
},
149+
});
150+
expect(res.statusCode).toBe(200);
151+
});
107152
});

jobrunner/src/jobs/ProcessMediaJob.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,13 @@ export default class ProcessMediaJob extends MediaJobCommon {
7878
});
7979

8080
try {
81+
// Test only: allow testing failure handling
82+
if (media.name.includes("__FAIL__")) {
83+
throw new Error(
84+
"Failing job to test error handling (I sure do hope this is a test...)",
85+
);
86+
}
87+
8188
const rawTempPath = await this._wrapTask(
8289
media,
8390
"Downloading source file",
@@ -291,6 +298,7 @@ export default class ProcessMediaJob extends MediaJobCommon {
291298
},
292299
},
293300
});
301+
await this._cleanupSourceFile(params);
294302
} catch (e) {
295303
await this.db.media.update({
296304
where: {

server/next.config.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// @ts-check
2-
/* eslint-disable @typescript-eslint/no-var-requires */
2+
/* eslint-disable @typescript-eslint/no-require-imports */
33
const { PrismaPlugin } = require("@prisma/nextjs-monorepo-workaround-plugin");
44
const { withSentryConfig } = require("@sentry/nextjs");
55
const { execFileSync } = require("child_process");

0 commit comments

Comments
 (0)