Cloud Tasksの最大同時実行ディスパッチの効果を検証してみる #cm_google_cloud_adcal_2024

Cloud Tasksの最大同時実行ディスパッチの効果を検証してみる #cm_google_cloud_adcal_2024

Clock Icon2024.12.19

はじめに

データ事業本部のkobayashiです。
クラスメソッドの Google Cloud Advent Calendar 2024 の 19 日目のブログです。

https://qiita.com/advent-calendar/2024/cm-google-cloud

先日のCloud Workflowsで1つのワークフローを実行する際にCloud Taskキューを使用して実行レートの制限をしてみた記事の最後に書いた「最大同時ディスバッチ数で同一ワークフローの同時実行」は出来なかったのですが、では制御の行えるCloud Run関数のタスクを同じキューに混ぜた時にどういった挙動になるかが気になったのでためてみましたのでその内容をまとめます。

https://dev.classmethod.jp/articles/workflows-cloud-tasks-cm_google_cloud_adcal_2024/

最大同時ディスバッチ数の挙動をCloud WorkflowsとCloud Run関数のタスクで確認する

最大同時ディスバッチ数ですがすでに記事にした通りCloud Workflowsでは効果がありませんでした。一方Cloud Run関数では同一関数の実行数を制御できます。詳しくはこちらの記事をご確認ください。

https://dev.classmethod.jp/articles/gcp-cloud-tasks-python-asynchronous/

最大同時ディスバッチ数の設定に対してCloud WorkflowsとCloud Run関数のタスクで異なる挙動となるのでタスクを混ぜた場合にどのような挙動になるかを調べるために以下の方法で検証してみました。

  1. 最大同時ディスバッチ数を1に設定しCloud WorkflowsとCloud Run関数のタスクを交互にタスクに登録して実行してみる
  2. 最大同時ディスバッチ数を2に設定しCloud WorkflowsとCloud Run関数のタスクを交互にタスクに登録して実行してみる
  3. 最大同時ディスバッチ数を2に設定し2つのCloud Workflowsをと1つのCloud Run関数のタスクを交互にタスクに登録して実行してみる

ワークフローは前回作成した10秒間待機するものtest-wf-queue1をそのまま使います。Cloud Tasksのキューも前回のものtest-wf-queue1をそのまま使います。
Cloud Run関数ですがこちらも以下のようなコードを使用して10秒間待機する関数test-taskを作成します。

main.py
import time

import functions_framework

@functions_framework.http
def main(request):
    request_json = request.get_json()
    iteration = request_json.get("iteration")

    print(f"Start task-{iteration}")

    time.sleep(10)

    print(f"Finish task-{iteration}")

    return f"task-{iteration}"
requirements.txt
functions-framework==3.*

それでは早速試してみます。

最大同時ディスバッチ数1

それでは先にCloud Tasksのキューの設定で最大同時ディスパッチ数を1に変更します。

$ gcloud tasks queues update test-wf-queue1 --location=asia-northeast1 \
    --max-dispatches-per-second=500 --max-concurrent-dispatches=1 \
    --log-sampling-ratio=1.0

なお、最大ディスパッチ数(1秒間に実行されるタスク数)は上限の500--max-dispatches-per-second=500にしてこちらの影響は考えないものとします。また実行結果をCloud Loggingで確認するため--log-sampling-ratio=1.0でログをすべて出力するようにしています。

次にワークフローとCloud Run関数を以下のshellスクリプトで交互に登録します。

#!/bin/bash

# 変数定義
QUEUE_NAME="test-wf-queue1"
LOCATION="asia-northeast1"
PROJECT_ID="{プロジェクトID}"
WORKFLOW_NAME="wf-task"
SERVICE_ACCOUNT="wf-sa-for-queue@{プロジェクトID}.iam.gserviceaccount.com"
ITERATIONS=10

