39

私のGoogleアプリスクリプトは、ユーザーのGoogleドライブファイルを繰り返し処理し、ファイルを他のフォルダーにコピーしたり、時には移動したりしています。スクリプトは常に 5 分後に停止し、ログにエラー メッセージは記録されません。

1回の実行で数十または時には数千のファイルをソートしています。

設定や回避策はありますか?

4

12 に答える 12

91

できることの 1 つ (もちろん、これは何を達成しようとしているかによって異なります) は次のとおりです。

  1. 必要な情報 (つまり、ループ カウンターなど) をスプレッドシートまたは別の永続的なストア (つまり、ScriptProperties) に保存します。
  2. スクリプトを 5 分程度ごとに終了させます。
  3. スクリプトを 5 分ごとに実行する時間駆動型トリガーを設定します (または、スクリプト サービスを使用してプログラムでトリガーを作成します)。
  4. 実行するたびに、使用した永続ストアから保存されたデータを読み取り、中断したところからスクリプトを実行し続けます。

これは、万能のソリューションではありません。コードを投稿すると、人々がより適切に支援できるようになります。

以下は、私が毎日使用しているスクリプトからの簡単なコードの抜粋です。

function runMe() {
  var startTime= (new Date()).getTime();
  
  //do some work here
  
  var scriptProperties = PropertiesService.getScriptProperties();
  var startRow= scriptProperties.getProperty('start_row');
  for(var ii = startRow; ii <= size; ii++) {
    var currTime = (new Date()).getTime();
    if(currTime - startTime >= MAX_RUNNING_TIME) {
      scriptProperties.setProperty("start_row", ii);
      ScriptApp.newTrigger("runMe")
               .timeBased()
               .at(new Date(currTime+REASONABLE_TIME_TO_WAIT))
               .create();
      break;
    } else {
      doSomeWork();
    }
  }
  
  //do some more work here
  
}

注 1: 変数REASONABLE_TIME_TO_WAITは、新しいトリガーが起動するのに十分な大きさである必要があります。(私は 5 分に設定しましたが、それより短くてもよいと思います)。

注#2:doSomeWork()比較的迅速に実行される関数でなければなりません(1分未満と言います)。

注 3 : Google は を廃止し、代わりScript Propertiesに導入しました。Properties Serviceそれに応じて関数が変更されました。

注 4: 関数が 2 回呼び出されると、for ループの i 番目の値が文字列として取得されます。したがって、整数に変換する必要があります

于 2011-12-22T18:36:29.057 に答える
63

クォータ

1 つのスクリプトの最大実行時間は 6 分/実行です
- https://developers.google.com/apps-script/guides/services/quotas

しかし、他にも理解しておくべき制限があります。たとえば、合計トリガー実行時間は 1 日あたり 1 時間しか許可されていないため、長い関数を 12 の異なる 5 分間のブロックに分割することはできません。

最適化

とはいえ、実行に 6 分もかからなければならない理由はほとんどありません。JavaScript は、数秒で数千行のデータをソートしても問題ありません。パフォーマンスを低下させている可能性があるのは、Google Apps 自体へのサービス呼び出しです。

読み取りと書き込みの回数を最小限に抑えることで、組み込みのキャッシュを最大限に活用するスクリプトを作成できます。交互の読み取りコマンドと書き込みコマンドは低速です。スクリプトを高速化するには、1 つのコマンドですべてのデータを配列に読み取り、配列内のデータに対して任意の操作を実行し、1 つのコマンドでデータを書き出します。
- https://developers.google.com/apps-script/best_practices

バッチ処理

あなたができる最善のことは、サービス呼び出しの数を減らすことです。Google は、ほとんどの API 呼び出しのバッチ バージョンを許可することでこれを可能にします。

些細な例として、これの代わりに:

for (var i = 1; i <= 100; i++) {
  SpreadsheetApp.getActiveSheet().deleteRow(i);
}

これを行います:

SpreadsheetApp.getActiveSheet().deleteRows(i, 100);

最初のループでは、シートで deleteRow を 100 回呼び出すだけでなく、アクティブなシートも 100 回取得する必要がありました。2 番目のバリエーションは、最初のバリエーションより数桁優れたパフォーマンスを発揮するはずです。

