よくあるアンチパターン
よくあるアンチパターン
Section titled “よくあるアンチパターン”GASでよくあるアンチパターンと、実際に事故った構造を詳しく解説します。
A. 実行時間制限を無視した処理
Section titled “A. 実行時間制限を無視した処理”実際に事故った構造
Section titled “実際に事故った構造”// ❌ アンチパターン: 実行時間制限を考慮していないfunction processLargeDataset() { const data = fetchLargeDataset(); // 10,000件のデータ data.forEach(item => { processItem(item); // 各アイテムの処理に1秒かかる // 合計: 10,000秒 = 約2.8時間 → タイムアウト });}
function fetchLargeDataset() { const response = UrlFetchApp.fetch('https://api.example.com/data'); return JSON.parse(response.getContentText()); // 10,000件のデータ}なぜ事故るか:
- 実行時間制限: GASの実行時間制限(6分または30分)を超えると強制終了される
- 中途半端な状態: 処理が途中で終了し、データが中途半端な状態になる
- 再実行の困難: どこまで処理したか分からず、再実行が困難
実際の事故例:
10:00:00 - スクリプト開始(実行時間: 0秒)10:00:01 - データ取得開始(10,000件のデータ)10:05:30 - データ処理中(実行時間: 5分30秒、3,000件処理済み)10:06:00 - 実行時間制限に達する(6分)10:06:01 - スクリプトが強制終了10:06:02 - エラー: "Maximum execution time exceeded"10:06:03 - 3,000件だけ処理され、残り7,000件は未処理設計レビューでの指摘文例:
【指摘】実行時間制限を考慮していません。【問題】大量のデータを一度に処理すると、実行時間制限(6分)を超えて強制終了されます。【影響】処理が中途半端な状態で終了、データの不整合、再実行の困難【推奨】バッチ処理に分割し、実行時間をチェックして5分で停止するB. クォータ制限を無視した処理
Section titled “B. クォータ制限を無視した処理”実際に事故った構造
Section titled “実際に事故った構造”// ❌ アンチパターン: クォータ制限を考慮していないfunction sendBulkEmails(recipients) { recipients.forEach(recipient => { MailApp.sendEmail({ to: recipient.email, subject: recipient.subject, body: recipient.body, }); // 問題: 100通を超えるとエラー // 問題: エラーハンドリングがない });}
function fetchManyAPIs(urls) { urls.forEach(url => { UrlFetchApp.fetch(url); // 問題: 20,000回/日の制限を超える可能性 });}なぜ事故るか:
- 1日の実行時間制限: 6時間(無料プラン)を超えるとエラー
- 同時実行数制限: 30(無料プラン)を超えるとエラー
- URLフェッチ制限: 20,000回/日を超えるとエラー
- メール送信制限: 100通/日(無料プラン)を超えるとエラー
実際の事故例:
10:00:00 - メール送信開始(200通のメール)10:00:01 - 1通目送信成功10:00:02 - 2通目送信成功...10:00:50 - 100通目送信成功10:00:51 - 101通目送信失敗: "Daily email limit exceeded"10:00:52 - エラーが発生し、残り100通は送信されない設計レビューでの指摘文例:
【指摘】クォータ制限を考慮していません。【問題】1日の送信制限(100通)を超えるとエラーが発生します。【影響】一部のメールが送信されない、エラー処理が不十分【推奨】クォータをチェックし、制限を超える場合はキューに保存して次回実行するC. エラーハンドリングの不備
Section titled “C. エラーハンドリングの不備”実際に事故った構造
Section titled “実際に事故った構造”// ❌ アンチパターン: エラーハンドリングがないfunction updateSpreadsheet() { const response = UrlFetchApp.fetch('https://api.example.com/data'); const data = JSON.parse(response.getContentText());
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet(); sheet.getRange('A1').setValue(data.value); // 問題: エラーが発生すると、スクリプト全体が停止 // 問題: エラーの原因が分からない}なぜ事故るか:
- エラーの伝播: エラーが発生すると、スクリプト全体が停止する
- エラーの原因不明: エラーメッセージが不十分で、原因が分からない
- ロールバックの困難: エラー発生時、既に処理されたデータのロールバックが困難
実際の事故例:
10:00:00 - スクリプト開始10:00:01 - データ取得開始10:00:02 - API呼び出し失敗: "Connection timeout"10:00:03 - エラーが発生し、スクリプトが停止10:00:04 - エラーログが残らない10:00:05 - 管理者がエラーに気づかない設計レビューでの指摘文例:
【指摘】エラーハンドリングが不十分です。【問題】エラーが発生すると、スクリプト全体が停止し、エラーの原因が分かりません。【影響】処理の中断、エラーの原因不明、再実行の困難【推奨】try-catchでエラーを捕捉し、ログに記録して通知を送信するD. トランザクション境界の不備
Section titled “D. トランザクション境界の不備”実際に事故った構造
Section titled “実際に事故った構造”// ❌ アンチパターン: トランザクション境界がないfunction updateSpreadsheet() { const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
// 問題: 各更新が個別のトランザクション sheet.getRange('A1').setValue('Value 1'); sheet.getRange('A2').setValue('Value 2'); sheet.getRange('A3').setValue('Value 3'); // ここでエラー // A1とA2だけ更新され、A3は更新されない(データ不整合)}なぜ事故るか:
- 部分的な更新: エラーが発生すると、一部だけ更新される
- データ不整合: データの整合性が保たれない
- ロールバックの困難: 既に更新されたデータのロールバックが困難
実際の事故例:
10:00:00 - スクリプト開始10:00:01 - A1を更新: 成功10:00:02 - A2を更新: 成功10:00:03 - A3を更新: エラー(権限エラー)10:00:04 - スクリプトが停止10:00:05 - A1とA2は更新されているが、A3は更新されていない(データ不整合)設計レビューでの指摘文例:
【指摘】トランザクション境界が不適切です。【問題】各更新が個別のトランザクションのため、エラー時に部分的な更新が発生します。【影響】データの不整合、ロールバックの困難【推奨】範囲単位で一度に更新し、アトミックに処理するE. 外部API呼び出しのタイムアウト設定なし
Section titled “E. 外部API呼び出しのタイムアウト設定なし”実際に事故った構造
Section titled “実際に事故った構造”// ❌ アンチパターン: タイムアウト設定がないfunction fetchData() { const response = UrlFetchApp.fetch('https://api.example.com/data'); // 問題: タイムアウト設定がない(デフォルト60秒) // 問題: エラーハンドリングがない return JSON.parse(response.getContentText());}なぜ事故るか:
- 長時間の待機: 外部APIの応答が遅い場合、60秒まで待機する
- 実行時間の浪費: 実行時間制限(6分)のうち、60秒を浪費する
- エラーの伝播: エラーが発生すると、スクリプト全体が停止する
実際の事故例:
10:00:00 - スクリプト開始(実行時間: 0秒)10:00:01 - 外部API呼び出し開始10:00:30 - 外部APIが応答しない(30秒経過)10:01:00 - タイムアウト(60秒経過)10:01:01 - エラーが発生し、スクリプトが停止10:01:02 - 実行時間の10%を浪費設計レビューでの指摘文例:
【指摘】外部API呼び出しにタイムアウト設定がありません。【問題】外部APIの応答が遅い場合、60秒まで待機し、実行時間を浪費します。【影響】実行時間の浪費、エラーの伝播、パフォーマンスの低下【推奨】タイムアウトを30秒に設定し、エラーハンドリングとフォールバックを実装するF. 状態管理の不備
Section titled “F. 状態管理の不備”実際に事故った構造
Section titled “実際に事故った構造”// ❌ アンチパターン: 状態を保持しないfunction processLargeDataset() { const data = fetchLargeDataset(); // 10,000件のデータ let processed = 0;
data.forEach(item => { processItem(item); processed++; // 問題: 実行時間制限で停止した場合、どこまで処理したか分からない });}なぜ事故るか:
- 再実行の困難: 実行時間制限で停止した場合、どこまで処理したか分からない
- 重複処理: 再実行時に、既に処理したデータを再度処理する可能性がある
- データ不整合: 重複処理により、データが不整合になる
実際の事故例:
10:00:00 - スクリプト開始10:00:01 - データ処理開始(10,000件)10:05:30 - 3,000件処理済み(実行時間: 5分30秒)10:06:00 - 実行時間制限で停止10:06:01 - 次回実行時に、最初から処理を開始10:06:02 - 既に処理した3,000件を再度処理(重複処理)設計レビューでの指摘文例:
【指摘】状態管理が不適切です。【問題】実行時間制限で停止した場合、どこまで処理したか分からず、再実行が困難です。【影響】重複処理、データ不整合、再実行の困難【推奨】PropertiesServiceに処理位置を保存し、次回実行時に続きから処理するGASでよくあるアンチパターンは、実行時間制限、クォータ制限、エラーハンドリング、トランザクション境界、状態管理の不備が主な原因です。
重要なポイント:
- 実行時間制限: 6分または30分の制限を考慮する
- クォータ制限: 1日の制限をチェックする
- エラーハンドリング: try-catchでエラーを捕捉し、ログに記録する
- トランザクション境界: 範囲単位でアトミックに処理する
- 状態管理: PropertiesServiceに状態を保存する
これらのアンチパターンを避けることで、堅牢で効率的なGASアプリケーションを構築できます。