for i in $(seq 1 $ITERATIONS); do
    PADDED_NUM=$(printf "%03d" $i)

    # UUIDの生成
    TASK_UUID=$(uuidgen)
    echo "Creating task $i of $ITERATIONS (UUID: $TASK_UUID)"

    gcloud tasks create-http-task "task-${PADDED_NUM}-workflow-${TASK_UUID}" \
        --queue=$QUEUE_NAME \
        --location=$LOCATION \
        --url="https://workflowexecutions.googleapis.com/v1/projects/$PROJECT_ID/locations/$LOCATION/workflows/$WORKFLOW_NAME/executions" \
        --method=POST \
        --oauth-token-scope="https://www.googleapis.com/auth/cloud-platform" \
        --oauth-service-account-email=$SERVICE_ACCOUNT \
        --body-content="{\"argument\": \"{\\\"iteration\\\": $i}\"}"

    sleep 0.1

    gcloud tasks create-http-task "task-${PADDED_NUM}-cloudrunfunc-${TASK_UUID}" \
        --queue=$QUEUE_NAME \
        --location=$LOCATION \
        --url="https://asia-northeast1-$PROJECT_ID.cloudfunctions.net/test-task" \
        --method=POST \
        --oidc-service-account-email="$PROJECT_ID@appspot.gserviceaccount.com" \
        --header="Content-Type: application/json" \
        --body-content="{\"iteration\": $i}"

    sleep 0.1

done

echo "Task creation completed"

キューに登録したタスクを確認してみると次にようにワークフローとCloud Run関数のタスクが交互に登録されていることが確認できます。

$ gcloud tasks list --queue=test-wf-queue1 --location=asia-northeast1 --sort-by=SCHEDULE_TIME
TASK_NAME                                                   TYPE  CREATE_TIME           SCHEDULE_TIME                DISPATCH_ATTEMPTS  RESPONSE_ATTEMPTS  LAST_ATTEMPT_STATUS
task-001-workflow-8C62411B-263B-4D49-A5C1-8E1C46C6E646      http  2024-12-15T05:24:02Z  2024-12-15T05:24:02.080372Z  0                  0                  Unknown
task-001-cloudrunfunc-8C62411B-263B-4D49-A5C1-8E1C46C6E646  http  2024-12-15T05:24:02Z  2024-12-15T05:24:02.948595Z  0                  0                  Unknown
...
task-010-workflow-3863A950-3FF7-4594-BD2C-1AC814550A27      http  2024-12-15T05:24:16Z  2024-12-15T05:24:16.791385Z  0                  0                  Unknown
task-010-cloudrunfunc-3863A950-3FF7-4594-BD2C-1AC814550A27  http  2024-12-15T05:24:17Z  2024-12-15T05:24:17.581308Z  0                  0                  Unknown

では実行結果をCloud Logginのログエクスプローラーで見てみます。
使用するクエリは次のものです。

resource.type="cloud_tasks_queue" resource.labels.queue_id="test-wf-queue1" severity>=DEFAULT 
jsonPayload.attemptDispatchLog.dispatchReason="PUSH_QUEUE"
jsonPayload.task:"projects/{プロジェクトID}/locations/asia-northeast1/queues/test-wf-queue1/tasks/"

1

結果を見てみるとワークフローが実行された後にCloud Run関数が実行され最大同時ディスバッチ数の1となりCloud Run関数の終了を待ちます。Cloud Run関数の実行に10秒かかるのでCloud Run関数の実行開始から10秒程度で次のワークフロー、Cloud Run関数が実行されこれを繰り返します。

最大同時ディスバッチ数2

それでは次に最大同時ディスバッチ数を2--max-concurrent-dispatches=2にして同じ処理を行ってみます。

$ gcloud tasks queues update test-wf-queue1 --location=asia-northeast1 \
    --max-concurrent-dispatches=2

2

これも結果は単純で、Cloud Run関数の実行数のみが最大同時ディスバッチ数の効果があるだけになります。結果を見ると

  1. ワークフローtask-001-workflow、Cloud Run関数task-001-cloudrunfuncが実行され、この時点ではCloud Run関数の実行数が1となる
  2. ワークフローtask-002-workflow、Cloud Run関数task-002-cloudrunfuncが実行され、この時点でCloud Run関数の実行数が2になる
  3. Cloud Run関数task-001-cloudrunfuncの終了を待ちで実行からおよそ10秒後にワークフローtask-003-workflow、Cloud Run関数task-003-cloudrunfuncが実行される
  4. Cloud Run関数task-002-cloudrunfuncの終了を待ちで実行からおよそ10秒後にワークフローtask-004-workflow、Cloud Run関数task-004-cloudrunfuncが実行される

