Skip to content

Feature: Support child_process runtime #64

@AriPerkkio

Description

@AriPerkkio

Description

Currently Tinypool can be used to run tasks in node:worker_threads only. However it seems that various Node ecosystem packages are incompatible with worker_threads, e.g. aws-sdk, @prisma/client, bcrypt. Typically the common feature of these packages is that they utilize NodeJS API's through native addons. I guess there is a reason why Jest does not use worker_threads by default.

Add an option to choose between node:worker_threads and node:child_process runtimes.

Constructor

new Tinypool({}) // Defaults to 'worker_threads' for backwards compatibility
new Tinypool({ runtime: 'worker_threads' })
new Tinypool({ runtime: 'child_process' })

In future we might even add support for third party runtimes. These could be for example browser runtimes:

new Tinypool({ runtime: 'tinypool-runtime-browser' })
new Tinypool({ runtime: '@organization/tinypool-runtime-custom' })

Pool methods

There might be times where we don't know which runtime to use when initializing the pool. We'll need to add support for changing the runtime of existing pool.

pool.run()

const pool = new Tinypool({
  runtime: 'worker_threads',
  minThreads: 4,
  maxThreads: 4
})
await pool.run(task) // Runs in `worker_threads`

At this point pool has 4 idle worker_threads. We want to run next task in child_process instead:

await pool.run(task, { runtime: 'child_process' })

Pool notices that runtime does not match with idle workers. It has to pick a worker from pool, terminate it and spawn a new worker with different runtime.

Once task has finished there will be 3 worker_threads and 1 child_process workers idle.

pool.recycleWorkers()

For optimal performance the end-users should be able to change runtime of all workers. This can be useful when they can identify which runtimes their predetermined tasks will need.

const tasks = [...]
const pool = new Tinypool({ runtime: 'worker_threads' })

// Run `worker_thread` tasks first
await Promise.all(tasks.filter(pickWorkerThreadCompatibleTasks).map(t => pool.run(t)))

await pool.recycleWorkers({ runtime: 'child_process' })
// All idle workers will now be `child_process` runtimes

await Promise.all(tasks.filter(pickChildProcessTasks).map(t => pool.run(t)))

Other

This won't be easy task and might require heavy refactoring. If there are some features that require worker_threads, I think we should simply throw or log an error when those are called with different runtime.

Best approach to start this is to create a common interface for runtimes. This interface would hide implementation details of runtimes and provide a simple API for pool to utilize. Same interface would eventually be used by third party runtime implementors.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions