Slide 1

Slide 1 text

2024/06/13 #Cloudflare_findy Cloudflare Workers で構築する 非同期ジョブシステム

Slide 2

Slide 2 text

No content

Slide 3

Slide 3 text

非同期ジョブシステム

Slide 4

Slide 4 text

非同期ジョブシステム メインの処理から切り離されたジョブやタスクを管理するシステム - cronなどによって起動するバッチ処理 - DBのイベントをトリガーにするタスク - 3rdパーティーのシステムやツールから呼び出されるジョブ - …etc Sidekiq(Ruby) や Celery(Python) などが代表的なミドルウェア

Slide 5

Slide 5 text

機能 - スケジューリングによる定期実行 - キューイング - リトライ処理 - ステータス管理 システムが複雑になっていくと更に高度なことが求められる - フロー管理 - 排他制御や重複抑制

Slide 6

Slide 6 text

そんな非同期ジョブシステムを Cloudflare Workers を使って 構築・運用しているという話🏗

Slide 7

Slide 7 text

- 開発体験 - デプロイの速さ - 徐々にライブラリが対応してきている - 起動速度とスケーラビリティ - コスト - Cloudflareにスタック・機能が揃った なぜCloudflare Workersを使うのか

Slide 8

Slide 8 text

非同期ジョブシステム構築 に使える機能

Slide 9

Slide 9 text

スケジューリング Cloudflare Workers の Cron Triggers cronの記法で設定を書くと、設定時刻・頻度でWorkersを起動できる

Slide 10

Slide 10 text

キューシステム キューを登録するプロデューサーWorkerと、 キューを処理するコンシューマーWorkerを構築できる Cloudflare Queues enqueue dequeue リトライ回数や待機などの設定も可能 producer consumer

Slide 11

Slide 11 text

ただ、この2つだけでは足りない Cloudflare Queues - キューのステータスを管理しづらく、成功したキューは揮発する - 他のキューの参照、過去のキューを確認などはできない - 排他制御や重複抑制などがし辛い - 過去の実績(失敗率や実行時間など)を分析するなどもできない - プロバイダーとコンシューマーは1対1 - ジョブの種類が多くなればなるほどコードが複雑になる - ちょっとだけ不安定 - たまにキューが消失する

Slide 12

Slide 12 text

なので 複雑な非同期ジョブ管理システム を構築するのは諦めていた...😂

Slide 13

Slide 13 text

今春、D1の一般公開と Service Bindings RPCの発表によって 大きく状況が変わった🤩 https://blog.cloudflare.com/making-full-stack-easier-d1- ga-hyperdrive-queues https://blog.cloudflare.com/javascript-native-rpc

Slide 14

Slide 14 text

キューをD1に保存することで、ジョブの情報の永続化を可能にする - 不安定さへの対処 - 検索可能性 - 排他処理などのジョブを跨ぐ管理・制御 - ログストア - 専用の管理画面の構築 キューのステータス管理と半永続化 D1 Cloudflare Workers 用の永続データストア (SQLite)

Slide 15

Slide 15 text

これまでも Service Bindigs (別ワーカーをグローバルネットワークを経由せずシステムコールで 呼び出せる) はあったのだが、fetchのインタフェースにしか対応しておらず、使い勝手があまり良くなった。 SB RPCでは、他のWorkerに展開されているモジュールコードを、同一ワーカー内にあるように コールできるようになった。 コードベースの切り出しと隠蔽 Service Bindings RPC Workerから別WorkerをRPCで呼び出し可能になる SBからSBを呼び出すことでProxyモジュールを経由してジョブを呼び出せる 一つのインターフェースで同期的に処理させたり、キューに流して非同期的に処 理させたりできる。(複雑なジョブの管理をシンプルにできる)

Slide 16

Slide 16 text

これらを使ってどう構築するか🏗

Slide 17

Slide 17 text

main job 📄Payload(type: A) メインWorkerから、分岐可能なフラグを持つペイロードを持たせてエンキューし、 ジョブWorker側で処理を呼び分ける。 ※分岐が増えていくと管理が複雑になり、またインタフェースの設計がかなり重要に なってくる 📄Payload(type: B) Before

Slide 18

Slide 18 text

main proxy メインワーカーでエンキュー、ジョブワーカーでデキューをやめて、 キューの前後にエンキュー・デキュー専用のプロキシワーカーを用意。 さらに、間を Service Bindings RPC でつなぐ RPC RPC job-A job-B proxy’ After

Slide 19

Slide 19 text

main proxy 一見複雑に見えるが、 間を隠蔽し、インターフェースを工夫することで、メインワーカからは シンプルにjob-A, job-Bを起動することができる(見せかけられる)。 ※実際にはキュー越しなので非同期に処理される RPC RPC job-A job-B proxy’ Blackbox env.MyProxy.asyncExec(“job-A”, payload) env.MyProxy.asyncExec(“job-B”, payload)

Slide 20

Slide 20 text

main proxy RPC RPC job-A job-B proxy’ D1 更にD1を挟んで、Queueデータを永続化する。 他のキューを検索可能になるので重複抑制ができるようになったり、 ログの分析や状態管理、管理画面を作って制御したりなどができる。

Slide 21

Slide 21 text

便利そうではあるが どう考えても構築が面倒🤮

Slide 22

Slide 22 text

npmパッケージ あります🚀

Slide 23

Slide 23 text

🎇 Kiribi

Slide 24

Slide 24 text

https://kiribi.pages.dev/

Slide 25

Slide 25 text

# kiribiをインストール $ npm install kiribi # d1とqueueを構築 $ npx wrangler d1 crete kiribi-db $ npx wrangler d1 migrations apply kiribi-db –local # or --remote $ npx wrangler queue create kiribi-queue 数コマンド + wrangler.toml の編集で前述したような仕組みを構築可能 詳しくはドキュメントページ https://kiribi.pages.dev/ を参照

Slide 26

Slide 26 text

env.KIRIBI.enqueue(“MyJob”, payload, option) ジョブの呼び出し(エンキュー) import { KiribiPerformer } from “kiribi/performer” export class MyJob extends KiribiPerformer { async perform(payload) { // 何らかの処理 } } ジョブの定義

Slide 27

Slide 27 text

Demo

Slide 28

Slide 28 text

No content

Slide 29

Slide 29 text

No content

Slide 30

Slide 30 text

No content

Slide 31

Slide 31 text

No content

Slide 32

Slide 32 text

No content

Slide 33

Slide 33 text

Demo

Slide 34

Slide 34 text

パッケージ化されてるとはいえ 複数のWorkersを管理するのは大変では? 🫠 main proxy RPC RPC job-A job-B proxy’ D1

Slide 35

Slide 35 text

実際は1つのWorkersで構築可能 - Service Bindings RPCは自Workerを呼び出すこともできる - Queueに対して1WorkerでProvider/Consumer両方をさせることができる main RPC RPC job-A job-B proxy’ proxy

Slide 36

Slide 36 text

実際は1つのWorkersで構築可能 main RPC RPC job-A job-B proxy’ proxy さらにKiribiで隠蔽すれば意外とシンプル✨ env.KIRIBI.enqueue(“MyJob”, payload, option) - Service Bindings RPCは自Workerを呼び出すこともできる - Queueに対して1WorkerでProvider/Consumer両方をさせることができる

Slide 37

Slide 37 text

まとめ - Cloudflare Queues, SB RPC, D1などを組み合わせれば ある程度の規模に耐えうる非同期ジョブシステムが構築できる - 今回紹介した仕組みの部分はnpmのパッケージがある - ドキュメント: https://kiribi.pages.dev/ - Github: https://github.com/aiji42/kiribi - PR/Star ぜひお願いします🙏 - Cloudflare Workers(+周辺ツール) x アイデアで 面白いものや仕組みを構築可能

Slide 38

Slide 38 text

ご清聴ありがとうございました