Skip to content

Commit 6dbc9c5

Browse files
authored
⚡️ perf: move pglite into web worker (lobehub#5500)
* move pglite into web worker * fix turbopack build * fix turbopack build * improve code
1 parent 9b38c9c commit 6dbc9c5

File tree

5 files changed

+92
-13
lines changed

5 files changed

+92
-13
lines changed

next.config.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -165,8 +165,8 @@ const nextConfig: NextConfig = {
165165
source: '/welcome',
166166
},
167167
],
168-
serverExternalPackages: ['@electric-sql/pglite', 'sharp'],
169-
168+
// when external packages in dev mode with turbopack, this config will lead to bundle error
169+
serverExternalPackages: isProd ? ['@electric-sql/pglite'] : undefined,
170170
transpilePackages: ['pdfjs-dist', 'mermaid'],
171171

172172
webpack(config) {

src/database/client/db.ts

+42-11
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { PgliteDatabase } from 'drizzle-orm/pglite';
1+
import { PgliteDatabase, drizzle } from 'drizzle-orm/pglite';
22
import { Md5 } from 'ts-md5';
33

44
import { ClientDBLoadingProgress, DatabaseLoadingState } from '@/types/clientDB';
@@ -28,6 +28,12 @@ export class DatabaseManager {
2828
private static WASM_CDN_URL =
2929
'https://registry.npmmirror.com/@electric-sql/pglite/0.2.13/files/dist/postgres.wasm';
3030

31+
private static FSBUNDLER_CDN_URL =
32+
'https://registry.npmmirror.com/@electric-sql/pglite/0.2.13/files/dist/postgres.data';
33+
34+
private static VECTOR_CDN_URL =
35+
'https://registry.npmmirror.com/@electric-sql/pglite/0.2.13/files/dist/vector.tar.gz';
36+
3137
private constructor() {}
3238

3339
static getInstance() {
@@ -88,6 +94,12 @@ export class DatabaseManager {
8894
return WebAssembly.compile(wasmBytes);
8995
}
9096

97+
private fetchFsBundle = async () => {
98+
const res = await fetch(DatabaseManager.FSBUNDLER_CDN_URL);
99+
100+
return await res.blob();
101+
};
102+
91103
// 异步加载 PGlite 相关依赖
92104
private async loadDependencies() {
93105
const start = Date.now();
@@ -100,7 +112,7 @@ export class DatabaseManager {
100112
PGlite: m.PGlite,
101113
})),
102114
import('@electric-sql/pglite/vector'),
103-
import('drizzle-orm/pglite'),
115+
this.fetchFsBundle(),
104116
];
105117

106118
let loaded = 0;
@@ -125,9 +137,9 @@ export class DatabaseManager {
125137
});
126138

127139
// @ts-ignore
128-
const [{ PGlite, IdbFs, MemoryFS }, { vector }, { drizzle }] = results;
140+
const [{ PGlite, IdbFs, MemoryFS }, { vector }, fsBundle] = results;
129141

130-
return { IdbFs, MemoryFS, PGlite, drizzle, vector };
142+
return { IdbFs, MemoryFS, PGlite, fsBundle, vector };
131143
}
132144

133145
// 数据库迁移方法
@@ -177,17 +189,34 @@ export class DatabaseManager {
177189
this.callbacks?.onStateChange?.(DatabaseLoadingState.Initializing);
178190

179191
// 加载依赖
180-
const { PGlite, vector, drizzle, IdbFs, MemoryFS } = await this.loadDependencies();
192+
const { fsBundle, PGlite, MemoryFS, IdbFs, vector } = await this.loadDependencies();
181193

182194
// 加载并编译 WASM 模块
183195
const wasmModule = await this.loadWasmModule();
184196

185-
const db = new PGlite({
186-
extensions: { vector },
187-
fs: typeof window === 'undefined' ? new MemoryFS('lobechat') : new IdbFs('lobechat'),
188-
relaxedDurability: true,
189-
wasmModule,
190-
});
197+
const { initPgliteWorker } = await import('./pglite');
198+
199+
let db: typeof PGlite;
200+
201+
const dbName = 'lobechat';
202+
203+
// make db as web worker if worker is available
204+
if (typeof Worker !== 'undefined') {
205+
db = await initPgliteWorker({
206+
dbName,
207+
fsBundle: fsBundle as Blob,
208+
vectorBundlePath: DatabaseManager.VECTOR_CDN_URL,
209+
wasmModule,
210+
});
211+
} else {
212+
// in edge runtime or test runtime, we don't have worker
213+
db = new PGlite({
214+
extensions: { vector },
215+
fs: typeof window === 'undefined' ? new MemoryFS(dbName) : new IdbFs(dbName),
216+
relaxedDurability: true,
217+
wasmModule,
218+
});
219+
}
191220

192221
this.dbInstance = drizzle({ client: db, schema });
193222

@@ -210,6 +239,8 @@ export class DatabaseManager {
210239
name: error.name,
211240
stack: error.stack,
212241
});
242+
243+
console.error(error);
213244
throw error;
214245
}
215246
})();

src/database/client/pglite.ts

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { PGliteWorker } from '@electric-sql/pglite/worker';
2+
3+
import { InitMeta } from './type';
4+
5+
export const initPgliteWorker = async (meta: InitMeta) => {
6+
const worker = await PGliteWorker.create(
7+
new Worker(new URL('pglite.worker.ts', import.meta.url)),
8+
{ meta },
9+
);
10+
11+
// 监听 worker 状态变化
12+
worker.onLeaderChange(() => {
13+
console.log('Worker leader changed, isLeader:', worker?.isLeader);
14+
});
15+
16+
return worker as PGliteWorker;
17+
};

src/database/client/pglite.worker.ts

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { worker } from '@electric-sql/pglite/worker';
2+
3+
import { InitMeta } from './type';
4+
5+
worker({
6+
async init(options) {
7+
const { wasmModule, fsBundle, vectorBundlePath, dbName } = options.meta as InitMeta;
8+
const { PGlite } = await import('@electric-sql/pglite');
9+
10+
return new PGlite({
11+
dataDir: `idb://${dbName}`,
12+
extensions: {
13+
vector: {
14+
name: 'pgvector',
15+
setup: async (pglite, options) => {
16+
return { bundlePath: new URL(vectorBundlePath), options };
17+
},
18+
},
19+
},
20+
fsBundle,
21+
relaxedDurability: true,
22+
wasmModule,
23+
});
24+
},
25+
});

src/database/client/type.ts

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export interface InitMeta {
2+
dbName: string;
3+
fsBundle: Blob;
4+
vectorBundlePath: string;
5+
wasmModule: WebAssembly.Module;
6+
}

0 commit comments

Comments
 (0)