Datastore へのキャッシュに Memcache を利用している場合、Memcache が落ちると Datastore のパフォーマンスが劣化することがある

Datastore へのキャッシュに Memcache を利用している場合、
Memcache が落ちると Datastore のパフォーマンスが劣化することがある。

当たり前と言えば、当たり前だが、
こういったケースを想定できていなかったので、
書いておく。

パフォーマンス劣化について

Memcache が落ちた際のインシデントが以下。

The Memcache service has recovered from a disruption between 12:30 US/Pacific and 15:30 US/Pacific.
https://status.cloud.google.com/incident/appengine/17007#5750790484393984

上記のインシデントに以下の記載がある。

Some customers experienced elevated Datastore latency and errors while Memcache was unavailable. At this time, we believe that all the Datastore issues were caused by surges of Datastore activity due to Memcache being unavailable. When Memcache failed, if an application sent a surge of Datastore operations to specific entities or key ranges, then Datastore may have experienced contention or hotspotting, as described in https://cloud.google.com/datastore/docs/best-practices#designing_for_scale. Datastore experienced elevated load on its servers when the outage ended due to a surge in traffic.

Memcache に Datastore のデータをキャシュしている場合、
Memcache が落ちると、
Datastore へのアクセスが急増してしまう。
その結果、エラーになる確率が上がったり、レスポンスが遅くなったりする。

Datastore へのアクセスに goon を利用していると、
このケースに該当する可能性がある。

ちなみに、エラーになる確率が上がったり、レスポンスが遅くなったりするだけで
全てのアクセスがエラーになるわけではない。
必ずしもサービスに影響が出るレベルでエラーが発生するとは限らない。

対策

Datastore の Entity の key 範囲を広くする

以下に記載があるように Datastore の Entity の key 範囲が狭い場合は、
特定のタブレットへのアクセスが集中し、
パフォーマンスが劣化する可能性が高くなる。
https://cloud.google.com/datastore/docs/best-practices#high_readwrite_rates_to_a_narrow_key_range

key 範囲を広くしましょう。
とはいえ、これは Datastore を利用する上で基本となるルールなので、守れていることが多いと思う。

シャーディング & レプリケーション

key 範囲を広くしていても、
ある特定の key に対するアクセスが集中する場合は
パフォーマンスが劣化してしまう。

以下に記載があるようにシャーディング、レプリケーションを検討するといい。
https://cloud.google.com/datastore/docs/best-practices#sharding_and_replication

シャーディングに関してはこちらにもまとめてある。
http://pospome.hatenablog.com/entry/2017/02/05/171635#エンティティグループは1秒間に1回しか更新できない--解決策

レプリケーションについては調べても分かりませんでした・・・。
知っている人は教えてください・・・。

タブレットの分割

key 範囲を広くしている & 特定の key に対するアクセスが集中しない
という場合でも、
アクセスに対してタブレットの数が少ないと(十分に分割されていないと)、
パフォーマンスが劣化する可能性がある。
https://cloud.google.com/datastore/docs/best-practices#ramping_up_traffic

過去 Memcache が落ちている期間に Datastore のエラーが発生しなくなったことがあるので、
アクセスが集中することによってタブレットが分割してくれたのかなと思っている。

事前にタブレットを分割させた方がいいのか? と言われると、
それなりに手間かかりそうなので微妙かもしれない・・・。

アプリケーションコードでのリトライ実装

わざわざリトライさせるのか? という感じではあるが、
Datastore のアクセスがエラーになると困るような場合はリトライを実装するといい。
全てのアクセスがエラーになるわけではないので、
短期間のリトライであっても成功するかもしれない。

まとめ

アクセスがエラーになった場合でも
データの整合性を損なわないように実装されていれば、
利用側のリトライによってケアできる問題なので、
あまり気にする必要はないかもしれない。

ただ、マイクロサービス化によって一部の処理が非同期化されていたりすると、
なかなか整合性を担保するのが難しかったりするので、
このようなケースがあるということを頭の片隅に入れておくといいかもしれない。