Skip to content

Instantly share code, notes, and snippets.

@sizovs
Last active January 1, 2024 08:13
Show Gist options
  • Save sizovs/5a6300e2b729304b86e5078c6278ce5f to your computer and use it in GitHub Desktop.
Save sizovs/5a6300e2b729304b86e5078c6278ce5f to your computer and use it in GitHub Desktop.
SQLite concurrent write test
// package.json
{
  "name": "stress-sqlite",
  "scripts": {
    "start": "node test.js"
  },
  "dependencies": {
    "better-sqlite3": "^9.2.2",
    "tmp": "^0.2.1"
  }
}
// test.js
const { Worker, workerData} = require('node:worker_threads');
const { cpus } = require('os');
const Database = require("better-sqlite3");

const CPU_COUNT = cpus().length;
const CONCURRENCY_LEVEL = CPU_COUNT;
const DATABASE_FILES = 4;
const TOTAL_RECORDS = 500000;

const tmp = require("tmp");
tmp.setGracefulCleanup();

const tmpDir = tmp.dirSync({
    prefix: 'stress-sqlite'
}).name;

async function run() {
    prepareDatabases();
    const dbFiles = dbFilesLoopingIterator();

    console.time("Insertions")
    const recordsPerWorker = Math.ceil(TOTAL_RECORDS / CONCURRENCY_LEVEL);
    const workers = [];
    for (let workerNo = 0; workerNo < CONCURRENCY_LEVEL; workerNo++) {
        const dbFile = dbFiles.next().value;
        const worker = runWorker(workerNo, recordsPerWorker, dbFile);
        workers.push(worker);
    }

    await Promise.all(workers);
    console.timeEnd("Insertions")
}


function prepareDatabases() {
    const dbFiles = dbFilesLoopingIterator();
    for (let i = 0; i < DATABASE_FILES; i++) {
        const dbFile = dbFiles.next().value;
        const db = new Database(dbFile);
        db.exec("PRAGMA journal_mode = WAL;");
        db.exec("CREATE TABLE IF NOT EXISTS data (info TEXT)")
    }
}

function* dbFilesLoopingIterator() {
    let current = 0;
    while (true) {
        yield tmpDir + '/' + current + '_database.db'
        current = (current + 1) % DATABASE_FILES;
    }
}

function runWorker(workerNo, numberOfWrites, dbFile) {
    return new Promise((resolve, reject) => {
        const worker = new Worker('./worker.js', {
            workerData: { workerNo, numberOfWrites, dbFile }
        });
        worker.on('exit', resolve);
        worker.on('error', reject);
    });
}



run().catch(err => console.error(err));
// worker.js
const { workerData } = require('worker_threads');
const Database = require("better-sqlite3");

const db = new Database(workerData.dbFile)
db.exec('pragma busy_timeout = 10000;');
db.exec("PRAGMA journal_mode = WAL;");

console.log(`Worker ${workerData.workerNo} writing ${workerData.numberOfWrites} items into ${workerData.dbFile}...`)
for (let i = 0; i < workerData.numberOfWrites; i++) {
    db.exec(`INSERT INTO data (info) VALUES (${i})`);
}

db.close();

Now run:

 npm start
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment