デプロイとデバッグがぐっと楽になる、シンプルなTasksエミュレータをご紹介します。
Cloud Tasksを使うアプリケーション(たとえばWebアプリ)を開発していると、タスクがどのようにスケジューリングされ、配信され、ハンドラーで処理されるのかを実際に確認したくなります。ところが、繰り返し要望が出ているにもかかわらず、Googleは、DatastoreやPubSubに用意されているような開発用エミュレータをCloud Tasks向けには提供していません。
そこで作ったのが、新しいPython製のCloud Tasks In-Process Emulatorです。とにかくすぐ試したい方は、下の「クイックスタート」へどうぞ。

Cloud Tasks開発の進め方
選択肢は4つあります。
- 本物のCloud Tasksサービスをそのまま使う。
- Cloud Tasksと同じHTTPインターフェースを公開する、localhostベースのエミュレータを使う。
- あえて何もしない。タスクの処理は気にせず、タスク作成を黙って失敗させたり、ハンドラーを定義しないままタスクを発行したりする。
- インプロセスのエミュレータを使う。開発時にはこれを差し込み、デプロイ環境では実際のCloud Tasksを使う実装に切り替える。
おすすめは次のとおりです。
- 開発にはインプロセスエミュレータ
- クラウド上の統合テストには実際のCloud Tasks
- ユニットテストではCloud Tasksを使わない
その理由を見ていきましょう。
インプロセスのメリット
デバッグしやすい
開発中は、各タスクが今どこにあって、中身に何が入っているのかを把握したいものです。エミュレータがインプロセスで動いていれば、デバッガでキューを止めて値をそのまま覗き込めます。
シンプルさが武器
このエミュレータのコードは非常にシンプルで、タスクの作成と処理だけをサポートしています。これは意図的な設計です。デバッグするときは、コードベースがシンプル(本エミュレータは100行未満)なほうが圧倒的に扱いやすいからです。
また、アルファ版のプロダクトを扱うとき(本エミュレータを含め、Cloud Tasksエミュレータはどれもアルファ版です)は、すべてのコードを自分の手元に置けることが安心材料になります。問題があってもすぐにデバッグでき、必要なら手を入れることもできます。
より幅広い機能や、より本番に近いテストが必要なら、クラウドにデプロイしたアプリケーションで実際のCloud Tasksを使うのがよいでしょう。
使い勝手
Potato Londonのgcloud-tasks-emulatorやAert van de Hulsbeek氏のcloud-tasks-emulatorといった他のCloud Tasksエミュレータは、localhostで動作し、クラウドサービスと同じAPIを公開できるのが強みです。エンドポイントを差し替えるだけで使えますし、どの言語からでも呼び出せます(これはインプロセス版にはできない芸当です)。
その反面、別プロセスを動かす手間がついて回ります。開発セッションのたびに起動し、クラッシュやフリーズが起きていないかを気にしなくてはなりません。デバッガから起動すればデバッグもできますが、普段の開発フローに組み込むのは現実的とは言えないでしょう。
タスクを取りこぼさない
タスクをCloud Tasksに投げっぱなしにして処理しない、あるいは作成自体を黙って失敗させる、という割り切り方もあります。これは、メインの開発フローでタスク処理を扱わない場合(たとえば別チームが担当する別のマイクロサービスでタスクが処理されるようなケース)にはうまく機能します。ユニットテストでも有効です。分散・非同期処理のテストは一般にユニットテストの守備範囲ではなく、統合テストで担うものだからです。
とはいえ、自分のタスクが実際にどう処理され、コールバックがどう動くかを目で見て確認したいなら、何らかのエミュレータが必要になります。
クラウドありでも、なしでも
エンドツーエンドの流れを丸ごと再現したい場合は、開発マシンから実際のCloud Tasksを呼び出すという選択肢もあります。デプロイ環境とまったく同じAPIと機能が手に入るので、クラウド上で動かす統合テストには好適です。
ただし開発マシンでこれをやろうとすると、常時ネットワーク接続が必須になりますし、Google Cloud Platformからローカルマシンに到達できるようngrokのようなプロキシを立てる必要もあります(詳細はこちら)。さらに、本物のCloud Tasks APIを使う以上は本物のキューも必要で、自分専用のGoogle Cloudプロジェクトを用意するか、開発者ごとにキューを割り当てて管理する仕組みが要ります。
結果として、開発マシン上のちょっとした処理をデバッグするだけでも、詳しい情報を見るためにCloud Consoleを開く羽目になります。個人的には、そうしたお膳立ては省いて、デバッグは自分のプロセスの中で完結させたい派です。
機能と制限事項
本プロジェクトは、開発で日常的に使う機能、つまりタスクの作成と、コールバックでのタスク処理をサポートしています。
ただし、前述のとおりシンプルさを優先しているため、次のような機能には対応していません。
- キュー管理(キューの作成、削除、パージ、一時停止、再開)。なお、タスクを作成した時点でキューがまだなければ、自動的に作成されます。
- レート制限とリトライ。
- タスクの重複排除と削除。
新しい機能のリクエストがあれば(そして、追加する複雑さに見合うくらいシンプルに実装できそうなら)、GitHubのPull RequestsやIssuesでお知らせください。
クイックスタート
このエミュレータを使うには、emulator.pyを自分のコードベースにコピーするだけ。GitHub上のソースを直接コピー&ペーストしてもかまいません。これですぐ使い始められます。
Emulatorオブジェクトを生成し、コールバック関数を渡します。作成したタスクのペイロードは、このコールバックに届きます。あとはタスクを送るときにEmulator.create_taskメソッドを呼び出すだけ。キュー名や配信予定時刻も指定できます。
使用例
プロジェクトでは、シンプルなWebアプリの中でemulator.pyが動く様子を確認できます。local_server.pyを実行して起動し、http://127.0.0.1:8080にアクセスする(あるいはコンソールに表示されるリンクをクリックする)と、タスクが作成されます。タスクは予定どおり3秒後に処理され、標準出力に出力されます。
コードの整理にこだわりがあって、本番のコードベースにはエミュレータを混ぜたくない(デプロイ時にemulator.pyを含めたくない)という方のために、その実現方法もこの例で示しています。開発で使うlocal_server.pyではEmulatorを差し込んで使い、デプロイ済みのサーバーではEmulatorを差し込まず、代わりにCloudTasksAccessorを新規に生成して実際のCloud Tasks APIを呼び出します。こうすることで、サーバー側のコード(main.py)にエミュレータのコードがいっさい混ざらないようにできます。