jobs: queue scheduler + REST job API (phase 1c + 2)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -77,6 +77,7 @@ export {
|
||||
getPipelineJob,
|
||||
updatePipelineJob,
|
||||
getPipelineJobsForUser,
|
||||
getOldestPendingJob,
|
||||
markInterruptedJobs,
|
||||
} from './queries/pipeline-jobs';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { eq, and, inArray, desc } from 'drizzle-orm';
|
||||
import { eq, asc, desc } from 'drizzle-orm';
|
||||
import { db } from '../db';
|
||||
import { pipelineJobs } from '../schema/pipeline-jobs';
|
||||
import type { PipelineJobInsert } from '../types';
|
||||
@@ -26,11 +26,24 @@ export async function getPipelineJobsForUser(userId: number, limit = 50) {
|
||||
.limit(limit);
|
||||
}
|
||||
|
||||
// Oldest queued job across everything (single-user → global queue). Used to promote the next job.
|
||||
export async function getOldestPendingJob() {
|
||||
const rows = await db
|
||||
.select()
|
||||
.from(pipelineJobs)
|
||||
.where(eq(pipelineJobs.status, 'pending'))
|
||||
.orderBy(asc(pipelineJobs.createdAt))
|
||||
.limit(1);
|
||||
return rows[0] ?? null;
|
||||
}
|
||||
|
||||
// On restart, only RUNNING jobs are orphaned (their process died) → interrupted. PENDING jobs are the
|
||||
// queue backlog and must survive to be promoted after startup.
|
||||
export async function markInterruptedJobs() {
|
||||
const result = await db
|
||||
.update(pipelineJobs)
|
||||
.set({ status: 'interrupted', completedAt: new Date() })
|
||||
.where(inArray(pipelineJobs.status, ['pending', 'running']))
|
||||
.where(eq(pipelineJobs.status, 'running'))
|
||||
.returning({ id: pipelineJobs.id });
|
||||
return result.length;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user