From 350671db4758890116fa6709be90189231958bdb Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 25 Sep 2024 14:31:01 -0500 Subject: [PATCH] DittoPglite: prevent starting PGlite instances in worker threads --- src/db/adapters/DittoPglite.ts | 19 +++++++++++++------ src/utils/worker.test.ts | 30 ++++++++++++++++++++++++++++++ src/utils/worker.ts | 9 +++++++++ 3 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 src/utils/worker.test.ts create mode 100644 src/utils/worker.ts diff --git a/src/db/adapters/DittoPglite.ts b/src/db/adapters/DittoPglite.ts index 2455fc37..6e4ae488 100644 --- a/src/db/adapters/DittoPglite.ts +++ b/src/db/adapters/DittoPglite.ts @@ -6,16 +6,23 @@ import { Kysely } from 'kysely'; import { DittoDatabase, DittoDatabaseOpts } from '@/db/DittoDatabase.ts'; import { DittoTables } from '@/db/DittoTables.ts'; import { KyselyLogger } from '@/db/KyselyLogger.ts'; +import { isWorker } from '@/utils/worker.ts'; export class DittoPglite { static create(databaseUrl: string, opts?: DittoDatabaseOpts): DittoDatabase { + const url = new URL(databaseUrl); + + if (url.protocol === 'file:' && isWorker()) { + throw new Error('PGlite is not supported in worker threads.'); + } + + const pglite = new PGlite(databaseUrl, { + extensions: { pg_trgm }, + debug: opts?.debug, + }); + const kysely = new Kysely({ - dialect: new PgliteDialect({ - database: new PGlite(databaseUrl, { - extensions: { pg_trgm }, - debug: opts?.debug, - }), - }), + dialect: new PgliteDialect({ database: pglite }), log: KyselyLogger, }); diff --git a/src/utils/worker.test.ts b/src/utils/worker.test.ts new file mode 100644 index 00000000..89845e2b --- /dev/null +++ b/src/utils/worker.test.ts @@ -0,0 +1,30 @@ +import { assertEquals } from '@std/assert'; + +import { isWorker } from '@/utils/worker.ts'; + +Deno.test('isWorker from the main thread returns false', () => { + assertEquals(isWorker(), false); +}); + +Deno.test('isWorker from a worker thread returns true', async () => { + const script = ` + import { isWorker } from '@/utils/worker.ts'; + postMessage(isWorker()); + self.close(); + `; + + const worker = new Worker( + URL.createObjectURL(new Blob([script], { type: 'application/javascript' })), + { type: 'module' }, + ); + + const result = await new Promise((resolve) => { + worker.onmessage = (e) => { + resolve(e.data); + }; + }); + + worker.terminate(); + + assertEquals(result, true); +}); diff --git a/src/utils/worker.ts b/src/utils/worker.ts new file mode 100644 index 00000000..ce94caee --- /dev/null +++ b/src/utils/worker.ts @@ -0,0 +1,9 @@ +/** + * Detect if this thread is running in a Worker context. + * + * https://stackoverflow.com/a/18002694 + */ +export function isWorker(): boolean { + // @ts-ignore This is fine. + return typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope; +}