読み取りと書き込みの織り交ぜ

さらに、読み書きを頻繁に行ったり来たりしないように十分に注意する必要があります。バッチ操作で得られる可能性を失うだけでなく、Google は組み込みのキャッシュを使用できなくなります。

読み取りを行うたびに、最新のデータを読み取っていることを確認するために、最初に書き込みキャッシュを空にする (コミットする) 必要があります ( を呼び出してキャッシュの書き込みを強制できますSpreadsheetApp.flush())。同様に、書き込みを行うたびに、読み取りキャッシュは無効になるため、破棄する必要があります。したがって、読み取りと書き込みのインターリーブを回避できれば、キャッシュを最大限に活用できます。
- http://googleappsscript.blogspot.com/2010/06/optimizing-spreadsheet-operations.html

たとえば、これの代わりに:

sheet.getRange("A1").setValue(1);
sheet.getRange("B1").setValue(2);
sheet.getRange("C1").setValue(3);
sheet.getRange("D1").setValue(4);

これを行います:

sheet.getRange("A1:D1").setValues([[1,2,3,4]]);

関数呼び出しの連鎖

最後の手段として、関数が実際に 6 分以内に終了できない場合は、呼び出しを連鎖させるか、関数を分割して、より小さなデータ セグメントで作業することができます。

キャッシュ サービス(一時的) またはプロパティ サービス(永続的) バケットにデータを保存して、複数の実行にわたって取得できます (Google Apps スクリプトにはステートレスな実行があるため)。

別のイベントを開始したい場合は、Trigger Builder クラスを使用して独自のトリガーを作成するか、タイトなタイムテーブルで繰り返しトリガーをセットアップできます。

于 2015-04-22T14:29:19.293 に答える
32

また、Googleサービスへの呼び出しの量を最小限に抑えるようにしてください。たとえば、スプレッドシートのセルの範囲を変更する場合は、各セルを読み取らずに、変更して保存し直してください。代わりに、範囲全体を(Range.getValues()を使用して)メモリに読み込み、変更してすべてを一度に保存します(Range.setValues()を使用)。

これにより、実行時間を大幅に節約できます。

于 2012-01-02T16:28:12.557 に答える
15

Anton Soradoi の回答は問題ないようですが、データを一時シートに保存する代わりにキャッシュ サービスを使用することを検討してください。

 function getRssFeed() {
   var cache = CacheService.getPublicCache();
   var cached = cache.get("rss-feed-contents");
   if (cached != null) {
     return cached;
   }
   var result = UrlFetchApp.fetch("http://example.com/my-slow-rss-feed.xml"); // takes 20 seconds
   var contents = result.getContentText();
   cache.put("rss-feed-contents", contents, 1500); // cache for 25 minutes
   return contents;
 }

また、2014 年 4 月現在、スクリプトの実行時間の制限 は 6 分です。


G Suite Business / Enterprise / Education および早期アクセス ユーザー:

2018 年 8 月現在、これらのユーザーの最大スクリプト実行時間は 30 分に設定されています。

于 2014-11-26T11:09:35.187 に答える
11

スクリプトの制限である6分未満で作業を分割する方法を見つけてください。最初のパスでは、ファイルとフォルダーのリストを繰り返してスプレッドシートに保存し、パート2の時間駆動型トリガーを追加できます。

パート2では、処理中にリスト内の各エントリを削除します。リストに項目がない場合は、トリガーを削除してください。

これは、約1ダースの異なるスプレッドシートに広がる約1500行のシートを処理する方法です。スプレッドシートへの呼び出し回数が多いため、タイムアウトになりますが、トリガーが再度実行されると続行されます。

于 2013-01-22T03:25:33.360 に答える
5

ScriptDB を使用して、ループ内で大量の情報を処理しながら自分の場所を保存しました。スクリプトは 5 分の制限を超える可能性があります。各実行中に ScriptDb を更新することにより、スクリプトはデータベースから状態を読み取り、すべての処理が完了するまで中断したところから再開できます。この戦略を試してみてください。満足のいく結果が得られると思います。

于 2012-07-27T20:28:48.277 に答える