Перейти к основному содержанию

Обзор

Запуск логической функции ограничен значением timeoutSeconds (максимум 900 секунд). Любая задача, которая не может завершиться за это время — полная повторная синхронизация, разветвление по каждой записи, сторонний API, который ограничивает вас по скорости, — должна быть разделена на более мелкие запуски. enqueueJobs делает именно это: он просит воркеров Twenty позже запустить одну из логических функций вашего приложения, по одному разу для каждой полезной нагрузки, каждый запуск в отдельном процессе с собственным лимитом времени. Вызов возвращается немедленно.

Постановка запусков в очередь

Импортируйте enqueueJobs из twenty-sdk/logic-function, укажите universalIdentifier логической функции для запуска и передайте в очередь по одной полезной нагрузке для каждого запуска.
src/logic-functions/sync-all-contacts.ts
Каждый запуск получает свою полезную нагрузку в качестве аргумента обработчика, точно так же, как любой другой триггер. Целевая функция должна принадлежать тому же приложению, что и вызывающая сторона — постановка в очередь функции другого приложения будет отклонена с ошибкой Logic function not found, и ничего не будет поставлено в очередь. Один вызов принимает до 200 заданий.
enqueueJobs возвращает управление, как только задания приняты, а не когда они выполнены. Он не возвращает результаты целевых функций — если вам нужно потом их прочитать, пусть каждая целевая функция запишет то, что она создаёт, в key-value store или в запись рабочего пространства. Чтобы узнать, завершился ли запуск, опрашивайте его статус задания.
Старый помощник enqueueJob, который добавляет в очередь одно задание за вызов, устарел. Вместо этого используйте enqueueJobs со списком jobs из одного элемента. Аргумент payloads также устарел — вместо него передавайте jobs, который принимает ту же полезную нагрузку для каждого запуска и позволяет задать имя для каждого из них.

Опрос заданий

enqueueJobs возвращает один jobId для каждого поставленного в очередь запуска в том же порядке, что и переданные вами задачи. getJobs за один вызов считывает, до какого этапа дошли эти запуски.
Один вызов getJobs считывает не более 200 идентификаторов — такое же ограничение enqueueJobs применяет к пакету. Каждая запись содержит запрошенный вами идентификатор: Задача, которую ещё не взяли в работу, обычно имеет статус PRIORITIZED, а не WAITING, поскольку задачи приложения ставятся в очередь с фиксированным низким приоритетом. COMPLETED и FAILED — конечные состояния. Цикл опроса также должен прекращаться самостоятельно после ограниченного числа попыток, а не ждать бесконечно, поскольку запуск доступен для чтения только пока очередь его хранит: 4 часа после завершения, 7 дней после сбоя и в обоих случаях — только последние 1000 задач в этом состоянии. getJobs возвращает только задачи, которые он всё ещё хранит, поэтому идентификатор, который был удалён — или никогда не существовал, — просто отсутствует в результате, а не вызывает ошибку для всего пакета. Сравнивайте полученный результат с запрошенным, чтобы выявить такие случаи, и считайте отсутствующий идентификатор «больше не известным», что не равнозначно ответу «он никогда не запускался». Статусы ограничены рабочей областью, из которой выполняется вызов, поэтому одна рабочая область никогда не может прочитать задачу другой рабочей области.

Выберите собственный идентификатор задачи

Передайте jobs вместо payloads, чтобы самостоятельно задать имя каждому запуску. Переданный вами идентификатор принимает getJobs, поэтому вызывающая сторона может опрашивать задачу без необходимости хранить то, что вернул enqueueJobs.
Идентификатор содержит не более 128 символов: букв, цифр, _, . или -; две задачи в одном вызове не могут иметь одинаковый идентификатор. Передавайте либо payloads, либо jobs, но никогда не оба. Если не указать jobId, Twenty создаст его. jobId также является ключом идемпотентности: постановка в очередь идентификатора, который очередь всё ещё хранит, принимается, но не запускает второй запуск, а getJobs продолжает сообщать о первом. Благодаря этому повторная постановка в очередь безопасна. Есть два нюанса: гарантия действует только пока задача хранится, поэтому тот же идентификатор может запустить новый запуск после удаления исходной задачи, а идентификаторы используются всеми приложениями в рабочей области, поэтому добавляйте к своим префикс, если совпадение может быть проблемой.

