goonのクエリをチューニングした時のメモ

App Engine(Go 1.9)でDatastoreからデータを取得するのにgoonGoon.Runを使っていたが、プロパティを追加する改修*1をした後、 latencyが遅くなり、DatastoreでDeadline exceededが頻繁に発生するようになってしまった。

とりあえず該当するリクエストをStackdriver Traceで見てみるとdatastore_v3.Nextが7回ほど呼ばれていることがわかった。

まずはこの呼び出し回数を減らせば速くなるだろうと思い、Query.BatchSizeを設定してみたが、呼び出し回数に変化は無く、全く効果がなかった。
それも変だなと思い、試しにデバッグログを仕込んで1回の呼び出しでどれくらいのデータを取得できているのか見てみたところ、なんと90件くらいしか取得できていなかった。

ここまで改修前の状態を確認していなかったので念のため調べてみると、datastore_v3.Nextの呼び出し回数は5回、1回あたりのデータ取得件数は約120件だった。
このことから、原因はプロパティを追加したことでデータのサイズが増え、一度に取得できる件数が減ったことによるDatastoreへのアクセス増ということがわかった。

ということで次はMemcacheを使ってDatastoreへのアクセスを減らすようにしてみた。
方法としては今までのクエリにQuery.KeysOnlyを設定してキーだけを取得するようにし、本体はGoon.GetMultiで取ってくるというシンプルなもの。
しかし、これだと1000件のデータを取得しようとするとGoon.GetMultiがMemcacheにデータを格納する際に大量のメモリを消費してしまうため、エラーになったり、インスタンスが落ちるようになってしまった。
そこでGoon.GetMultiの呼び出しが100件ずつになるようにしてみたところ、エラーは解消されたように見えたが、どうやらMemcacheにデータが乗っておらず、常にDatastoreにアクセスしてしまっていた。
仕方なく50件まで減らしたら安定してキャッシュが効くようになり、ようやく速度が改善されるようになった。

そもそもデータ構造がイマイチっぽいので本来はそこから手を入れるのが良さそうだが、今からそれをやるのもなかなかに厳しい状況なので当分はこれでしのぐことになりそう。

*1:後で確認したらデータサイズが80%ほど増加していた...