初稿撰写于2024年11月29日。
みなさん、久しぶりっ!
今年这边家庭发生了不太好的事情,我自己没心思研究技术,也就放置了博客很久。现在大概从阴霾走出来了,就写一些最近的尝试成果吧。
前段时间获知electric-sql/pglite这个项目用WASM运行PostgreSQL数据库,并且(初步?)支持了Deno,那我就想试试看在本地环境下体验究竟如何。
至于为什么是本地环境而非web app,原因是希望尽快尝试,而且持久化到本地的数据库可以通过Deno CLI方便地操作。
环境配置
没什么特别的,主要就是现在使用的是Deno 2:
> deno --version
deno 2.1.1 (stable, release, x86_64-pc-windows-msvc)
v8 13.0.245.12-rusty
typescript 5.6.2
项目结构
在PowerShell 7.4.6用deno新建工程,并添加所需package:
> deno init C:\prj\pg_test
> Set-Location C:\prj\pg_test
> deno add npm:@electric-sql/pglite
Add npm:@electric-sql/[email protected]
> tree . /F
フォルダー パスの一覧: ボリューム Windows
ボリューム シリアル番号は D8C4-41B5 です
C:\PRJ\PG_TEST
│ deno.json
│ deno.lock
│ main.ts
│ main_test.ts
│
└─.vscode
settings.json
deno.json内容如下:
{
"tasks": {
"dev": "deno run --watch main.ts"
},
"imports": {
"@electric-sql/pglite": "npm:@electric-sql/pglite@^0.2.14",
"@std/assert": "jsr:@std/assert@1"
}
}
本体实现
main.ts内容如下:
import { PGlite } from "@electric-sql/pglite";
const PG_DATA_PATH = './pgdata.tar';
export async function init() {
const pg = new PGlite();
await pg.exec(`
CREATE TABLE IF NOT EXISTS cashbook (
id SERIAL PRIMARY KEY
, act_on TIMESTAMP WITH TIME ZONE
, item TEXT
, cash BIGINT
, note TEXT
);
`);
const file = await pg.dumpDataDir('none'); // should not compress
await Deno.writeFile(PG_DATA_PATH, file.stream());
await pg.close();
}
// Learn more at https://docs.deno.com/runtime/manual/examples/module_metadata#concepts
if (import.meta.main) {
console.log('Checking DB...');
try {
await Deno.lstat(PG_DATA_PATH);
console.log('Exists!');
} catch (err) {
if (!(err instanceof Deno.errors.NotFound)) {
throw err;
}
console.log('Not exists!');
console.log('Initializing...');
await init();
console.log('Done!');
}
const arr = await Deno.readFile(PG_DATA_PATH);
const pg = new PGlite({
loadDataDir: new Blob([arr])
});
const tables = await pg.query(`
SELECT *
FROM information_schema.tables
WHERE table_schema = 'public';
`);
console.log(tables.rows);
await pg.close();
}
大体流程如下:
- 优先寻找特定的已持久化数据库,若没有则新建
- 载入数据库
- 简单检查数据库
由于现在PGlite对Deno的支持不甚完善,现在使用最低限度的实现方式,即:
- 先在内存中建立数据库,最后持久化到本地。
- 持久化时,为避免默认的gzip压缩与解压带来额外的复杂度,故直接持久化为tar文件。
建表时的字段名参考自此。
初次执行(以✅起始的输出即权限要求通过时的提示。下同):
> deno run .\main.ts
Checking DB...
✅ Granted all read access.
Not exists!
Initializing...
✅ Granted all write access.
Done!
[
{
table_catalog: "template1",
table_schema: "public",
table_name: "cashbook",
table_type: "BASE TABLE",
self_referencing_column_name: null,
reference_generation: null,
user_defined_type_catalog: null,
user_defined_type_schema: null,
user_defined_type_name: null,
is_insertable_into: "YES",
is_typed: "NO",
commit_action: null
}
]
现在数据库就被持久化好了:
> Get-ChildItem .\pgdata.tar
Directory: C:\prj\pg_test
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a--- 2024/11/30 0:30 28258304 pgdata.tar
再次执行:
> deno run .\main.ts
Checking DB...
✅ Granted all read access.
Exists!
[
{
table_catalog: "template1",
table_schema: "public",
table_name: "cashbook",
table_type: "BASE TABLE",
self_referencing_column_name: null,
reference_generation: null,
user_defined_type_catalog: null,
user_defined_type_schema: null,
user_defined_type_name: null,
is_insertable_into: "YES",
is_typed: "NO",
commit_action: null
}
]
对我来说现在这样就可以了。之后的事情就可以自己用Deno CLI折腾了。
后记
时过境迁,用WASM能完成的事颇有些超乎想象。感叹技术进步的同时,自己却不得不持续学习。不要停下来!
那么就到这里,以后看看还能玩什么再跟大家分享。
2025-03-02
之前上网冲浪找到了这么一个实现:Deno + Pglite + Drizzle で依存の少ないDBアプリを作る
至于Drizzle,它是个ORM。我理应也要找个ORM用来着,以免维护困难。这位仁兄提供了更完整的思路,值得学习。