138 lines
4.4 KiB
TypeScript
138 lines
4.4 KiB
TypeScript
/* istanbul ignore file */
|
|
import cron from 'node-cron'
|
|
import { moduleLogger, previewLogger as logger } from '@/observability/logging'
|
|
import { buildConsumePreviewResult } from '@/modules/previews/resultListener'
|
|
import {
|
|
disablePreviews,
|
|
getFeatureFlags,
|
|
getPreviewServiceRedisUrl,
|
|
getRedisUrl,
|
|
getServerOrigin
|
|
} from '@/modules/shared/helpers/envHelper'
|
|
import type { Queue } from 'bull'
|
|
import { ensureError } from '@speckle/shared'
|
|
import { previewRouterFactory } from '@/modules/previews/rest/router'
|
|
import type { SpeckleModule } from '@/modules/shared/helpers/typeHelper'
|
|
import {
|
|
initializeMetrics,
|
|
observeMetricsFactory
|
|
} from '@/modules/previews/observability/metrics'
|
|
import {
|
|
requestActiveHandlerFactory,
|
|
requestErrorHandlerFactory,
|
|
requestFailedHandlerFactory
|
|
} from '@/modules/previews/queues/previews'
|
|
import { scheduleExecutionFactory } from '@/modules/core/services/taskScheduler'
|
|
import {
|
|
acquireTaskLockFactory,
|
|
releaseTaskLockFactory
|
|
} from '@/modules/core/repositories/scheduledTasks'
|
|
import { getProjectDbClient } from '@/modules/multiregion/utils/dbSelector'
|
|
import { db } from '@/db/knex'
|
|
import { createRequestAndResponseQueues } from '@/modules/previews/clients/bull'
|
|
import { responseHandlerFactory } from '@/modules/previews/services/responses'
|
|
import { updateObjectPreviewFactory } from '@/modules/previews/repository/previews'
|
|
import type { BuildUpdateObjectPreview } from '@/modules/previews/domain/operations'
|
|
import { scheduleRetryFailedPreviews } from '@/modules/previews/tasks/tasks'
|
|
|
|
const { FF_RETRY_ERRORED_PREVIEWS_ENABLED } = getFeatureFlags()
|
|
|
|
let scheduledTasks: cron.ScheduledTask[] = []
|
|
|
|
const JobQueueName = 'preview-service-jobs'
|
|
const ResponseQueueNamePrefix = 'preview-service-results'
|
|
|
|
const buildUpdateObjectPreviewFunction =
|
|
(): BuildUpdateObjectPreview => async (params) => {
|
|
const { projectId } = params
|
|
const projectDb = await getProjectDbClient({ projectId })
|
|
return updateObjectPreviewFactory({ db: projectDb })
|
|
}
|
|
|
|
export const init: SpeckleModule['init'] = async ({
|
|
app,
|
|
isInitial,
|
|
metricsRegister
|
|
}) => {
|
|
if (!isInitial) return
|
|
|
|
if (disablePreviews()) {
|
|
moduleLogger.warn('📸 Object preview module is DISABLED')
|
|
return
|
|
} else {
|
|
moduleLogger.info('📸 Init object preview module')
|
|
}
|
|
|
|
const scheduleExecution = scheduleExecutionFactory({
|
|
acquireTaskLock: acquireTaskLockFactory({ db }),
|
|
releaseTaskLock: releaseTaskLockFactory({ db })
|
|
})
|
|
|
|
const responseQueueName = `${ResponseQueueNamePrefix}-${
|
|
new URL(getServerOrigin()).hostname
|
|
}`
|
|
|
|
let previewRequestQueue: Queue
|
|
let previewResponseQueue: Queue
|
|
|
|
try {
|
|
;({ requestQueue: previewRequestQueue, responseQueue: previewResponseQueue } =
|
|
await createRequestAndResponseQueues({
|
|
redisUrl: getPreviewServiceRedisUrl() ?? getRedisUrl(),
|
|
requestQueueName: JobQueueName,
|
|
responseQueueName,
|
|
requestErrorHandler: requestErrorHandlerFactory({ logger }),
|
|
requestFailedHandler: requestFailedHandlerFactory({
|
|
logger,
|
|
buildUpdateObjectPreview: buildUpdateObjectPreviewFunction()
|
|
}),
|
|
requestActiveHandler: requestActiveHandlerFactory({ logger })
|
|
}))
|
|
} catch (e) {
|
|
const err = ensureError(e, 'Unknown error when creating preview queues')
|
|
moduleLogger.error({ err }, 'Could not create preview queues. Disabling previews.')
|
|
return
|
|
}
|
|
|
|
scheduledTasks = [
|
|
...(FF_RETRY_ERRORED_PREVIEWS_ENABLED
|
|
? [
|
|
await scheduleRetryFailedPreviews({
|
|
scheduleExecution,
|
|
previewRequestQueue,
|
|
responseQueueName,
|
|
cronExpression: '*/23 * * * *' // every 23 minutes (kind of random prime number to reduce syncing with other possibly heavy tasks)
|
|
})
|
|
]
|
|
: [])
|
|
]
|
|
|
|
const { previewJobsProcessedSummary } = initializeMetrics({
|
|
registers: [metricsRegister],
|
|
previewRequestQueue,
|
|
previewResponseQueue
|
|
})
|
|
|
|
const previewRouter = previewRouterFactory({
|
|
previewRequestQueue,
|
|
responseQueueName
|
|
})
|
|
app.use(previewRouter)
|
|
|
|
void previewResponseQueue.process(
|
|
responseHandlerFactory({
|
|
observeMetrics: observeMetricsFactory({ summary: previewJobsProcessedSummary }),
|
|
logger,
|
|
consumePreviewResultBuilder: buildConsumePreviewResult
|
|
})
|
|
)
|
|
}
|
|
|
|
export const finalize = () => {}
|
|
|
|
export const shutdown: SpeckleModule['shutdown'] = async () => {
|
|
scheduledTasks.forEach((task) => {
|
|
task.stop()
|
|
})
|
|
}
|