Параметры задания

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

Повторная попытка при временном сбое

Twenty не выполняет повторную попытку при каждом исключении из кода приложения. Обычная вызванная ошибка считается постоянным сбоем. При временном сбое логическая функция в очереди может запросить до трёх повторных попыток, вызвав RetryableLogicFunctionError.
По возможности вызывайте RetryableLogicFunctionError напрямую. Если вы расширяете его, не заменяйте его name: Twenty распознаёт сериализованное имя RetryableLogicFunctionError во всех средах выполнения. retryCount равен 0 при первоначальном выполнении и увеличивается, только когда код приложения запрашивает повторную попытку. maxRetries не превышает 3 и может быть меньше, если у задания в очереди меньше общий лимит повторных попыток. Сбои платформы не увеличивают retryCount, хотя всё равно расходуют общий резерв безопасности очереди. Очередь задерживает повторные попытки с экспоненциальной задержкой и случайным разбросом. Точная задержка намеренно не гарантируется, поэтому код приложения не должен зависеть от выполнения повторной попытки в точно определённое время. После достижения maxRetries ещё один RetryableLogicFunctionError фиксируется как окончательный сбой приложения без нового выполнения.
Повторные попытки заново выполняют весь обработчик и могут происходить после успешного выполнения некоторых побочных эффектов. Сделайте обработчик идемпотентным, прежде чем запрашивать повторные попытки.

Использование: постраничный проход по длинной синхронизации

Классический шаблон — функция, которая ставит саму себя в очередь со следующим курсором. Каждый запуск обрабатывает одну страницу работы, уверенно укладываясь в собственный таймаут, а цепочка останавливается, когда больше нечего делать.
src/logic-functions/sync-contacts-page.ts

Разветвление по каждой записи

Когда работа естественным образом выполняется по отдельным элементам, добавляйте в очередь по одному заданию на элемент за один вызов и позвольте воркерам обрабатывать их параллельно, вместо того чтобы перебирать элементы в цикле непосредственно в коде.

Рекомендации для длительной работы

Два правила покрывают почти любое долгое задание: рекурсивный вызов вместо цикла и обработка ограниченного блока за один запуск. Запуск, который пытается сделать всё сразу, — это сценарий отказа: он упирается в таймаут и при повторной попытке начинает всё сначала с нуля. Вместо этого выберите размер блока так, чтобы он с запасом завершался в пределах timeoutSeconds, сохраните свою позицию и поставьте в очередь следующий запуск.
src/logic-functions/enrich-companies-batch.ts
Почему это работает:
  • Размер блока выбирайте, исходя из самого медленного элемента, а не среднего. CHUNK_SIZE × время обработки в худшем случае должно помещаться в timeoutSeconds с запасом, иначе хвост блока будет потерян, когда запуск обрежет таймаут.
  • Сделайте условие завершения явным. Вызывайте рекурсивно только пока вернулся полный блок. Цепочка, которая останавливается только на основе «нет результатов», будет продолжаться бесконечно, если источник когда‑либо вернёт укороченную страницу посередине.
  • Сохраняйте прогресс перед постановкой в очередь следующего запуска, чтобы при сбое звена цепочки она возобновлялась с последнего завершённого блока, а не с начала.
  • Сделайте каждый блок идемпотентным. Повторная обработка одного блока после повторной попытки не должна приводить к двойной записи — привязывайте операции записи к идентификатору обрабатываемой записи или к внешнему идентификатору.
  • Отдавайте предпочтение цепочке из блоков перед одним гигантским разветвлением, когда работа обращается к внешнему сервису с ограничением по скорости: цепочка с delayMs саморегулирует темп, тогда как тысячи заданий, поставленных в очередь одновременно, сразу становятся допустимыми к выполнению.