TQ を利用するとき、
いつもはタスクを受ける HTTP の受け口(handler)を定義して、
taskqueue パッケージで実装を完結させていただけど、
「今回は delay パッケージ使ってみようかなー」
と思ったので違いを調べてみた。
それぞれの使い方とかはネットに転がってるので、
ググってください。
挙動に大きな違いはない気がする
自分が軽く触った結果、挙動に大きな違いはなかった。handler でも delay でも、
どっちでもできることは同じに思える。
細かい実装の違い
handler でも delay でも、どっちでもできることは同じだが、
handler が HTTP を受けるのに対して、
delay は以下のような関数なので、
これらの違いによる 実装方法の違い は存在する。
var laterFunc = delay.Func("key", func(c context.Context, x string) { // ... })
例えば、リトライの実装方法が違う。
handler は HTTP Status によってリトライするかどうかを制御する。
https://cloud.google.com/appengine/docs/standard/go/taskqueue/push/retrying-tasks
一方、delay は HTTP を受け取らないので、
関数の戻り値で error を返すかどうかによってリトライするかどうかを制御する。
If the function has any return arguments, and the last one is of type error, the function may return a non-nil error to signal that the function should be retried.
https://cloud.google.com/appengine/docs/standard/go/taskqueue/delay
HTTP Status を返すよりも、
error を返すほうが簡単かなーと思わなくもないが、
実際大して変わらないと思う。
パッと思いつくのは、
リトライ実装くらいだが、
handler or 関数 の性質の違いが実装方法に違いを与えることがある。
delay パッケージには Function.Call() がある
delay には Function.Call() というメソッドが生えている。https://cloud.google.com/appengine/docs/standard/go/taskqueue/delay#Function
これは delay における以下のコードを、
t, _ := f.Task(...)
_, err := taskqueue.Add(c, t, "")
以下のように Call() のみで書くことができる。
err := f.Call(c, ...)
Call() では queue.yaml の name を指定することができないので、
queue name は default になる。
まあ、それだけなんだけど・・・。
delay パッケージはタスクに struct を突っ込める
delay では、タスクを処理する関数側で user struct を以下のように受け取ることができる。
var laterFunc = delay.Func("key", func(c context.Context, user User) { // ... })
なので、タスクを追加する際のコードでは、
以下のように user struct を突っ込むことができる。
laterFunc.Call(c, User{"pospome"})
一方、handler を定義する場合、
タスクに突っ込める値は url.Values になっているので、
string しか扱えない。
func NewPOSTTask(path string, params url.Values) *Task
https://cloud.google.com/appengine/docs/standard/go/taskqueue/reference#Task
ただ、
handler のタスクである taskqueue.Task の url.Values は、
以下の Task.Payload に []byte として突っ込まれるので、
type Task struct { // Path is the worker URL for the task. // If unset, it will default to /_ah/queue/<queue_name>. Path string // Payload is the data for the task. // This will be delivered as the HTTP request body. // It is only used when Method is POST, PUT or PULL. // url.Values' Encode method may be used to generate this for POST requests. Payload []byte //省略 }
delay のように struct を gob とかでシリアライズしてあげれば、
いけるのかもしれないが、
やはり delay の方が便利かなと思う。
*動作確認はしていません。
delay パッケージには罠がある
以下に載っている。https://qiita.com/hogedigo/items/fae5b6fe7071becd4051
・delay関数の生成タイミング間違えてハマった(´・ω・`)
・delay関数使ったコードのファイル名変えてハマった(´・ω・`)
ファイル名の変更が NG というのは結構なハマりポイントな気がする・・・。
忘れそう・・・。
外部から叩けるかどうか
handler として定義すると、以下のように handler をセキュアな設定にすると思うが、
https://cloud.google.com/appengine/docs/standard/go/taskqueue/push/creating-handlers#securing_task_handler_urls
この設定を取っ払ってしまえば、 handler を外部から叩くことができる。
オンプレの外部システムから handler を叩いてタスクを実行したい
というケースでは役に立つかもしれない。
ただ、delay を利用した場合でも、
それ用の handler を用意すればいいだけだし、
handler の処理は同期処理になると思うので、
結局 handler で定義することに明確なアドバンテージはない気がする。
どっちを使えばいいのか?
delay パッケージを触ってみて、TQ(Push)は全部 delay でいーかなと思いました。
handler 定義した方がいいよ っていうケースがあれば、
ブログのコメント欄 or twitter で教えてください。