์›Œํฌํ”Œ๋กœ ๋‹จ๊ณ„ ๋ณ‘๋ ฌ ์‹คํ–‰

๋ณ‘๋ ฌ ๋‹จ๊ณ„์—์„œ ์—ฌ๋Ÿฌ ์ฐจ๋‹จ ํ˜ธ์ถœ์„ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•˜๋ฉด ์›Œํฌํ”Œ๋กœ์˜ ์ด ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

sleep, HTTP ํ˜ธ์ถœ, ์ฝœ๋ฐฑ ๋“ฑ์˜ ์ฐจ๋‹จ ํ˜ธ์ถœ์—๋Š” ๋ฐ€๋ฆฌ์ดˆ ๋‹จ์œ„์—์„œ ์ผ ๋‹จ์œ„๊นŒ์ง€ ์‹œ๊ฐ„์ด ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ‘๋ ฌ ๋‹จ๊ณ„์˜ ์šฉ๋„๋Š” ์ด๋Ÿฌํ•œ ๋™์‹œ ์žฅ๊ธฐ ์‹คํ–‰ ์ž‘์—…์„ ์ง€์›ํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค. ์›Œํฌํ”Œ๋กœ์—์„œ ์„œ๋กœ ๋…๋ฆฝ๋œ ์—ฌ๋Ÿฌ ์ฐจ๋‹จ ํ˜ธ์ถœ์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ๋ณ‘๋ ฌ ๋ธŒ๋žœ์น˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ํ˜ธ์ถœ์„ ๋™์‹œ์— ์‹œ์ž‘ํ•˜๊ณ  ๋ชจ๋“  ํ˜ธ์ถœ์ด ์™„๋ฃŒ๋˜๊ธฐ๋ฅผ ๊ธฐ๋‹ค๋ฆฌ๋ฏ€๋กœ ์ด ์‹คํ–‰ ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด ์›Œํฌํ”Œ๋กœ๊ฐ€ ์ง„ํ–‰๋˜๊ธฐ ์ „์— ์—ฌ๋Ÿฌ ๋…๋ฆฝ ์‹œ์Šคํ…œ์—์„œ ๊ณ ๊ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฒ€์ƒ‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ๋ณ‘๋ ฌ ๋ธŒ๋žœ์น˜๋กœ API ์š”์ฒญ์„ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ์ด 5๊ฐœ ์žˆ๊ณ  ๊ฐ ์‹œ์Šคํ…œ์˜ ์‘๋‹ต ์‹œ๊ฐ„์ด 2์ดˆ๋ผ๋ฉด ์›Œํฌํ”Œ๋กœ์—์„œ ๋‹จ๊ณ„๋ฅผ ์ˆœ์ฐจ์ ์œผ๋กœ ์ˆ˜ํ–‰ํ•˜๋Š” ๋ฐ ์ตœ์†Œ 10์ดˆ๊ฐ€ ๊ฑธ๋ฆฌ์ง€๋งŒ, ๋‹จ๊ณ„๋ฅผ ๋ณ‘๋ ฌ๋กœ ์ˆ˜ํ–‰ํ•˜๋ฉด 2์ดˆ๋งŒ ๊ฑธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ณ‘๋ ฌ ๋‹จ๊ณ„ ๋งŒ๋“ค๊ธฐ

parallel ๋‹จ๊ณ„๋ฅผ ๋งŒ๋“ค์–ด ๋‘ ๊ฐœ ์ด์ƒ์˜ ๋‹จ๊ณ„๊ฐ€ ๋™์‹œ์— ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋Š” ์›Œํฌํ”Œ๋กœ ๋ถ€๋ถ„์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค.

YAML

  - PARALLEL_STEP_NAME:
      parallel:
        exception_policy: POLICY
        shared: [VARIABLE_A, VARIABLE_B, ...]
        concurrency_limit: CONCURRENCY_LIMIT
        BRANCHES_OR_FOR:
          ...

JSON

  [
    {
      "PARALLEL_STEP_NAME": {
        "parallel": {
          "exception_policy": "POLICY",
          "shared": [
            "VARIABLE_A",
            "VARIABLE_B",
            ...
          ],
          "concurrency_limit": "CONCURRENCY_LIMIT",
          "BRANCHES_OR_FOR":
          ...
        }
      }
    }
  ]

