taskforcesh / bullmq

BullMQ - Message Queue and Batch processing for NodeJS and Python based on Redis
https://bullmq.io
MIT License
5.73k stars 372 forks source link

Support sandboxed processors in Next.js #2572

Open lafbelmonte opened 2 months ago

lafbelmonte commented 2 months ago

Is your feature request related to a problem? Please describe. I want to run sandboxed processors in Next.js but the way it is architectured with webpack is giving me a hard time. The way I am planning to do this with Next.js is create a different entry point (webpack) for my task then pass it as path in the sandboxed processor:

next.config.js:

    webpack: (config, {isServer}) => {
        if (isServer && config.name === 'server') {
            const oldEntry = config.entry;

            return {
                ...config,
                async entry(...args) {
                    const entries = await oldEntry(...args);
                    return {
                        ...entries,
                        task: path.resolve(process.cwd(), 'path/to/task.ts'),
                    };
                },
            };
        }

        return config;
    },

The task file will be generated at .next/server/task.js, so I can just pass the path to the worker as ${__dirname}/.next/server/task.js.

Task file:

const task = async () => {
    console.log('PONG!');
};

export default task;

Now somehow after importing the processor file (${__dirname}/.next/server/task.js) here: https://github.com/taskforcesh/bullmq/blob/master/src/classes/child-processor.ts#L30, processorFn is a Promise, thus giving me:

processorFn value:
Promise {
  <pending>,
  [Symbol(webpack exports)]: Object [Module] { default: [Getter] },
  [Symbol(webpack queues)]: [Function (anonymous)]
}

error:
Error: No function is exported in processor file

I'm really not sure why its returning a Promise, maybe some webpack dynamic import thing?

Describe the solution you'd like Support processorFn being a Promise, check if it is a Promise then use await. This worked for my case:

        try {

            const { default: processorFn } = await import(processorFile);
            processor = await processorFn; <-- added await here
            if (processor.default) {
                // support es2015 module.
                processor = processor.default;
            }
            if (typeof processor !== 'function') {
                throw new Error('No function is exported in processor file');
            }
        }

Describe alternatives you've considered N/A

Additional context N/A

manast commented 2 months ago

I think that without really knowing why webpack is returning a promise when doing a dynamic import, I am going to be reluctant to implemente the proposed solution... it could be the result of something specific in your webpack setup, but even if not, it feels really hacky to do an extra await for seemly no reason.