Apps Scriptが6分で止まる。分割で粘るか、作り直すかの分かれ目 | GH Media
URLがコピーされました

Apps Scriptが6分で止まる。分割で粘るか、作り直すかの分かれ目

URLがコピーされました
Apps Scriptが6分で止まる。分割で粘るか、作り直すかの分かれ目

毎朝7時に動いていた集計スクリプトが、先月あたりから途中で止まるようになった。ログを見ると「最大実行時間を超過しました(Exceeded maximum execution time)」。コードは1行も変えていません。変わったのは扱うデータの量だけです。

この時点で相談されると、たいてい2つの誤解がセットで出てきます。「Workspace の有料プランなら上限が延びるはず」と、「処理を速くすれば直る」です。どちらも今は成り立ちません。

6分は延ばせない

まず前提を固めておきます。スクリプト1回あたりの実行時間の上限は6分で、無料の個人アカウントでも Google Workspace でも同じです。かつて Workspace 系アカウントに30分の枠があった時期がありますが、これは廃止されています。プランを上げても、管理コンソールを探しても、この6分は動きません。

関連する上限もあわせて把握しておくと、後の判断が速くなります。

項目個人アカウントGoogle Workspace
スクリプト1回の実行時間6分6分
トリガー実行時間の1日合計90分6時間
同時実行数3030
UrlFetch の1日あたり呼び出し20,000100,000
スクリプトあたりの時間主導トリガー数2020

見落とされやすいのは2行目です。1回6分に収まっていても、トリガーで動かす処理の合計が1日の枠を食い切ると、その日の残りは動きません。「途中まで動いて止まる」のではなく「そもそも起動しない」形で出るため、実行時間超過とは症状が違います。ログに何も残らないときは、まずこちらを疑ってください。

詰まっているのは計算ではないことが多い

「処理を速くすれば直る」が外れるのは、6分を食っているのが計算ではないからです。

Apps Script が遅くなる原因は、圧倒的にスプレッドシートとの往復です。ループの中で getValue()setValue() を1セルずつ呼んでいると、1回ごとにサービス境界をまたぐ通信が発生します。行数が1,000から10,000に増えれば、往復も10倍になる。ロジックそのものは一瞬で終わっているのに、待ち時間だけが積み上がっていきます。

// 往復が行数ぶん発生する
for (let i = 2; i <= lastRow; i++) {
  const v = sheet.getRange(i, 3).getValue();
  sheet.getRange(i, 4).setValue(v * 1.1);
}

// 往復は2回で済む
const values = sheet.getRange(2, 3, lastRow - 1, 1).getValues();
const result = values.map(([v]) => [v * 1.1]);
sheet.getRange(2, 4, result.length, 1).setValues(result);

外部 API を呼んでいる場合も同じ構造です。1件ずつ UrlFetchApp.fetch() を呼んでいるなら、fetchAll() でまとめられないかを先に見ます。

ここを直すだけで6分に収まるケースは珍しくありません。分割実行の仕組みを作るのは、まとめ読み・まとめ書きに直したあとの話です。順番を逆にすると、遅いままの処理を分割しただけの、複雑で遅い仕組みが残ります。

1セルずつ往復する実装ではデータ量に比例して待ち時間が積み上がるのに対し、範囲でまとめて読み書きする実装では往復回数が一定に保たれることを示した比較図

分割で粘るときの型

まとめ読み・まとめ書きに直してもなお6分に収まらないなら、分割実行に進みます。考え方は単純で、「どこまで終わったか」を外に記録して、次の実行が続きから始める構造にします。

  1. 処理開始時刻を記録し、経過が4分30秒程度を超えたらループを抜ける(6分ぎりぎりまで粘らない)
  2. 中断した位置を PropertiesService に保存する
  3. 未処理が残っていれば、ScriptApp.newTrigger() で数分後の単発トリガーを仕込んで自分を終了する
  4. 次の実行は保存した位置から再開する
  5. 全件終わったら記録を消し、後片付けとして自分が作ったトリガーを削除する

5番目を忘れると、スクリプトあたり20個というトリガー上限に静かにぶつかります。しばらく動いていたのに突然トリガーが作れなくなる、という形で表面化するため、原因にたどり着くまで時間がかかる部類の不具合です。

この型が向いているのは、処理が行単位に独立していて、途中で止まっても中途半端な状態が残らない場合です。1行ずつ独立した転記や集計はここに当てはまります。

粘っても意味がないとき

一方で、分割実行を入れても解決にならないパターンがあります。判断の材料は処理時間ではなく、次の3点です。

中断が業務的に許されない処理。在庫の引き当てや請求データの確定のように、途中まで反映された状態が実害になるものは、分割してはいけません。Apps Script には複数の処理をまとめて取り消す仕組みがないため、失敗したときに手で戻すことになります。

データ量が増え続ける前提の処理。いま6分を分割で凌いでも、半年後には分割数が倍になります。分割は時間を買っているだけで、増加そのものは止まりません。1年後の件数を見積もって、そこでも成立するかを確認してください。成立しないなら、置き場所をスプレッドシートの外に移す設計に切り替える時期です。

作った人以外が直せない処理。分割実行を入れると、コードは目に見えて複雑になります。もともと引き継ぎが難しかったスクリプトは、ここでさらに触れないものになります。この状態のまま放置されたスクリプトがどう問題化するかはApps Script が保守されなくなるときで扱っています。

3つのどれかに当てはまるなら、粘る先を変えたほうが早い。スプレッドシートを表計算として使い続けるべきか、データの置き場所ごと見直すべきかの判断軸はExcel 運用をいつシステム化に切り替えるかにまとめています。外部 API を呼ぶスクリプトを増やす前の点検はApps Script の外部通信を棚卸しするを参照してください。

次にやること

止まっているスクリプトがあるなら、**まず getValue()setValue() がループの中にあるかどうかだけ見てください。**あれば、そこが原因である可能性が高く、分割実行を検討する前にやることが残っています。

ループの外に出しても収まらない場合は、そのスクリプトが上の3条件に当てはまるかを確認します。当てはまらなければ分割実行で数年は持ちます。当てはまるなら、粘るための工数を作り直しに振り替えたほうが、結果的に安く済みます。

既存スクリプトの棚卸しや、どこまでを Apps Script で維持しどこから別の作りに移すかの切り分けは、グリームハブの IT・Google Workspace 無料相談で承っています。データ量の伸び方や業務の依存関係によって結論は変わるため、個別にご相談ください。お問い合わせからどうぞ。

Sources

URLがコピーされました

グリームハブ株式会社は、変化の激しい時代において、アイデアを形にし、人がもっと自由に、もっと創造的に生きられる世界を目指しています。

記事を書いた人

鈴木 翔

鈴木 翔

技術の可能性に魅了され、学生時代からプログラミングとデジタルアートの分野に深い関心を持つ

関連記事

「Google Workspace」の記事一覧を見る