๋‹ค์Œ์„ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

  • PARALLEL_STEP_NAME: ๋ณ‘๋ ฌ ๋‹จ๊ณ„์˜ ์ด๋ฆ„์ž…๋‹ˆ๋‹ค.
  • POLICY(์„ ํƒ์‚ฌํ•ญ): ๋ฏธ์ฒ˜๋ฆฌ ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ๋‹ค๋ฅธ ๋ธŒ๋žœ์น˜์—์„œ ์ˆ˜ํ–‰ํ•  ์ž‘์—…์„ ๊ฒฐ์ •ํ•ฉ๋‹ˆ๋‹ค. ๊ธฐ๋ณธ ์ •์ฑ…์ธ continueAll์€ ์ถ”๊ฐ€ ์กฐ์น˜๋ฅผ ์ทจํ•˜์ง€ ์•Š์œผ๋ฉฐ ๋‹ค๋ฅธ ๋ชจ๋“  ๋ธŒ๋žœ์น˜์—์„œ ์‹คํ–‰์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ๋Š” continueAll ์ •์ฑ…๋งŒ ์ง€์›๋ฉ๋‹ˆ๋‹ค.
  • VARIABLE_A, VARIABLE_B ๋“ฑ: ๋ณ‘๋ ฌ ๋‹จ๊ณ„ ๋‚ด์—์„œ ํ• ๋‹น์„ ํ—ˆ์šฉํ•˜๋Š” ์ƒ์œ„ ๋ฒ”์œ„๊ฐ€ ํฌํ•จ๋œ ์“ฐ๊ธฐ ๊ฐ€๋Šฅํ•œ ๋ณ€์ˆ˜์˜ ๋ชฉ๋ก์ž…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๊ณต์œ  ๋ณ€์ˆ˜๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.
  • CONCURRENCY_LIMIT(์„ ํƒ์‚ฌํ•ญ): ์ถ”๊ฐ€ ๋ธŒ๋žœ์น˜ ๋ฐ ๋ฐ˜๋ณต์ด ํ์— ์ถ”๊ฐ€๋  ๋•Œ๊นŒ์ง€ ๋‹จ์ผ ์›Œํฌํ”Œ๋กœ ์‹คํ–‰ ๋‚ด์—์„œ ๋™์‹œ์— ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋Š” ์ตœ๋Œ€ ๋ธŒ๋žœ์น˜ ๋ฐ ๋ฐ˜๋ณต ํšŸ์ˆ˜์ž…๋‹ˆ๋‹ค. ์ด๋Š” ๋‹จ์ผ parallel ๋‹จ๊ณ„์—๋งŒ ์ ์šฉ๋˜๋ฉฐ ํ•˜์œ„ ๋‹จ๊ณ„๋กœ๋Š” ์ ์šฉ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์–‘์˜ ์ •์ˆ˜์—ฌ์•ผ ํ•˜๋ฉฐ ๋ฆฌํ„ฐ๋Ÿด ๊ฐ’ ๋˜๋Š” ํ‘œํ˜„์‹์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋™์‹œ ์‹คํ–‰ ์ œํ•œ์„ ์ฐธ์กฐํ•˜์„ธ์š”.
  • BRANCHES_OR_FOR: branches ๋˜๋Š” for๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ ์ค‘ ํ•˜๋‚˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค.
    • ๋™์‹œ์— ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋Š” ๋ธŒ๋žœ์น˜
    • ๋ฐ˜๋ณต์ด ๋™์‹œ์— ์‹คํ–‰๋  ์ˆ˜ ์žˆ๋Š” ๋ฃจํ”„

