This document describes the implementation of Node.js worker thread support for @photostructure/sqlite.
Worker thread support is fully implemented and working in @photostructure/sqlite. The implementation provides complete thread safety through per-worker instance data management and thread validation, ensuring that database connections cannot be shared between threads.
SQLite supports three threading modes:
SQLITE_THREADSAFE=0): No thread safety, one thread onlySQLITE_THREADSAFE=2): Multiple threads, separate connections per thread ✅ CURRENTSQLITE_THREADSAFE=1): Full thread safety with mutexes (default)Current Configuration: Using SQLITE_THREADSAFE=2 (multi-thread mode) - the correct choice for worker threads where each thread gets its own database connection.
napi_set_instance_data in src/binding.cppCleanupAddonData for worker terminationWhat Was Fixed:
The root cause was static Napi::FunctionReference constructors that were shared across all worker threads but belonged to the main thread's V8 isolate. When worker threads tried to access these static constructors, it caused the "HandleScope::HandleScope Entering the V8 API without proper locking in place" error.
The Solution:
Each worker thread has its own AddonData instance managed through N-API:
struct AddonData {
Napi::FunctionReference database_sync_constructor;
Napi::FunctionReference statement_sync_constructor;
Napi::FunctionReference statement_sync_iterator_constructor;
Napi::FunctionReference session_constructor;
DatabaseMap database_map;
std::mutex database_map_mutex;
};
This data is:
napi_set_instance_dataCleanupAddonData when the worker terminatesEach database and statement object tracks its creation thread:
class DatabaseSync {
std::thread::id creation_thread_;
void ValidateThread() {
if (std::this_thread::get_id() != creation_thread_) {
throw Napi::Error("Database objects cannot be used from different thread");
}
}
};
This prevents cross-thread usage and provides clear error messages when violations occur
Connection Isolation Pattern (Recommended):
sqlite3* handles between threadsImplementation Pattern:
// main.js
const { Worker } = require("worker_threads");
const worker = new Worker("./worker.js", {
workerData: { dbPath: "./data.db" },
});
// worker.js
const { DatabaseSync } = require("@photostructure/sqlite");
const { workerData } = require("worker_threads");
// Each worker creates its own connection
const db = new DatabaseSync(workerData.dbPath);
// ... perform operations
db.close(); // Clean up when done
Per-Worker Instance Data:
struct WorkerData {
napi_env env;
std::vector<DatabaseSync*> databases;
std::vector<StatementSync*> statements;
};
Cleanup Sequence:
Reference Management:
Cross-Thread Usage Detection:
class DatabaseSync {
std::thread::id creation_thread_;
void ValidateThread() {
if (std::this_thread::get_id() != creation_thread_) {
throw std::runtime_error("Database connection cannot be used from different thread");
}
}
};
Error Categories:
Basic Functionality:
Concurrency Tests:
Error Scenarios:
Memory Tests:
test/worker-threads-simple.test.ts - Basic functionality teststest/worker-threads-simple-init.test.ts - Worker initialization teststest/worker-threads-initialization.test.ts - Module initialization in workerstest/worker-threads-error.test.ts - Error handling and cross-thread validation testsnapi_set_instance_data for per-worker statenapi_add_env_cleanup_hook for proper cleanupNapi::ThreadSafeFunction for callbackssrc/binding.cpp - Add instance data management and cleanup hookssrc/sqlite_impl.h - Add thread ID tracking and validation declarationssrc/sqlite_impl.cpp - Implement thread validation and cleanup logictest/worker-threads-simple.test.ts - Basic worker thread functionalitytest/worker-threads-simple-init.test.ts - Worker initialization teststest/worker-threads-initialization.test.ts - Module initialization in workerstest/worker-threads-error.test.ts - Error handling and cross-thread validationREADME.md - Add worker thread examplesdocs/WORKER-THREADS.md - Create detailed usage guideCLAUDE.md - Update with worker thread implementation notesbinding.gyp - Ensure proper threading flags are setpackage.json - Update test scripts for worker thread testingWhile worker thread support is fully functional, potential future enhancements could include:
Thread-Safe Function support
Napi::ThreadSafeFunction for cross-thread callbacksWorker pool utilities
Performance optimizations
Additional stress testing