98 lines
2.5 KiB
Markdown
98 lines
2.5 KiB
Markdown
# Worker threads
|
|
|
|
For most applications, `better-sqlite3` is fast enough to use in the main thread without blocking for a noticeable amount of time. However, if you need to perform very slow queries, you have the option of using [worker threads](https://nodejs.org/api/worker_threads.html) to keep things running smoothly. Below is an example of using a thread pool to perform queries in the background.
|
|
|
|
### worker.js
|
|
|
|
The worker logic is very simple in our case. It accepts messages from the master thread, executes each message's SQL (with any given parameters), and sends back the query results.
|
|
|
|
```js
|
|
const { parentPort } = require('worker_threads');
|
|
const db = require('better-sqlite3')('foobar.db');
|
|
|
|
parentPort.on('message', ({ sql, parameters }) => {
|
|
const result = db.prepare(sql).all(...parameters);
|
|
parentPort.postMessage(result);
|
|
});
|
|
```
|
|
|
|
### master.js
|
|
|
|
The master thread is responsible for spawning workers, respawning threads that crash, and accepting query jobs.
|
|
|
|
```js
|
|
const { Worker } = require('worker_threads');
|
|
const os = require('os');
|
|
|
|
/*
|
|
Export a function that queues pending work.
|
|
*/
|
|
|
|
const queue = [];
|
|
exports.asyncQuery = (sql, ...parameters) => {
|
|
return new Promise((resolve, reject) => {
|
|
queue.push({
|
|
resolve,
|
|
reject,
|
|
message: { sql, parameters },
|
|
});
|
|
drainQueue();
|
|
});
|
|
};
|
|
|
|
/*
|
|
Instruct workers to drain the queue.
|
|
*/
|
|
|
|
let workers = [];
|
|
function drainQueue() {
|
|
for (const worker of workers) {
|
|
worker.takeWork();
|
|
}
|
|
}
|
|
|
|
/*
|
|
Spawn workers that try to drain the queue.
|
|
*/
|
|
|
|
os.cpus().forEach(function spawn() {
|
|
const worker = new Worker('./worker.js');
|
|
|
|
let job = null; // Current item from the queue
|
|
let error = null; // Error that caused the worker to crash
|
|
|
|
function takeWork() {
|
|
if (!job && queue.length) {
|
|
// If there's a job in the queue, send it to the worker
|
|
job = queue.shift();
|
|
worker.postMessage(job.message);
|
|
}
|
|
}
|
|
|
|
worker
|
|
.on('online', () => {
|
|
workers.push({ takeWork });
|
|
takeWork();
|
|
})
|
|
.on('message', (result) => {
|
|
job.resolve(result);
|
|
job = null;
|
|
takeWork(); // Check if there's more work to do
|
|
})
|
|
.on('error', (err) => {
|
|
console.error(err);
|
|
error = err;
|
|
})
|
|
.on('exit', (code) => {
|
|
workers = workers.filter(w => w.takeWork !== takeWork);
|
|
if (job) {
|
|
job.reject(error || new Error('worker died'));
|
|
}
|
|
if (code !== 0) {
|
|
console.error(`worker exited with code ${code}`);
|
|
spawn(); // Worker died, so spawn a new one
|
|
}
|
|
});
|
|
});
|
|
```
|