๋‹ค์Œ์— ์œ ์˜ํ•˜์„ธ์š”.

  • ๋ณ‘๋ ฌ ๋ธŒ๋žœ์น˜์™€ ๋ฐ˜๋ณต์€ ์ˆœ์„œ์— ๊ด€๊ณ„์—†์ด ์‹คํ–‰๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์‹คํ–‰ํ•  ๋•Œ๋งˆ๋‹ค ์‹คํ–‰ ์ˆœ์„œ๊ฐ€ ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ๋ณ‘๋ ฌ ๋‹จ๊ณ„์—๋Š” ๊นŠ์ด ํ•œ๋„๊นŒ์ง€ ๋‹ค๋ฅธ ์ค‘์ฒฉ ๋ณ‘๋ ฌ ๋‹จ๊ณ„๊ฐ€ ํฌํ•จ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํ• ๋‹น๋Ÿ‰ ๋ฐ ํ•œ๋„๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.
  • ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ณ‘๋ ฌ ๋‹จ๊ณ„์˜ ๋ฌธ๋ฒ• ์ฐธ์กฐ ํŽ˜์ด์ง€๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

์‹คํ—˜์šฉ ํ•จ์ˆ˜๋ฅผ ๋ณ‘๋ ฌ ๋‹จ๊ณ„๋กœ ๊ต์ฒด

experimental.executions.map์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ณ‘๋ ฌ ์ž‘์—…์„ ์ง€์›ํ•˜๋Š” ๊ฒฝ์šฐ ๋ณ‘๋ ฌ ๋‹จ๊ณ„๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉํ•˜๋„๋ก ์›Œํฌํ”Œ๋กœ๋ฅผ ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜ํ•˜์—ฌ ์ผ๋ฐ˜์ ์ธ for ๋ฃจํ”„๋ฅผ ๋ณ‘๋ ฌ๋กœ ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์‹คํ—˜์šฉ ํ•จ์ˆ˜๋ฅผ ๋ณ‘๋ ฌ ๋‹จ๊ณ„๋กœ ๊ต์ฒด์˜ ์˜ˆ์‹œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

์ƒ˜ํ”Œ

์ด๋Ÿฌํ•œ ์ƒ˜ํ”Œ์€ ๋ฌธ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

๋ณ‘๋ ฌ๋กœ ์ž‘์—… ์ˆ˜ํ–‰(๋ธŒ๋žœ์น˜ ์‚ฌ์šฉ)

์›Œํฌํ”Œ๋กœ์— ๋™์‹œ์— ์‹คํ–‰ ๊ฐ€๋Šฅํ•œ ์„œ๋กœ ๋‹ค๋ฅธ ์—ฌ๋Ÿฌ ๋‹จ๊ณ„ ์ง‘ํ•ฉ์ด ์žˆ๋Š” ๊ฒฝ์šฐ, ์ด๋Ÿฌํ•œ ์ง‘ํ•ฉ์„ ๋ณ‘๋ ฌ ๋ธŒ๋žœ์น˜์— ๋ฐฐ์น˜ํ•˜์—ฌ ๋‹จ๊ณ„๋ฅผ ์™„๋ฃŒํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ์ด ์‹œ๊ฐ„์„ ์ค„์ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์‹œ์—์„œ๋Š” ์‚ฌ์šฉ์ž ID๋ฅผ ์›Œํฌํ”Œ๋กœ์— ์ธ์ˆ˜๋กœ ์ „๋‹ฌํ•˜๊ณ  ์„œ๋กœ ๋‹ค๋ฅธ ๋‘ ์„œ๋น„์Šค์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ‘๋ ฌ๋กœ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค. ๊ณต์œ  ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ธŒ๋žœ์น˜์—์„œ ๊ฐ’์„ ๊ธฐ๋กํ•˜๊ณ  ๋ธŒ๋žœ์น˜๊ฐ€ ์™„๋ฃŒ๋œ ํ›„์— ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

YAML

main:
  params: [input]
  steps:
    - init:
        assign:
          - userProfile: {}
          - recentItems: []
    - enrichUserData:
        parallel:
          shared: [userProfile, recentItems]  # userProfile and recentItems are shared to make them writable in the branches
          branches:
            - getUserProfileBranch:
                steps:
                  - getUserProfile:
                      call: http.get
                      args:
                        url: '${"https://example.com/users/" + input.userId}'
                      result: userProfile
            - getRecentItemsBranch:
                steps:
                  - getRecentItems:
                      try:
                        call: http.get
                        args:
                          url: '${"https://example.com/items?userId=" + input.userId}'
                        result: recentItems
                      except:
                        as: e
                        steps:
                          - ignoreError:
                              assign:  # continue with an empty list if this call fails
                                - recentItems: []

