LockService: Как заблокировать лист Google из скрипта? - PullRequest
1 голос
/ 25 февраля 2020


Я использую электронную таблицу Google для хранения контактной информации, которую я собираю с веб-сайта (соответствует GDPR, не беспокойтесь), и я синхронизирую этот лист с Mailchimp, используя собственный скрипт приложения, который работает в фоновом режиме.

Более подробно, я использую временный лист в качестве буфера, который заполняется данными, которые пользователи отправляют через формы сайта (я использую контактную форму 7 Google Sheets Connector). Мой скрипт запускается каждый раз, когда событие «INSERT_ROW» запускается в «буферном» листе. Он анализирует данные, сохраняет их на последнем листе и, в конце концов, освобождает «буфер».
Теперь, зная о потенциальных проблемах параллелизма, я написал код в следующей форме:

function onChange(e) {
  // Script initializaztion

  if (!formNames.includes(formName) || e.changeType != "INSERT_ROW") return;

  let lock = LockService.getScriptLock();

  try {
    lock.waitLock(60000);

    // Get new stuff from Form sheet
    .
    .
    .

    // Get existing data and compare with new
    compareData();

    // New entries
    insertNewDataIntoFinalSheet();

    // Updates
    updatesExistingDataInFinalSheet();

    // Clean up the buffer sheet
    deleteRowsFromSiteForms();

    console.log(formName);

  } catch(e) {
    console.log(e);
  }

  lock.releaseLock();
}

Но я не уверен, что Локк делает то, что ожидал. А в Google Docs не очень ясно, как его использовать.
Если я не правильно понимаю Google Docs, любой Lock

не позволяет одновременно выполнять разделы кода. Этот сервис позволяет сценариям предотвращать одновременный доступ к разделам кода. Это может быть полезно, когда несколько пользователей или процессов изменяют общий ресурс и хотят предотвратить коллизии (Google docs).

Теперь представьте себе следующий сценарий: Пользователь A в своем браузере заполняет форму. В то же самое время (ну, почти) Пользователь B делает то же самое в другом браузере где-то еще в мире. Соединитель CF7 браузера пользователя А отправляет данные на мой «буферный» лист и получает блокировку, чтобы мой скрипт начал свою работу. Через наносекунду пользовательская форма B вставляет другие данные в «буфер», вызывая, таким образом, выполнение сценария. Этот новый экземпляр сценария пытается получить блокировку, но блокируется блокировкой пользователя A.

В то же время экземпляр сценария пользователя A завершает свою работу и очищает все, удаляя также данные, введенные с помощью Пользователь B ! Когда экземпляр сценария пользователя A снимает блокировку, сценарий пользователя B включается, но обнаруживает, что его данные сметены экземпляром сценария пользователя A.

В идеале мне нужен способ «заморозить» любую модификацию «буферный» лист (включая вставку новых строк) до тех пор, пока каждый экземпляр скрипта не завершит свою работу.

Но, насколько я понимаю, а также, судя по некоторым странным ошибкам в моих журналах, это не то, что getScriptlock () выполняет (и getUserlock () или getDocumentlock () нет).

Может кто-нибудь помочь мне понять, как мне использовать Google LockService для этого?

Заранее спасибо

РЕДАКТИРОВАТЬ

Те, кто прокомментировал мой пост (в частности, @TheMaster), уловили самую суть проблемы, поэтому позвольте мне добавить дополнительную информацию / мысли. Это ошибка, которую я получаю (иногда) в моем журнале Stackdriver

Error log from Stackdrive

Очевидно, мой скрипт пытается получить доступ к данным, когда он не должен. Я подозреваю, что это сценарий, который я изобразил: второй экземпляр скрипта находит «буфер», очищенный первым.

Как говорит @TheMaster, проблема в том, что нет способа действительно «заблокировать» «документ, если доступ к документу осуществляется через какой-либо внешний ресурс, например, Zapier, или, как в моем случае, коннектор Wordpress или какой-либо запрос API (разработчики Google, не должны ли вы решить эту проблему?).

1 Ответ

1 голос
/ 27 февраля 2020

Разработчики Google, если бы вы не обратились к этой проблеме

Я бы сообщил об этом, но, как вы видите, ограничения устанавливаемого триггера очевидны:

Выполнение сценариев и запросы API не приводят к запуску триггеров.

Итак, я не хотел, чтобы они "исправляли" это, удалив эту функциональность в целом. Тогда вам придется опрашивать через регулярные промежутки времени, что было бы больно.

Если два API INSERT_ROW выполняются API, скажем,

  • строк с 21 по 25 и
  • в строках с 26 по 30.

Несмотря на то, что внешний интерфейс API вставляет строки по порядку, иногда последний запускается первым (скажем, в 23:00), а первый - последним (в 23:01). У первого sheet.getLastRow() все еще может быть 30 (даже при том, что это было запущено для вставки строки с 21 по 25, потому что это было запущено поздно). Одним из решений было бы позволить всему работать асинхронно c: Внутри функции триггера события изменения active range относится к диапазону, добавленному в настоящее время API. Размеры «INSERT_ROW» можно получить следующим образом:

  const rng = ss.getActiveRange(); //Currently Active Range
  const row = rng.getRow(); //Active Row number
  const col = rng.getColumn(); //Active Column number
  const lrow = rng.getLastRow(); //Last Row in Active range

Обратите внимание, что последний ряд выводится как rng.getLastRow(), а не sheet.getLastRow(). Таким образом, даже если последний запускается первым, мы получаем только добавленные в данный момент строки в этом экземпляре выполнения скрипта (т.е. строки с 26 по 30). Возможно, вы сможете использовать это, чтобы получить настоящий асин c рабочий процесс.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...