BackgroundJob: Store result!

Hello,

I’m using the new BackgroundJob task from Redwood 8.4, and I needed to store result at the end of my job. For exmeple, store a downloadLink, return an ID, a password or a state value.

It was missing in Redwood so I patched it a little and I wanted to share :

  • add result value as JSON in the BackgroundJob schema
  • add uid value as String in the BackgroundJob schema
  • extend the PrismaAdapter
  • store the result

Here is the CustomPrismaAdapter

import type { PrismaClient } from '@prisma/client'

import { v4 as uuidv4 } from 'uuid'

import type { SchedulePayload } from '@redwoodjs/jobs'
import { PrismaAdapter } from '@redwoodjs/jobs'

export class CustomPrismaAdapter extends PrismaAdapter {

  constructor(options) {
    super(options)
  }

  override async schedule({
    name,
    path,
    args,
    runAt,
    queue,
    priority,
  }: SchedulePayload) {
    this.logger.debug(`[CustomRedwoodJob] Job scheduled`)
    const uid = uuidv4()
    await this.accessor.create({
      data: {
        handler: JSON.stringify({ name, path, args: [uid, ...args] }),
        runAt,
        queue,
        priority,
        uid
      },
    })
  }
}

The new schema


model BackgroundJob {
  id        Int       @id @default(autoincrement())
  attempts  Int       @default(0)
  handler   String
  queue     String
  priority  Int
  runAt     DateTime?
  lockedAt  DateTime?
  lockedBy  String?
  lastError String?
  failedAt  DateTime?
  createdAt DateTime  @default(now())
  updatedAt DateTime  @updatedAt
  result    Json?
  uid       String?   @unique
}

And finally, how to use it:

MyJob.ts

import { updateBackgroundJobByUid } from 'src/services/backgroundJobs/backgroundJobs'
....


export const MyJob = jobs.createJob({
  queue: 'default',
  perform: async (uid, projectId, username) => {
    jobs.logger.info(`MyJob for project ${projectId} by ${username} is starting...`)

    await updateBackgroundJobByUid({uid, input: {result: {aa: 42} }})

    jobs.logger.info('SaveProjectJob has completed.')
  },
})

The uid thing is ugly, but I didnt found a better way to access to the current job inside himself in a simple way.

Let me share you tought !

Ps : We then have a TaskPanel displaying task and able to display the output

image

You can even decorate the function like this, to handle return. It cleaner


export const ExportProjectJob = jobs.createJob({
  queue: "default",
  perform: async (...args) => storeResult(perform, ...args)
});

const perform = async (projectId, username) => {
  jobs.logger.info("ExportProjectJob is performing...")

  let project = await findProjectJSON(projectId, username)
  if (!project) {
    return { statusCode: 404 }
  }
}


export const storeResult = async (fct, ...args) => 
{
  const [uid, ...rest] = args
  const res = await fct(...rest)
  await updateBackgroundJobByUid({uid, input: {result: res }})
}