mirror of
https://github.com/jakejarvis/sofa.git
synced 2026-08-29 01:35:39 -04:00
Add cronRuns table and integrate job scheduling with database logging
- Created a new `cronRuns` table to track job execution history, including job name, status, timestamps, and error messages. - Updated job scheduling logic to log job status in the `cronRuns` table, capturing success and error states. - Enhanced error handling to log detailed error messages in the database. This commit establishes a foundation for monitoring scheduled jobs and their outcomes.
This commit is contained in:
+31
-23
@@ -3,6 +3,7 @@ import { and, eq, isNotNull, lt, or } from "drizzle-orm";
|
||||
import { db } from "@/lib/db/client";
|
||||
import {
|
||||
availabilityOffers,
|
||||
cronRuns,
|
||||
seasons,
|
||||
titles,
|
||||
userTitleStatus,
|
||||
@@ -45,28 +46,35 @@ const jobs = globalForJobs._jobs;
|
||||
function schedule(name: string, cron: string, handler: () => Promise<void>) {
|
||||
jobs.set(
|
||||
name,
|
||||
new Cron(
|
||||
cron,
|
||||
{
|
||||
name,
|
||||
protect: true,
|
||||
catch: (err: unknown) => {
|
||||
log.error(`Job ${name} failed:`, err);
|
||||
},
|
||||
},
|
||||
async () => {
|
||||
log.info(`Running job: ${name}`);
|
||||
new Cron(cron, { name, protect: true }, async () => {
|
||||
log.info(`Running job: ${name}`);
|
||||
const run = db
|
||||
.insert(cronRuns)
|
||||
.values({ jobName: name, status: "running", startedAt: new Date() })
|
||||
.returning()
|
||||
.get();
|
||||
try {
|
||||
await handler();
|
||||
db.update(cronRuns)
|
||||
.set({ status: "success", finishedAt: new Date() })
|
||||
.where(eq(cronRuns.id, run.id))
|
||||
.run();
|
||||
log.info(`Completed job: ${name}`);
|
||||
},
|
||||
),
|
||||
} catch (err) {
|
||||
db.update(cronRuns)
|
||||
.set({
|
||||
status: "error",
|
||||
finishedAt: new Date(),
|
||||
errorMessage: err instanceof Error ? err.message : String(err),
|
||||
})
|
||||
.where(eq(cronRuns.id, run.id))
|
||||
.run();
|
||||
log.error(`Job ${name} failed:`, err);
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
function delay(ms: number) {
|
||||
return new Promise((r) => setTimeout(r, ms));
|
||||
}
|
||||
|
||||
function getLibraryTitleIds(): string[] {
|
||||
const rows = db
|
||||
.select({ titleId: userTitleStatus.titleId })
|
||||
@@ -93,7 +101,7 @@ async function nightlyRefreshLibrary() {
|
||||
.get();
|
||||
if (t) {
|
||||
await refreshTitle(titleId);
|
||||
await delay(RATE_LIMIT_MS);
|
||||
await Bun.sleep(RATE_LIMIT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,7 +121,7 @@ async function nightlyRefreshLibrary() {
|
||||
for (const t of nonLibrary) {
|
||||
if (!libraryIds.includes(t.id)) {
|
||||
await refreshTitle(t.id);
|
||||
await delay(RATE_LIMIT_MS);
|
||||
await Bun.sleep(RATE_LIMIT_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -145,7 +153,7 @@ async function refreshAvailabilityJob() {
|
||||
|
||||
if (offer || !anyOffer) {
|
||||
await refreshAvailability(titleId);
|
||||
await delay(RATE_LIMIT_MS);
|
||||
await Bun.sleep(RATE_LIMIT_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -156,7 +164,7 @@ async function refreshRecommendationsJob() {
|
||||
|
||||
for (const titleId of libraryIds) {
|
||||
await refreshRecommendations(titleId);
|
||||
await delay(RATE_LIMIT_MS);
|
||||
await Bun.sleep(RATE_LIMIT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +198,7 @@ async function refreshTvChildrenJob() {
|
||||
if (staleSeason) {
|
||||
const details = await getTvDetails(show.tmdbId);
|
||||
await refreshTvChildren(show.id, show.tmdbId, details.number_of_seasons);
|
||||
await delay(RATE_LIMIT_MS);
|
||||
await Bun.sleep(RATE_LIMIT_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,7 +217,7 @@ async function cacheImagesJob() {
|
||||
} catch {
|
||||
// Continue with remaining titles
|
||||
}
|
||||
await delay(RATE_LIMIT_MS);
|
||||
await Bun.sleep(RATE_LIMIT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -337,6 +337,25 @@ export const webhookEventLog = sqliteTable(
|
||||
],
|
||||
);
|
||||
|
||||
// ─── Cron Run History ────────────────────────────────────────────────
|
||||
|
||||
export const cronRuns = sqliteTable(
|
||||
"cronRuns",
|
||||
{
|
||||
id: uuidPk(),
|
||||
jobName: text("jobName").notNull(),
|
||||
status: text("status", {
|
||||
enum: ["running", "success", "error"],
|
||||
}).notNull(),
|
||||
startedAt: int("startedAt", { mode: "timestamp" }).notNull(),
|
||||
finishedAt: int("finishedAt", { mode: "timestamp" }),
|
||||
errorMessage: text("errorMessage"),
|
||||
},
|
||||
(table) => [
|
||||
index("cronRuns_jobName_startedAt").on(table.jobName, table.startedAt),
|
||||
],
|
||||
);
|
||||
|
||||
// ─── App Settings ───────────────────────────────────────────────────
|
||||
|
||||
export const appSettings = sqliteTable("appSettings", {
|
||||
|
||||
Reference in New Issue
Block a user