以降、これを繰り返します。

最大同時ディスパッチ数2(ワークフロー2、Cloud Run関数1)

最後に最大同時ディスパッチ数は2のままでCloud Workflowsのワークフローを2つ、Cloud Run関数を1つを繰り返し実行するパターンを検証してみます。

$ gcloud tasks list --queue=test-wf-queue1 --location=asia-northeast1 --sort-by=SCHEDULE_TIME
TASK_NAME                                                   TYPE  CREATE_TIME           SCHEDULE_TIME                DISPATCH_ATTEMPTS  RESPONSE_ATTEMPTS  LAST_ATTEMPT_STATUS
task-001-a-workflow-2CACA3E5-E96B-42A9-B15A-62480ED3C587    http  2024-12-15T06:07:35Z  2024-12-15T06:07:35.261374Z  0                  0                  Unknown
task-001-b-workflow-2CACA3E5-E96B-42A9-B15A-62480ED3C587    http  2024-12-15T06:07:36Z  2024-12-15T06:07:36.126778Z  0                  0                  Unknown
task-001-cloudrunfunc-2CACA3E5-E96B-42A9-B15A-62480ED3C587  http  2024-12-15T06:07:36Z  2024-12-15T06:07:36.989334Z  0                  0                  Unknown
task-002-a-workflow-95DDB106-EC6B-49B4-9B96-7591AB1705E4    http  2024-12-15T06:07:37Z  2024-12-15T06:07:37.918163Z  0                  0                  Unknown
task-002-b-workflow-95DDB106-EC6B-49B4-9B96-7591AB1705E4    http  2024-12-15T06:07:38Z  2024-12-15T06:07:38.663005Z  0                  0                  Unknown
task-002-cloudrunfunc-95DDB106-EC6B-49B4-9B96-7591AB1705E4  http  2024-12-15T06:07:39Z  2024-12-15T06:07:39.540776Z  0                  0                  Unknown
task-003-a-workflow-D4E5E78D-902E-46AD-BB1C-1EDF62F83269    http  2024-12-15T06:07:40Z  2024-12-15T06:07:40.340936Z  0                  0                  Unknown
task-003-b-workflow-D4E5E78D-902E-46AD-BB1C-1EDF62F83269    http  2024-12-15T06:07:41Z  2024-12-15T06:07:41.244466Z  0                  0                  Unknown
...

3

これも結果を見てみます。結果は先程の最大同時ディスパッチ数2のパターンと同じでワークフローの実行は最大同時ディスパッチに関係のないものということだけです。
Cloud Run関数の実行数のみが最大同時ディスバッチ数の効果があるだけになります。結果を見ると

  1. ワークフローtask-001-a-workflow、ワークフローtask-001-b-workflow、Cloud Run関数task-001-cloudrunfuncが実行され、この時点ではCloud Run関数の実行数が1となる
  2. ワークフローtask-002-a-workflow、ワークフローtask-002-b-workflow、Cloud Run関数task-002-cloudrunfuncが実行され、この時点でCloud Run関数の実行数が2になる
  3. Cloud Run関数task-001-cloudrunfuncの終了を待ちで実行からおよそ10秒後にワークフローtask-003-a-workflow、ワークフローtask-003-b-workflow、Cloud Run関数task-003-cloudrunfuncが実行される
  4. Cloud Run関数task-002-cloudrunfuncの終了を待ちで実行からおよそ10秒後にワークフローtask-004-a-workflow、ワークフローtask-004-b-workflow、Cloud Run関数task-004-cloudrunfuncが実行される

以降、これを繰り替える形です。

まとめ

Cloud Tasksの最大同時ディスパッチ数の挙動を確認してみました。3つの検証結果としては単純にCloud Workflowsの実行数は無視されCloud Run関数の同時実行数のみが影響するという内容でした。この検証結果を踏まえCloud Tasksのキューをうまく使っていきたいと思います。

明日 12/20 は suto さんです。お楽しみに。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.