Update objectpreview on error

- use const with explanatory variable names
This commit is contained in:
Iain Sproat
2025-03-13 09:15:41 +00:00
parent bb57e1a5fa
commit f033953d33
9 changed files with 61 additions and 22 deletions
+8 -6
View File
@@ -33,7 +33,11 @@ export const jobProcessor = async ({
port,
timeout
}: JobArgs): Promise<PreviewResultPayload> => {
const start = new Date()
const elapsed = (() => {
const start = new Date().getTime()
return () => (new Date().getTime() - start) / 1000
})()
logger.info('Picked up job {jobId} for {serverUrl}')
const jobMessage =
@@ -43,12 +47,10 @@ export const jobProcessor = async ({
page = await browser.newPage()
const result = await pageFunction({ page, job, logger, port, timeout })
const elapsed = (new Date().getTime() - start.getTime()) / 1000
logger.info({ status: result.status, elapsed }, jobMessage)
logger.info({ status: result.status, elapsed: elapsed() }, jobMessage)
return result
} catch (err: unknown) {
const elapsed = (new Date().getTime() - start.getTime()) / 1000
logger.error({ err, elapsed, status: 'error' }, jobMessage)
logger.error({ err, elapsed: elapsed(), status: 'error' }, jobMessage)
const reason =
err instanceof Error
? err.stack ?? err.toString()
@@ -60,7 +62,7 @@ export const jobProcessor = async ({
jobId: job.jobId,
status: 'error',
result: {
durationSeconds: elapsed
durationSeconds: elapsed()
},
reason
}
@@ -0,0 +1,12 @@
export const PreviewStatus = {
PENDING: 0,
PROCESSING: 1,
DONE: 2,
ERROR: 3
} as const
export const PreviewPriority = {
LOW: 0,
MEDIUM: 100,
HIGH: 200
} as const
@@ -1,6 +1,6 @@
import { ObjectPreview } from '@/modules/previews/domain/types'
import { Nullable, Optional } from '@speckle/shared'
import express from 'express'
import type { ObjectPreview } from '@/modules/previews/domain/types'
import type { Nullable, Optional, PartialBy } from '@speckle/shared'
import type { Request, Response } from 'express'
export type GetObjectPreviewInfo = (params: {
streamId: string
@@ -17,7 +17,7 @@ export type ObjectPreviewInput = Pick<
>
export type StoreObjectPreview = (params: ObjectPreviewInput) => Promise<void>
export type UpsertObjectPreview = (params: {
objectPreview: ObjectPreview
objectPreview: PartialBy<ObjectPreview, 'preview' | 'priority'>
}) => Promise<void>
export type ObjectPreviewRequest = {
@@ -54,13 +54,13 @@ export type GetObjectPreviewBufferOrFilepath = (params: {
>
export type SendObjectPreview = (
req: express.Request,
res: express.Response,
req: Request,
res: Response,
streamId: string,
objectId: string,
angle?: string
) => Promise<void>
export type CheckStreamPermissions = (
req: express.Request
req: Request
) => Promise<{ hasPermissions: boolean; httpErrorCode: number }>
+4 -1
View File
@@ -70,7 +70,10 @@ const getPreviewQueues = (params: { responseQueueName: string }) => {
// previews are requested on this queue
const previewRequestQueue = new Bull('preview-service-jobs', opts)
addRequestQueueListeners({ logger, previewRequestQueue })
addRequestQueueListeners({
logger,
previewRequestQueue
})
// rendered previews are sent back on this queue
const previewResponseQueue = new Bull(responseQueueName, opts)
@@ -2,6 +2,9 @@ import type { RequestObjectPreview } from '@/modules/previews/domain/operations'
import type { Logger } from '@/observability/logging'
import type { Queue, Job } from 'bull'
import type { EventEmitter } from 'stream'
import { upsertObjectPreviewFactory } from '@/modules/previews/repository/previews'
import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector'
import { PreviewStatus } from '@/modules/previews/domain/consts'
export const requestObjectPreviewFactory =
({
@@ -30,9 +33,20 @@ export const addRequestQueueListeners = (params: {
previewRequestQueue.removeListener('error', requestErrorHandler)
previewRequestQueue.on('error', requestErrorHandler)
const requestFailedHandler = (job: Job, err: Error) => {
const requestFailedHandler = async (job: Job, err: Error) => {
const jobId = 'jobId' in job.data ? job.data.jobId : undefined
logger.error({ err, jobId }, 'Preview job {jobId} failed.')
if (!jobId) return
const [projectId, objectId] = jobId.split('.')
const projectDb = await getProjectDbClient({ projectId })
upsertObjectPreviewFactory({ db: projectDb })({
objectPreview: {
streamId: projectId,
objectId,
previewStatus: PreviewStatus.ERROR,
lastUpdate: new Date()
}
})
}
previewRequestQueue.removeListener('failed', requestFailedHandler)
previewRequestQueue.on('failed', requestFailedHandler)
@@ -12,6 +12,7 @@ import {
} from '@/modules/previews/domain/types'
import { Knex } from 'knex'
import { SetOptional } from 'type-fest'
import { PreviewStatus } from '@/modules/previews/domain/consts'
const ObjectPreview = buildTableHelper('object_preview', [
'streamId',
@@ -53,7 +54,7 @@ export const storeObjectPreviewFactory =
streamId,
objectId,
priority,
previewStatus: 0
previewStatus: PreviewStatus.PENDING
}
const sqlQuery = tables.objectPreview(db).insert(insertionObject)
@@ -10,6 +10,7 @@ import crypto from 'crypto'
import { StorePreview, UpsertObjectPreview } from '@/modules/previews/domain/operations'
import { joinImages } from 'join-images'
import { GetObjectCommitsWithStreamIds } from '@/modules/core/domain/commits/operations'
import { PreviewPriority, PreviewStatus } from '@/modules/previews/domain/consts'
const payloadRegexp = /^([\w\d]+):([\w\d]+):([\w\d]+)$/i
@@ -70,8 +71,7 @@ export const consumePreviewResultFactory =
}) => {
const streamId = projectId
const lastUpdate = new Date()
const priority = 0
const previewStatus = 2
const priority = PreviewPriority.LOW
const log = logger.child({
jobId: previewResult.jobId,
status: previewResult.status,
@@ -92,7 +92,7 @@ export const consumePreviewResultFactory =
lastUpdate,
preview: { err: previewResult.reason },
priority,
previewStatus
previewStatus: PreviewStatus.ERROR
}
})
break
@@ -141,7 +141,7 @@ export const consumePreviewResultFactory =
lastUpdate,
preview,
priority,
previewStatus
previewStatus: PreviewStatus.DONE
}
})
const commits = await getObjectCommitsWithStreamIds([objectId], {
@@ -13,6 +13,7 @@ import { authorizeResolver, validateScopes } from '@/modules/shared'
import { disablePreviews } from '@/modules/shared/helpers/envHelper'
import { Roles, Scopes } from '@speckle/shared'
import type { Logger } from 'pino'
import { PreviewPriority, PreviewStatus } from '@/modules/previews/domain/consts'
const noPreviewImage = require.resolve('#/assets/previews/images/no_preview.png')
const previewErrorImage = require.resolve('#/assets/previews/images/preview_error.png')
@@ -54,12 +55,16 @@ export const getObjectPreviewBufferOrFilepathFactory =
const objPreviewQueued = await deps.createObjectPreview({
streamId,
objectId,
priority: 0
priority: PreviewPriority.LOW
})
if (!objPreviewQueued) return { type: 'file', file: noPreviewImage }
}
if (!previewInfo || previewInfo.previewStatus !== 2 || !previewInfo.preview) {
if (
!previewInfo ||
previewInfo.previewStatus !== PreviewStatus.DONE ||
!previewInfo.preview
) {
return { type: 'file', file: noPreviewImage }
}
@@ -16,6 +16,8 @@ export type PartialNullable<T> = {
[K in keyof T]?: T[K] | null
}
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
type NullableKeys<T> = {
[K in keyof T]: T[K] extends NonNullable<T[K]> ? never : K
}[keyof T]