diff --git a/src/db.ts b/src/db.ts index c5a3e17..a722d4e 100644 --- a/src/db.ts +++ b/src/db.ts @@ -4,7 +4,7 @@ import path from 'node:path'; import { FileMigrationProvider, Kysely, Migrator, PolySqliteDialect } from '@/deps.ts'; import { Conf } from '@/config.ts'; import { getPragma, setPragma } from '@/pragma.ts'; -import { sqliteWorker } from '@/workers.ts'; +import SqliteWorker from '@/workers/sqlite.ts'; interface DittoDB { events: EventRow; @@ -57,8 +57,8 @@ interface UnattachedMediaRow { uploaded_at: Date; } -await sqliteWorker.ready; -await sqliteWorker.open(); +const sqliteWorker = new SqliteWorker(); +await sqliteWorker.open(Conf.dbPath); const db = new Kysely({ dialect: new PolySqliteDialect({ diff --git a/src/workers.ts b/src/workers.ts deleted file mode 100644 index 291217e..0000000 --- a/src/workers.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { Conf } from '@/config.ts'; -import SqliteWorker from '@/workers/sqlite.ts'; - -const sqliteWorker = new SqliteWorker(Conf.dbPath); - -export { sqliteWorker }; diff --git a/src/workers/sqlite.ts b/src/workers/sqlite.ts index 0a6f35f..a6d2fac 100644 --- a/src/workers/sqlite.ts +++ b/src/workers/sqlite.ts @@ -1,15 +1,18 @@ +import { Comlink } from '@/deps.ts'; + +import type { SqliteWorker as _SqliteWorker } from './sqlite.worker.ts'; import type { CompiledQuery, QueryResult } from '@/deps.ts'; class SqliteWorker { - #path: string; #worker: Worker; - ready: Promise; + #client: ReturnType>; + #ready: Promise; - constructor(path: string) { - this.#path = path; + constructor() { this.#worker = new Worker(new URL('./sqlite.worker.ts', import.meta.url).href, { type: 'module' }); + this.#client = Comlink.wrap(this.#worker); - this.ready = new Promise((resolve) => { + this.#ready = new Promise((resolve) => { const handleEvent = (event: MessageEvent) => { if (event.data[0] === 'ready') { this.#worker.removeEventListener('message', handleEvent); @@ -20,37 +23,18 @@ class SqliteWorker { }); } - async open(): Promise { - await this.ready; - return this.#call(['open', [this.#path]]); + async open(path: string): Promise { + await this.#ready; + return this.#client.open(path); } - async executeQuery({ sql, parameters }: CompiledQuery): Promise> { - await this.ready; - return this.#call(['query', [sql, parameters]]); + async executeQuery(query: CompiledQuery): Promise> { + await this.#ready; + return this.#client.executeQuery(query) as Promise>; } - #call(msg: [string, unknown[]]): Promise { - const id = crypto.randomUUID(); - - this.#worker.postMessage([id, msg]); - - // TODO: use a hashmap instead of an event listener for better performance. - return new Promise((resolve) => { - const handleEvent = (event: MessageEvent<[string, T]>) => { - const [_id, result] = event.data; - if (_id === id) { - this.#worker.removeEventListener('message', handleEvent); - resolve(result); - } - }; - this.#worker.addEventListener('message', handleEvent); - }); - } - - // deno-lint-ignore require-await - async destroy() { - this.#worker.terminate(); + destroy(): Promise { + return this.#client.destroy(); } } diff --git a/src/workers/sqlite.worker.ts b/src/workers/sqlite.worker.ts index 69acf6f..c79f078 100644 --- a/src/workers/sqlite.worker.ts +++ b/src/workers/sqlite.worker.ts @@ -1,38 +1,26 @@ /// -import { DenoSqlite3 } from '@/deps.ts'; +import { Comlink, type CompiledQuery, DenoSqlite3, type QueryResult } from '@/deps.ts'; -let db: DenoSqlite3; +let db: DenoSqlite3 | undefined; -type Msg = - | ['open', [string]] - | ['query', [string, unknown[]]]; +export const SqliteWorker = { + open(path: string): void { + db = new DenoSqlite3(path); + }, + executeQuery({ sql, parameters }: CompiledQuery): QueryResult { + if (!db) throw new Error('Database not open'); + return { + rows: db.prepare(sql).all(...parameters as any[]) as R[], + numAffectedRows: BigInt(db.changes), + insertId: BigInt(db.lastInsertRowId), + }; + }, + destroy() { + db?.close(); + }, +}; -function call([cmd, args]: Msg) { - switch (cmd) { - case 'open': - return handleOpen(args[0]); - case 'query': - return handleQuery(args[0], args[1]); - } -} - -function handleOpen(path: string): void { - db = new DenoSqlite3(path); -} - -function handleQuery(sql: string, params: any[] = []) { - return { - rows: db.prepare(sql).all(...params), - numAffectedRows: BigInt(db.changes), - insertId: BigInt(db.lastInsertRowId), - }; -} - -self.addEventListener('message', (event: MessageEvent<[string, Msg]>) => { - const [id, msg] = event.data; - const result = call(msg); - self.postMessage([id, result]); -}); +Comlink.expose(SqliteWorker); self.postMessage(['ready']);