個人で運用しているサービスをNeonからCloudflare D1へ移行した

個人で運用しているサービスでNeonを利用していたが、そこそこ値段が高くなってきたのでCloudflareのD1へ移行しました。

運用物の前提

  • Cloudflare Workers上で動く配信系のキュレーションサイト/管理画面/Discord Bot(500 Server~)

移行前のざっくり構成図は↓

関連記事

blog.sugar-cat.dev

モチベーション

NeonのLaunchプランで月$40~$50前後かかっていました。(Compute CU-hour課金) 大した額ではないですが、データ量に対して額が徐々に増えてきていたので、早いうちに手を打っといたほうが良いなーというところで移行を考え始めました。

https://neon.com/pricing

移行先としてはサーバーのスタックを全てCloudflareに寄せていたこともあり、Internalで完結できるD1を第一候補で考えました。

zenn.dev

D1のWorkers Paidプランは月次無料枠で25 billion rows read50 million rows written5 GB storageです。

Neonを運用していたときはの時のDBはtup_returned1年で20億程度だったので、雑に平均して550万行/日~くらいのD1でのスキャン量になりそうかなという見積もりでした。 自分の規模なら工夫すればWorker Paidのプランであれば実質無料で利用できそうです(???)

developers.cloudflare.com

D1の制約と対応

項目 Workers Paid
Max database size 10 GB(ハードリミット)
Max bound parameters per query 100
Queries per Worker invocation 1,000
Max SQL query duration 30 sec

developers.cloudflare.com

物理構成と分散配置

D1は単一プライマリ + 非同期レプリカの構成で、プライマリの物理リージョンはcreate時に決まり、以後は動かません。候補は6リージョン(wnam / enam / weur / eeur / apac / oc)で、自分は主要ユーザーが日本なのでapacを指定しました。

developers.cloudflare.com

ただしapacを指定したからといって正確にその範囲に置かれるわけではなく、location hintは候補リージョンへのbest-effortフォールバックという扱いになるので本当にapacにあるのかはわかりません。

Providing a location hint does not guarantee that D1 runs in your preferred location. Instead, it will run in the nearest possible location (by latency) to your preference.

developers.cloudflare.com

10 GBのハードリミットがあるので、公式はDBを分割して利用するように推奨されています。

D1 is designed for horizontal scale out across multiple, smaller (10 GB) databases, such as per-user, per-tenant or per-entity databases.

developers.cloudflare.com

なので今回はドメインごとにD1のDatabaseを複数に分離してテーブルなどの再設計を行いました。(省略)

D1移行で対応したこと

  • 1DBあたり10 GBのハードリミット

この制約は結構でかいので、日時で増えていくデータに対してはretention policyを定めて運用をするようにしました。(D1は参照頻度の高い直近分だけ持たせ他はR2へ逃す感じ)

ちなみに無料枠は5 GB、超過分は$0.75/GB-moの従量課金で、これは10 GBのハードリミットとは別軸。10 GB未満であれば追加課金だけで済みます。

  • bound parameters100個上限

SQLite一般のSQLITE_MAX_VARIABLE_NUMBERは999〜32766なので、数千行規模のbatch insertを感覚で書いていたところtoo many SQL variables相当のエラーで落ちたので、アプリでよしなに分割してInsertをしています。

www.sqlite.org

  • その他
    • Miniflare等を活用し事前にワークフローレベルのIntegration Testを構築し、D1に切り替えたとて振る舞いが変わらないことを検証
    • 対話型トランザクションはD1にないのでbatchに寄せる
    • UUID採番はアプリ側、DBトリガー依存は排除する
    • パフォーマンス劣化が起きないかの確認(そもそもNeonがシンガポールで運用していて対して速度出ていなかったのでそれより悪くはならんやろという前提の元、ザル計測で済ませた、同等以上のパフォーマンスはでてるはず、割と最近)

移行後Hyperdriveが不要になり、アプリ側はD1Databasebinding 1本で読み書き両用の構成に変わりました。コストは月$50前後からWorkers Paid $5/moの枠内に収まっています。