JSON

{
  "main": {
    "params": [
      "input"
    ],
    "steps": [
      {
        "init": {
          "assign": [
            {
              "userProfile": {}
            },
            {
              "recentItems": []
            }
          ]
        }
      },
      {
        "enrichUserData": {
          "parallel": {
            "shared": [
              "userProfile",
              "recentItems"
            ],
            "branches": [
              {
                "getUserProfileBranch": {
                  "steps": [
                    {
                      "getUserProfile": {
                        "call": "http.get",
                        "args": {
                          "url": "${\"https://example.com/users/\" + input.userId}"
                        },
                        "result": "userProfile"
                      }
                    }
                  ]
                }
              },
              {
                "getRecentItemsBranch": {
                  "steps": [
                    {
                      "getRecentItems": {
                        "try": {
                          "call": "http.get",
                          "args": {
                            "url": "${\"https://example.com/items?userId=\" + input.userId}"
                          },
                          "result": "recentItems"
                        },
                        "except": {
                          "as": "e",
                          "steps": [
                            {
                              "ignoreError": {
                                "assign": [
                                  {
                                    "recentItems": []
                                  }
                                ]
                              }
                            }
                          ]
                        }
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      }
    ]
  }
}

๋ณ‘๋ ฌ๋กœ ํ•ญ๋ชฉ ์ฒ˜๋ฆฌ(๋ณ‘๋ ฌ ๋ฃจํ”„ ์‚ฌ์šฉ)

๋ชฉ๋ก์˜ ๊ฐ ํ•ญ๋ชฉ์— ๋™์ผํ•œ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ๋ณ‘๋ ฌ ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์‹คํ–‰์„ ๋” ๋น ๋ฅด๊ฒŒ ์™„๋ฃŒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณ‘๋ ฌ ๋ฃจํ”„๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์—ฌ๋Ÿฌ ๋ฃจํ”„ ๋ฐ˜๋ณต์„ ๋™์‹œ์— ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ผ๋ฐ˜์ ์ธ for ๋ฃจํ”„์™€ ๋‹ฌ๋ฆฌ ๋ฐ˜๋ณต์€ ์ˆœ์„œ์— ๊ด€๊ณ„์—†์ด ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์‹œ์—์„œ๋Š” ๋ณ‘๋ ฌ for ๋ฃจํ”„์—์„œ ์‚ฌ์šฉ์ž ์•Œ๋ฆผ ์ง‘ํ•ฉ์„ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค.

YAML

main:
  params: [input]
  steps:
    - sendNotifications:
        parallel:
          for:
            value: notification
            in: ${input.notifications}
            steps:
              - notify:
                  call: http.post
                  args:
                    url: https://example.com/sendNotification
                    body:
                      notification: ${notification}

JSON

{
  "main": {
    "params": [
      "input"
    ],
    "steps": [
      {
        "sendNotifications": {
          "parallel": {
            "for": {
              "value": "notification",
              "in": "${input.notifications}",
              "steps": [
                {
                  "notify": {
                    "call": "http.post",
                    "args": {
                      "url": "https://example.com/sendNotification",
                      "body": {
                        "notification": "${notification}"
                      }
                    }
                  }
                }
              ]
            }
          }
        }
      }
    ]
  }
}

๋ฐ์ดํ„ฐ ์ง‘๊ณ„(๋ณ‘๋ ฌ ๋ฃจํ”„ ์‚ฌ์šฉ)

ํ•ญ๋ชฉ ์ง‘ํ•ฉ์„ ์ฒ˜๋ฆฌํ•˜๋ฉด์„œ ๊ฐ ํ•ญ๋ชฉ์— ์ˆ˜ํ–‰๋œ ์ž‘์—…์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ˆ˜์ง‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด ์ƒ์„ฑ๋œ ํ•ญ๋ชฉ์˜ ID๋ฅผ ์ถ”์ ํ•˜๊ฑฐ๋‚˜ ์˜ค๋ฅ˜๊ฐ€ ์žˆ๋Š” ํ•ญ๋ชฉ ๋ชฉ๋ก์„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ์˜ˆ์‹œ์—์„œ๋Š” ๊ณต๊ฐœ BigQuery ๋ฐ์ดํ„ฐ ์„ธํŠธ์— ๋Œ€ํ•œ ๊ฐœ๋ณ„ ์ฟผ๋ฆฌ 10๊ฐœ๊ฐ€ ๋ฌธ์„œ ๋˜๋Š” ๋ฌธ์„œ ์ง‘ํ•ฉ์˜ ๋‹จ์–ด ์ˆ˜๋ฅผ ๊ฐ๊ฐ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๊ณต์œ  ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋‹จ์–ด ์ˆ˜๋ฅผ ๋ˆ„์‚ฐํ•˜๊ณ  ๋ชจ๋“  ๋ฐ˜๋ณต์ด ์™„๋ฃŒ๋œ ํ›„์— ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์›Œํฌํ”Œ๋กœ๋Š” ๋ชจ๋“  ๋ฌธ์„œ์˜ ๋‹จ์–ด ์ˆ˜๋ฅผ ๊ณ„์‚ฐํ•œ ํ›„ ํ•ฉ๊ณ„๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

YAML

# Use a parallel loop to make ten queries to a public BigQuery dataset and
# use a shared variable to accumulate a count of words; after all iterations
# complete, return the total number of words across all documents
main:
  params: [input]
  steps:
    - init:
        assign:
          - numWords: 0
          - corpuses:
              - sonnets
              - various
              - 1kinghenryvi
              - 2kinghenryvi
              - 3kinghenryvi
              - comedyoferrors
              - kingrichardiii
              - titusandronicus
              - tamingoftheshrew
              - loveslabourslost
    - runQueries:
        parallel:  # 'numWords' is shared so it can be written within the parallel loop
          shared: [numWords]
          for:
            value: corpus
            in: ${corpuses}
            steps:
              - runQuery:
                  call: googleapis.bigquery.v2.jobs.query
                  args:
                    projectId: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
                    body:
                      useLegacySql: false
                      query: ${"SELECT COUNT(DISTINCT word) FROM `bigquery-public-data.samples.shakespeare` " + " WHERE corpus='" + corpus + "' "}
                  result: query
              - add:
                  assign:
                    - numWords: ${numWords + int(query.rows[0].f[0].v)}  # first result is the count
    - done:
        return: ${numWords}

JSON

{
  "main": {
    "params": [
      "input"
    ],
    "steps": [
      {
        "init": {
          "assign": [
            {
              "numWords": 0
            },
            {
              "corpuses": [
                "sonnets",
                "various",
                "1kinghenryvi",
                "2kinghenryvi",
                "3kinghenryvi",
                "comedyoferrors",
                "kingrichardiii",
                "titusandronicus",
                "tamingoftheshrew",
                "loveslabourslost"
              ]
            }
          ]
        }
      },
      {
        "runQueries": {
          "parallel": {
            "shared": [
              "numWords"
            ],
            "for": {
              "value": "corpus",
              "in": "${corpuses}",
              "steps": [
                {
                  "runQuery": {
                    "call": "googleapis.bigquery.v2.jobs.query",
                    "args": {
                      "projectId": "${sys.get_env(\"GOOGLE_CLOUD_PROJECT_ID\")}",
                      "body": {
                        "useLegacySql": false,
                        "query": "${\"SELECT COUNT(DISTINCT word) FROM `bigquery-public-data.samples.shakespeare` \" + \" WHERE corpus='\" + corpus + \"' \"}"
                      }
                    },
                    "result": "query"
                  }
                },
                {
                  "add": {
                    "assign": [
                      {
                        "numWords": "${numWords + int(query.rows[0].f[0].v)}"
                      }
                    ]
                  }
                }
              ]
            }
          }
        }
      },
      {
        "done": {
          "return": "${numWords}"
        }
      }
    ]
  }
}

๋‹ค์Œ ๋‹จ๊ณ„