Cloudflare Workersで個人用のちょっとしたワークロードを組んでいるのだが、開発中にちょっと困ったことがあったので、対応した内容をここにメモで載せる。
いくつかのパッケージがCloudflare Workers上で(nodejs_compatでも)使用できない
もともとLambdaで動かしていたワークロードをCloudflare Workersにそのまま移植したものがあったのだが、これの一部処理で使っていたパッケージが動作しなかった。
sharp
sharp
はCloudflare Workerのランタイム上で使えないっぽい。「ぽい」というのは、実際にdeployして試したというよりローカルでwrangler dev
実行してエラーになったことからそうだと判断した。もちろんwrangler.toml
にnodejs_compat
は設定済である。wrangler dev
すると以下のようなエラーが出て、wrangler
が起動しない。
✘ [ERROR] service core:user:hogehoge: Uncaught Error: [unenv] process.report.getReport is not implemented yet!
at null.<anonymous> (index.js:50:10) in createNotImplementedError
at null.<anonymous> (index.js:54:11) in fn
at null.<anonymous> (index.js:51387:36) in getReport
at null.<anonymous> (index.js:51962:23) in familyFromReport
at null.<anonymous> (index.js:52035:21) in familySync
at null.<anonymous> (index.js:52045:73) in isNonGlibcLinuxSync
at null.<anonymous> (index.js:53653:63) in runtimeLibc
at null.<anonymous> (index.js:53654:82) in runtimePlatformArch
at null.<anonymous> (index.js:53805:27) in node_modules/sharp/lib/sharp.js
at null.<anonymous> (index.js:20:50) in __require2
✘ [ERROR] The Workers runtime failed to start. There is likely additional logging output above.
jsdom
これも↑と同じ。jsdom
はCloudflare Workersでは使えない。こっちはワンチャンCloudflare Workers上だと動いたりしないかなと思って思い切ってDeployしてみたが、同じエラーでDeployに失敗した。なのでwrangler
でダメなものはCloudflare Workersでもダメ、なんだと思う。
▲ [WARNING] Import "default" will always be undefined because there is no matching export in "node_modules/unenv/dist/runtime/npm/whatwg-url.mjs" [import-is-undefined]
required-unenv-alias:whatwg-url:6:27:
6 │ "default" in esm ? esm.default : {}
╵ ~~~~~~~
[wrangler-UserWorker:wrn] Miniflare 3 does not currently trigger scheduled Workers automatically.
Use `--test-scheduled` to forward fetch triggers.
⎔ Starting local server...
✘ [ERROR] service core:user:hogehoge: Uncaught Error: [unenv] whatwg-url.parseURL is not implemented yet!
at null.<anonymous> (index.js:59:10) in createNotImplementedError
at null.<anonymous> (index.js:64:27) in fn
at null.<anonymous> (index.js:130676:34) in DocumentImpl
at null.<anonymous> (index.js:131400:16) in exports.setup
at null.<anonymous> (index.js:131350:22) in exports.create
at null.<anonymous> (index.js:131353:31) in exports.createImpl
at null.<anonymous> (index.js:134911:24) in exports.createImpl
at null.<anonymous> (index.js:134914:37) in exports.createWrapper
at null.<anonymous> (index.js:188912:37) in installOwnProperties
at null.<anonymous> (index.js:188826:7) in exports.createWindow
✘ [ERROR] The Workers runtime failed to start. There is likely additional logging output above.
scheduler云々のエラーが出ているのはこの処理がCronトリガーで呼び出されるものだったためである。
Cronトリガーでちょっとハマった
CloudflareのCronトリガーはWeekdayの部分の規定値が"1-7"になっており、一般的には(というか私の理解がそうだったというだけなのだが)"0-7"が規定値だと思い込んでいたので、ちょっとハマった。Weekdayに規定外の値、たとえばここで言うと"0"を含めてDeployすると、エラーになってDeployが失敗する。ちなみに以下のようなエラーが出てくる:
...
X [ERROR] A request to the Cloudflare API failed
invalid cron string [code: 10100]
...
厄介なのは、ローカルでwrangler dev --test-scheduled
で試している分には、このエラーは出てこないことだ。これでcurl "http://localhost:8787/__scheduled?cron=0+12+*+*+0"
とかやってもこのエラーが起きず正常動作するので、何が原因なのか最初ちょっとよくわからなかった。つまりローカルで--test-scheduled
つけて動かしたときの動作は、パラメータに渡した文字列をそのまま素通りでアプリに回してるだけで、Cron式のvalidationなんか一切していないんだと思われる。極論、cron=aaa
とかでも動くんだろう(試してないけど)。ちなみにCloudflare WorkersのCron式のWeekdayは、"1"が日曜日、"2"が月曜日、…で"7"が土曜日である。"0"を許容するCron式の一般的な体系(?)では、"0"と"7"が日曜日で、"1"が月曜日、…、"6"が土曜日、となるので、一般的なCron式のWeekdayの体系とCloudflareのCron式は数値と曜日が1つずつズレていることになる。わかりづらい。なんで特殊な体系にしてしまったのだ?
指定したVersionのPrismaがインストールされない(のでDBに接続できずエラー)
これは(おそらく)Cloudflare WorkersとPrismaの複合的な問題(?)である。最初に発見したのは4/10 18:30JST頃のDeployだが、このときDeployのログに以下のメッセージを見つけた
npm warn exec The following package was not found and will be installed: prisma@6.6.0
このときpackage.json
に指定していたPrisma関連のパッケージはおおむね6.4.1を指定していた
"@prisma/adapter-pg-worker": "6.4.1",
"@prisma/client": "6.4.1",
"@prisma/pg-worker": "6.4.1",
で、どうも6.6.0からPrismaClientの生成方法が変わってるらしく、6.4.1までなら以下の実装でいけたんだが
import { Prisma, PrismaClient, } from "@prisma/client"
import { Pool, PoolConfig } from "@prisma/pg-worker";
import { PrismaPg } from "@prisma/adapter-pg-worker";
...
const databaseUrl = String(process.env.DATABASE_URL);
const schema = new URLSearchParams(new URL(databaseUrl).searchParams).get('schema');
const pool = new Pool({
connectionString: databaseUrl,
});
const adapter = new PrismaPg(pool, { schema: schema || 'public' });
const prisma = new PrismaClient({
adapter,
});
6.6.0だとこれではコンパイルエラーになる(adapter
の生成部分がエラーになる)ようで、以下に変更が必要になった
import { Prisma, PrismaClient, } from "@prisma/client"
import { Pool, PoolConfig } from "@prisma/pg-worker";
import { PrismaPg } from "@prisma/adapter-pg-worker";
...
const databaseUrl = String(process.env.DATABASE_URL);
const schema = new URLSearchParams(new URL(databaseUrl).searchParams).get('schema');
const config:PoolConfig = {
connectionString: databaseUrl,
};
const adapter = new PrismaPg(config, {schema: schema || 'public'});
const prisma = new PrismaClient({
adapter,
});
要するにPrismaPg
の第一引数にnew Pool({connectionString: hogehoge})
を渡すか(~6.4.1)、{connectionString: hogehoge}
を渡すか(6.6.0~)の違いである。この{connectionString: hogehoge}
の部分がPoolConfigというnode-pgのInterfaceになっていて、いちいちPool
オブジェクト生成しなくてもよくなったということのようだ。が、こういうちょっとしたことでも破壊的変更はしないでほしかったというのが個人的にな正直な感想である。これのせいでDBにつながるワークロードはすべて全滅してしまい軽く阿鼻叫喚に陥った。
この件は、Prisma関連のパッケージのバージョンを6.4.1->6.6.0にアップデートして、コードを上記の内容に修正したうえでDeployし、とりあえず復旧させた。
一方、上のログメッセージの通りなら「6.4.1が見つからないから代わりに6.6.0インストールするね」ということらしいが、npm view
やっても以下の通りちゃんとバージョン返ってくるので、こんなこと言われる筋合いはないはずなのである
$ npm view @prisma/adapter-pg-worker@6.4.1 version
6.4.1
かつ、不思議なのは、このログメッセージは、Prisma関連のパッケージを6.6.0にアップデートした後でも出てくることである。もう6.4.1はpackage.json
には存在してないのにも関わらず、である。キャッシュかな?Cloudflare WorkersのDISCORDを検索してみたが同じことに悩んでる人はいなかった。ので、特殊な事情なのかもしれない。Cloudflare Workersがビルドのときに個別のパッケージレジストリ使ってるとか、そういうことなんだろうか?