понедельник, 29 марта 2021 г.

Длительные операции в google script. Обходим ограничение в 6 минут.

 Насколько долго можно выполнять операции в google apps script? Согласно официальной документации, скрипты имеют ограничения. Время работы всех триггеров для вашей учётной записи ограничено 90 минутами в день, запросы на внешний сервер 20000, максимальное время работы функции скрипта - 6 минут, хватает, в общем, квот.

Зачастую, для решения тех или иных задач, мы используем не очень оптимальные методы работы и наш скрипт может не успеть за 6 минут обработать данные - время кончилось, скрипт остановился. Что делать в этом случае? Оптимизировать работу с данными, ускорять алгоритмы.

Что делать в том случае когда повлиять на скорость выполнения скрипта мы не можем? 

  1. Переносить всю логику работы на свой сервер.
  2. Перенести работу с данными в браузер, нагружая тем самым не скриптовый движок google, а ваш браузер, который во много раз мощнее и быстрее тех мощностей выделяемых google.
1 рассматривать не будем - слишком всё банально). 2 - интереснее.

Рассмотрим задачу получения списка файлов в таблицу google, к которым у вас есть доступ. То есть, это все ваши файлы, папки; не ваши, но те к которым у вас есть хоть какой-нибудь доступ. Сколько таких может быть? 20 000, 30 000 и даже больше если вы активно используете google drive. Нам важны не только названия файлов, не только их id, но и кто владелец, какие права есть у нас на эти файлы, открыт ли файл по ссылке, у кого ещё есть права на этот файл, время создания, кто последним редактировал и т.д. и т.п. Для получения всей этой информации мы можем обратиться к Drive api, так как понимаем что DriveApp, "родной" для ScriptApp, с такой задачей не справится. 
Метод list - https://developers.google.com/drive/api/v3/reference/files/list нам подходит как нельзя лучше так как содержит всю нужную информацию. 
При выставлении всех нужных параметров для поиска и указания полей можно прийти к неутешительным выводам - даже если мы выставим pageSize в 1000 файлов за один запрос Drive api автоматически выставит 100 (слишком уж много информации нам нужно). Один запрос может обрабатываться до 2 секунд и более, таким образом мы не сможем получить информацию более чем о 8000 файлах за 80 запросов и за 6 минут запуска функции скрипта. Нужно перенести всё в html код, приправленный js кодом, который будет обращаться по api в google drive при помощи асинхронной функции fetch. И делать это будет не один раз, а ровно столько сколько необходимо для получения информации о файлах на нашем диске. Полученные ответы обрабатывать и передавать в таблицу.

В общем таблица с кодом здесь. В таблице вызывается модальное диалоговое окно, внутри которого и происходят все запросы к drive api, обработка данных и отправка в таблицу. Сортировка происходит по дате последнего просмотра файлов.

Проверено на количестве файлов в 30000, отрабатывать может достаточно долго, главное - запастись терпением. Ограничения в 6 минут нет, так как всё происходит на вашем компьютере. Используя обычную фильтрацию по столбцам можно найти все файлы, открытые вами по ссылке, какие либо файлы, давным давно считавшиеся утерянными. Фантазии должно хватить на всё.)
Информация берётся из api, но почему то не всегда содержит полную информацию о файлах.
Польза данного подхода - возможность переноса сложных, долгих процессов вычислений с плеч google, на ваш компьютер и возможное увеличение скорости работы скриптов.
Недостаток - невозможность запуска в качестве триггера, т.к. необходимо формирование html окна.
Будут вопросы - милости прошу в комментарии. В комментариях также, интересно было бы почитать про количество ваших файлов которые смог обработать скрипт и время работы скрипта.

Особая благодарность каналу телеграм - Google Таблицы и чату Чат | Google Таблицы и скрипты за продвижение технологий google скриптинга и таблицестроительства.

