fix: users can retriger transcription
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import * as schema from '@db/schema';
|
||||
|
||||
import { and, desc, eq, inArray, sql } from '@db/drizzle';
|
||||
import { and, desc, eq, inArray, lt, sql } from '@db/drizzle';
|
||||
import { db, type DbOrTxClient } from '@db/drizzle';
|
||||
import type { TJobStatus, TMediaJob, TNewMediaJob } from '@db/types';
|
||||
|
||||
@@ -109,6 +109,39 @@ export async function updateMediaJob(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flip any `queued` / `running` media_job rows untouched since `staleBeforeIso`
|
||||
* to `failed`. These belong to crashed workers, Redis-less runs, or BullMQ
|
||||
* jobs that vanished — without this sweep the active-job guard would keep
|
||||
* blocking new runs forever.
|
||||
*
|
||||
* Returns the rows that were updated so callers can log or dead-letter them.
|
||||
*/
|
||||
export async function reapStuckMediaJobs(
|
||||
staleBeforeIso: string,
|
||||
dbClient: DbOrTxClient = db
|
||||
): Promise<Pick<TMediaJob, 'id' | 'organizationId' | 'assetId'>[]> {
|
||||
try {
|
||||
return await dbClient
|
||||
.update(schema.mediaJob)
|
||||
.set({
|
||||
status: 'failed',
|
||||
stage: 'failed',
|
||||
error: { code: 'STUCK_AFTER_RESTART', message: 'No worker progress within the stale window' },
|
||||
updatedAt: new Date().toISOString()
|
||||
})
|
||||
.where(and(inArray(schema.mediaJob.status, ['queued', 'running']), lt(schema.mediaJob.updatedAt, staleBeforeIso)))
|
||||
.returning({
|
||||
id: schema.mediaJob.id,
|
||||
organizationId: schema.mediaJob.organizationId,
|
||||
assetId: schema.mediaJob.assetId
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('reapStuckMediaJobs error:', error);
|
||||
throw new Error('Failed to reap stuck media jobs');
|
||||
}
|
||||
}
|
||||
|
||||
export async function requestMediaJobCancel(
|
||||
jobId: string,
|
||||
orgId: string,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as schema from '@db/schema';
|
||||
|
||||
import { and, eq, inArray } from '@db/drizzle';
|
||||
import { and, eq, gte, inArray } from '@db/drizzle';
|
||||
import { db, type DbOrTxClient } from '@db/drizzle';
|
||||
import type { TMediaTranscript, TNewMediaTranscript } from '@db/types';
|
||||
|
||||
@@ -99,14 +99,28 @@ export async function listMediaTranscriptsByAssetIds(
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any media_job for this asset is still queued or running.
|
||||
* Default freshness window for "active" media_job rows. Rows older than this
|
||||
* are assumed to belong to a crashed worker / a Redis-less run and are
|
||||
* ignored by `hasActiveMediaJobForAsset` so users can retry. A separate
|
||||
* sweeper (`reapStuckMediaJobs`) flips those stale rows to `failed`.
|
||||
*/
|
||||
export const MEDIA_JOB_STALE_AFTER_MINUTES = 30;
|
||||
|
||||
/**
|
||||
* Returns true if any media_job for this asset is still queued or running and
|
||||
* has been touched within `staleAfterMinutes` (default 30). Stale rows are
|
||||
* treated as dead: they shouldn't block new runs because the worker that
|
||||
* owned them is gone (server restart, Redis flush, lost BullMQ job, etc.).
|
||||
*/
|
||||
export async function hasActiveMediaJobForAsset(
|
||||
assetId: string,
|
||||
organizationId: string,
|
||||
dbClient: DbOrTxClient = db
|
||||
options: { staleAfterMinutes?: number; dbClient?: DbOrTxClient } = {}
|
||||
): Promise<boolean> {
|
||||
const { staleAfterMinutes = MEDIA_JOB_STALE_AFTER_MINUTES, dbClient = db } = options;
|
||||
|
||||
try {
|
||||
const freshSinceIso = new Date(Date.now() - staleAfterMinutes * 60 * 1_000).toISOString();
|
||||
const [row] = await dbClient
|
||||
.select({ id: schema.mediaJob.id })
|
||||
.from(schema.mediaJob)
|
||||
@@ -114,7 +128,8 @@ export async function hasActiveMediaJobForAsset(
|
||||
and(
|
||||
eq(schema.mediaJob.assetId, assetId),
|
||||
eq(schema.mediaJob.organizationId, organizationId),
|
||||
inArray(schema.mediaJob.status, ['queued', 'running'])
|
||||
inArray(schema.mediaJob.status, ['queued', 'running']),
|
||||
gte(schema.mediaJob.updatedAt, freshSinceIso)
|
||||
)
|
||||
)
|
||||
.limit(1);
|
||||
|
||||
Reference in New Issue
Block a user