GAE の TaskQueue(PushQueue) で、delay パッケージと HTTP の受け口(handler)を定義するのは何が違うのか?

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 で教えてください。