27 комментариев:

  1. Большое Вам спасибо, умнейший человек

    ОтветитьУдалить
  2. Респектише . реально я уперся со своим под завязку 100 гиговым диском (более 15 лет использую) !
    "Переносить всю логику работы на свой сервер
    " имеется ввиду самому парсить содержимое а не получать вывод от апи ?

    ОтветитьУдалить
  3. Ответы
    1. Доброго дня.
      В начале статьи приведена ссылка на официальную страницу с квотами google. Посмотрите всё что связано с google workspace.

      Удалить
  4. Я новичок в GAS, пытаюсь разобраться в Вашем коде. Можете, пожалуйста, подсказать какую часть кода можно поменять чтоб попробовать запустить свой скрипт.

    ОтветитьУдалить
    Ответы
    1. Доброго дня.
      Части касающейся gas в коде совсем чуть чуть, остальное js.
      Не зная чего вы собрались делать, не смогу что либо посоветовать.

      Удалить
    2. Извините за несвоевременный ответ. Я с Кей Коллектора выгружаю данные, примерно, по 20000 тысячам ключей. И мне нужно с помощью написанного ВПР на gas(это я уже сделал) подтянуть данные в разные таблицы. И мне 6 минут работы скрипта не хватает.

      Удалить
    3. Доброго дня. Вам скорее всего нужно пересмотреть ваш ВПР, оптимизировав и уменьшив количество вставок данных в таблицы до минимума. То что построчно вставляете данные в таблицу можно собрать в один массив, и за один же раз вставить в таблицу. Тогда ваш скрипт скорее всего всё успеет за пару минут.

      Удалить
    4. Да я знаю про оптимизацию. Поэтому максимально использую массивы и построчно ничего не делаю, но количество таблиц растет и количество ключей соответственно. Поэтому пробую освоить работу скриптов через Html Service. В данной теме наткнулся на Ваш блог и блог англоязычный.

      Удалить
    5. В таком случае могу только посоветовать изучить функцию fetch на js, sheets api google.
      Так просто взять и поменять что-либо в коде не получится, нужно немножко разбираться в данных получаемых от fetch и их обработке.
      У меня в коде запросы, как таковые, идут к Drive Api. Почитайте в официальной документации как формируются запросы, затем сможете переключится на Sheets Api.

      Удалить
  5. Спасибо за подсказку в направлении поиска информации)

    ОтветитьУдалить
  6. Спасибо за полезные советы.

    ОтветитьУдалить
  7. Можно ли провернуть с данным кодом?
    function myFunction() {
    var ss = SpreadsheetApp.openById("1B7LZIMlqqxCLSSagTyCg2xSqBseAH5hVEtFZTkIplko");
    var sheet = ss.getSheetByName("Лист4"); //лист
    var sht = ss.getSheetByName("Лист1"); //лист
    var lastColumn = sheet.getLastColumn();

    // сортировка 1 строки (с1 - 1)
    var firstValues = sheet.getRange(1, 3, 1, lastColumn - 1).getValues().flat();
    sheet.getRange(1, 3, 1, lastColumn - 1).setValues([sortWithIndices(firstValues)]);

    // сортировка 2 - 10строки по 1 строке
    var i = 2;
    var lastRow = 1000;
    while(i <= lastRow) {
    var values = sheet.getRange(i, 3, 1, lastColumn - 1).getValues().flat();
    var tempValues = sheet.getRange(i, 3, 1, lastColumn - 1).getValues().flat();
    for (var index = 0; index < values.length; index++) {
    values[index] = tempValues[firstValues.sortIndices[index]];
    }
    sheet.getRange(i, 3, 1, lastColumn - 1).setValues([values]);
    i++;
    }
    sheet.deleteColumn(3);//Удалить С
    sheet.sort(1); //сортировка по 1 колонке
    sht.deleteColumn(15)//Удалить О

    function sortWithIndices(toSort) {
    for (var i = 0; i < toSort.length; i++) {
    toSort[i] = [toSort[i], i];
    }
    toSort.sort(function(а,б) {
    // < по ворзрастию
    // > по убыванию
    return ((а == б) ? 0 : (а && (!б || а < б)) ? -1 : 1);
    });
    toSort.sortIndices = [];
    for (var j = 0; j < toSort.length; j++) {
    toSort.sortIndices.push(toSort[j][1]);
    toSort[j] = toSort[j][0];
    }
    return toSort;
    }}

    ОтветитьУдалить
    Ответы
    1. Доброго вечера. Здесь не нужно html окно и вычисления в нём. Нужен рефакторинг кода.
      var values = sheet.getRange(i, 3, 1, lastColumn - 1).getValues().flat();
      var tempValues = sheet.getRange(i, 3, 1, lastColumn - 1).getValues().flat();
      2000 раз берёте строчки. Для чего?

      Удалить
    2. нашел такой скрипт на stuckoverflow и переделал под себя. сам ничего особо в скриптах не смыслю. но нужно было сортировать колонки по алфавиту по строке 1.

      Удалить
    3. оно как бы работает, но не успевает проверить 1000 строк(

      Удалить
    4. Знаете те ли вы как его оптимизировать? Буду очень благодарен если предоставите решение моей проблемы.

      Удалить
    5. Без примера таблицы очень трудно понять что там у вас вообще происходит.

      Удалить
    6. https://docs.google.com/spreadsheets/d/1B7LZIMlqqxCLSSagTyCg2xSqBseAH5hVEtFZTkIplko/edit#gid=1448297076

      Удалить
  8. сначала берет первую строку с ячейки c1 и сортирует её по алфавиту, а затем переносит данные которые были под ячейками перед сортировкой на соответсвующее место. к сожалению скрин тут не прикрепить. я бы мог отправить его где-то еще.

    ОтветитьУдалить
    Ответы
    1. Очень сомнительное удовольствие лицезреть скрины таблиц. Покопайтесь в блоге - узнаете мой e-mail, которому сможете открыть доступ на редактирование к своей таблице.

      Удалить
  9. Как выводить окно загрузки во время выполнения функции?

    ОтветитьУдалить
    Ответы
    1. Доброго дня.
      Во-первых скрипт должен знать сколько всего файлов, а так как запросы отправляются в иттеративном режиме, то узнаём об этом только в конце запросов.
      Во-вторых, наберите в гугл анимация загрузки, и найдите пару примеров.

      Удалить
  10. Здравствуйте!
    Нужно периодически делать копию одного гугл диска на другой. Написал скрипт создающий копию.
    function start() {
    var sourceFolder = "Квартиры";
    var targetFolder = "1 Копия Квартиры";

    var source = DriveApp.getFoldersByName(sourceFolder);
    var target = DriveApp.createFolder(targetFolder);

    if (source.hasNext()) {
    copyFolder(source.next(), target);
    }
    }

    function copyFolder(source, target) {
    var folders = source.getFolders();
    var files = source.getFiles();

    while(files.hasNext()) {
    var file = files.next();
    //Logger.log(file.getName())
    file.makeCopy(file.getName(), target);
    }

    while(folders.hasNext()) {
    var subFolder = folders.next();
    var folderName = subFolder.getName();
    Logger.log(folderName)
    var targetFolder = target.createFolder(folderName);
    copyFolder(subFolder, targetFolder);
    }
    }

    На диске 30 000+ файлов и я уперся в ограничение 6 минут.
    Можно ли докрутить Ваш скрипт реализовать эту задачу?
    Js не знаю.

    ОтветитьУдалить
    Ответы
    1. Доброго дня.
      Можете попробовать докрутить, но нужно будет с js подружиться.

      Удалить