prisma wasm のビルドサイズの調査
prisma を cloudflare d1 で動かしたい。その調査
prismaの qruey-engine の wasm build ができた。
d1 用のアダプタがまだ存在しないので、sqlite のクエリエンジン部分をどう動かすかを考える。
手元でビルドする方法
$ git clone https://github.com/prisma/prisma-engines
$ cd prisma-engines
$ cargo build
$ cd query-engine/query-engine-wasm
$ bash build.sh # pkg/query-engine-bg.wasm
現状3.0MB
とりあえず手元で動いた。
/**
* Run with: `node --experimental-wasm-modules ./example.js`
* on Node.js 18+.
*/
import { readFile } from 'fs/promises'
import { PrismaLibSQL } from '@prisma/adapter-libsql'
import { createClient } from '@libsql/client'
import { bindAdapter } from '@prisma/driver-adapter-utils'
import { QueryEngine, getBuildTimeInfo } from '@prisma/query-engine-wasm';
async function main() {
// Always initialize the Wasm library before using it.
// This sets up the logging and panic hooks.
const client = createClient({ url: "file:./prisma/dev.db" })
const adapter = new PrismaLibSQL(client)
const driverAdapter = bindAdapter(adapter)
console.log('buildTimeInfo', getBuildTimeInfo())
const datamodel = await readFile('prisma/schema.prisma', 'utf8')
const options = {
datamodel,
logLevel: 'info',
logQueries: true,
datasourceOverrides: {},
env: process.env,
configDir: '/tmp',
ignoreEnvVarErrors: true,
}
const callback = () => { console.log('log-callback') }
const queryEngine = new QueryEngine(options, callback, driverAdapter)
await queryEngine.connect('trace')
const created = await queryEngine.query(JSON.stringify({
modelName: 'User',
action: 'createOne',
query: {
arguments: {
data: {
id: 1235,
},
},
selection: {
$scalars: true
}
}
}), 'trace')
console.log({ created })
const res = await queryEngine.query(JSON.stringify({
modelName: 'User',
action: 'findMany',
query: {
arguments: {},
selection: {
$scalars: true
}
}
}), 'trace')
const parsed = JSON.parse(res);
console.log('query result = ')
console.dir(parsed, { depth: null })
const error = parsed.errors?.[0]?.user_facing_error
if (error?.error_code === 'P2036') {
console.log('js error:', driverAdapter.errorRegistry.consumeError(error.meta.id))
}
// console.log('before disconnect')
await queryEngine.disconnect('trace')
// console.log('after disconnect')
// console.log('before free')
queryEngine.free()
// console.log('after free')
}
main()
d1 用にビルドサイズを減らすためのアイデア
現状 3.0MB
prisma.schema のパースとバリデーションを追い出す
スキーマのバリデーションだけでビルドしてみると、660k。
runtime では事前にチェックしてあるとして、シリアライズ済みの schema を受け取れば 660kb のうちいくらかは減るはず。
// src/wasm/engine.rs
// QueryEngine をコメントアウトして次のコードでビルド
#[wasm_bindgen]
pub async fn init0(options: ConstructorOptions, adapter: JsObject) -> Result<(), wasm_bindgen::JsError> {
let ConstructorOptions {
datamodel,
log_level,
log_queries,
engine_protocol,
} = options;
let connector_registry: ConnectorRegistry<'_> = &[SQLITE];
let mut schema = psl::parse_without_validation(datamodel.into(), connector_registry);
// --- 660k
let js_queryable = driver_adapters::from_js(adapter);
// js_queryable.instrument(span)
// --- 661k
sql_connector::activate_driver_adapter(Arc::new(js_queryable));
// --- 1.0MB
let enable_tracing = false; // config.preview_features().contains(PreviewFeature::Tracing);
let engine_protocol = engine_protocol.unwrap_or(EngineProtocol::Json);
// --- 1.0MB
}
おそらく psl パッケージ
d1 専用ビルド(sqlite only)
実装に mssql, mongodb, mysql が入ってきているので、sqlite 以外の実装を落とす。
そもそも現状は cloudflare d1 用のアダプタがないので、それごと作る必要がある。
bindAdapter()
でブリッジを生成している部分を d1 用に差し替えてやる必要がある?
そもそも query builder だけを取り出す
d1 でほしいのは prisma の型定義ファイルから生成される json query => sql への変換部分。
queryable.build_query がそれっぽく見えるのだが、自信がない。
tokio => wasm_bindgen_features
非同期ランタイムに tokio を使っているが、wasm 環境なら spawn_local でなんとかなる。
ビルドサイズは3MB なので workers の bundled ($5/m)にのせることはできる。
が、 d1 で動かすにはアダプタが足りない。これは mongo adapter あたりを参考にすると動く?
そもそも prisma の吐く sqlite が d1 でパースできるかの問題があるので、beta リリースされたらまた考える。