GAEのpushキューではタスク実行レート制御に「トークンバケット方式」を採用している。
実際に利用してみると想定通りの動作をしてくれなかったので、調査してみました。
試行環境
- GAE Standard Environment
- Scala2.12
トークンバケットとは
ネットワークに流れるトラフィックを一定量以下になるように調整するアルゴリズムです。
まず動かす
まずはbucket_size=5で rate=5/mのqueueを作成
queue:
- name: gae-study-push-queue
mode: push
rate: 5/m
target: gae-study
bucket_size: 5
retry_parameters:
task_retry_limit: 1
- bucket_size:トークンを保持できる最大数
- rate:タスクを処理する頻度で、単位はsが秒、mが分、hが時間、dが日を表します。例えば5/mという値はタスクが1分あたり5回のレートで処理されることを示します
- max_concurrent_requests:キューから同時に実行できるタスクの最大数(default:1000)
<appengine-web-app xmlns="http://appengine.google.com/ns/1.0">
<application>XXXXXXX</application>
<module>gae-study</module>
<version>app001</version>
<runtime>java8</runtime>
<threadsafe>false</threadsafe>
<instance-class>F1</instance-class>
<automatic-scaling>
<!--https://cloud.google.com/appengine/docs/standard/java/config/appref?hl=ja#scaling_elements_automatic_scaling-->
<min-idle-instances>0</min-idle-instances>
<max-idle-instances>1</max-idle-instances>
<max-concurrent-requests>30</max-concurrent-requests>
<max-pending-latency>10s</max-pending-latency>
<min-pending-latency>4s</min-pending-latency>
</automatic-scaling>
<system-properties>
<property name="java.util.logging.config.file" value="WEB-INF/logging.properties"/>
</system-properties>
</appengine-web-app>
Taskで行う処理
package com.gae.study
import skinny.micro.WebApp
trait Hello extends WebApp {
get("/sayhello") {
val key = params("key")
val message = s"Hello, key => $key!"
logger.info(message)
Thread.sleep(5000) // 5秒待機
message
}
}
予想
gae-study-push-queue
にタスクをいっきに7件追加したらどうなるか。
bucket_size=5でrate=5/mなので
- まず最初の1分間で5件のタスクを処理
- 次の1分間で残りの2件のタスクを処理
よって、全体で約1分ちょいかかる見込み
実行結果
30秒
で全処理が完了。。。rate=5/mなのに1分間に5件以上処理をしているのでは???
最初の5件は想定通り、13:58:47〜48で受付されており、
6件目は13:58:59に受付
7件目は13:59:11に受付
なので、初回から12秒後に6件目、さらに12秒後に7件目が受付されている
結論
最初は5個のトークンが存在するため、1分間で5件以上のタスクが処理されているように見えるだけで
基本的にはrateが5/mの場合、タスクは1分あたり5回のrateで処理される。
ただ、トークンが補充されるタイミングは1分後にいっきに補充されるのではなく、rateが5/mであれば、60秒/5=12秒おきに1個ずつ補充されていく。rateが10/mであれば、60秒/10=6秒おきにトークンが1個ずつ補充されていくといった動きになる。