> ## Documentation Index
> Fetch the complete documentation index at: https://docs.twenty.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Рекомендации

> Как разработать приложение, которое работает эффективно и хорошо масштабируется в разных рабочих пространствах.

## Обзор

API Twenty принимает **500 запросов в минуту на регистрацию приложения** для запросов, которые приложение выполняет от своего имени: запусков без участия человека (cron, вебхуки, хуки установки) и клиентов, созданных с помощью `runAs: 'application'`. Бюджет является общим для всех рабочих пространств, в которых установлено приложение, а запросы сверх него отклоняются с кодом `429`. Запросы, выполненные от имени пользователя, запустившего запуск, вместо этого учитываются в лимитах этого пользователя. Описанные ниже практики позволяют приложению оставаться в рамках этого бюджета по мере роста числа его установок и в пределах лимитов вызываемых им сервисов.

* **Распределяйте cron-задачи обхода с произвольной задержкой.** Триггер cron запускается в каждой рабочей области в одну и ту же минуту, поэтому все установки одновременно расходуют общий бюджет. Пусть обработчик cron только ставит задачу в очередь с произвольным `delayMs` в большом окне, например в один час. Постановка задач в очередь также ограничена 2 000 в минуту на регистрацию приложения, а тик cron сверх этого лимита пропускается в остальных рабочих областях, поэтому делайте обход идемпотентным и позволяйте следующему тику наверстать упущенное.

```ts src/logic-functions/enqueue-nightly-sweep.ts theme={null}
import { defineLogicFunction } from 'twenty-sdk/define';
import { enqueueJobs } from 'twenty-sdk/logic-function';

const NIGHTLY_SWEEP = '3f9d1c02-8a44-4f0e-b1d7-9c2e5a7b4f10';
const SPREAD_WINDOW_MS = 60 * 60 * 1000;

export default defineLogicFunction({
  universalIdentifier: 'b7c2e4d1-0f5a-4c3e-9d8b-6a1f2e3c4d5b',
  name: 'enqueue-nightly-sweep',
  timeoutSeconds: 30,
  cronTriggerSettings: { pattern: '0 3 * * *' },
  handler: async () => {
    await enqueueJobs({
      logicFunctionUniversalIdentifier: NIGHTLY_SWEEP,
      jobs: [{ payload: {} }],
      delayMs: Math.floor(Math.random() * SPREAD_WINDOW_MS),
    });
  },
});
```

```text theme={null}
   pattern "0 3 * * *"                  the server, every minute
                                                  │
              ┌───────────────┬───────────────────┼───────────────┐
              ▼               ▼                   ▼               ▼
        workspace A     workspace B     workspace C   ...   workspace N
          03:00:00        03:00:00        03:00:00            03:00:00
              │               │                   │               │
              └───────────────┴─────────┬─────────┴───────────────┘
                                        ▼
                      500 requests per minute for the whole app
                                   N × calls  →  429
```

При произвольной задержке в пределах одного часа те же запуски распределяются:

```text theme={null}
   pattern "0 3 * * *"  +  random delayMs in [0, 60 min)
                                                  │
              ┌───────────────┬───────────────────┼───────────────┐
              ▼               ▼                   ▼               ▼
        workspace A     workspace B     workspace C   ...   workspace N
          03:00:00        03:00:00        03:00:00            03:00:00
           + 12 min        + 41 min        + 03 min            + 27 min
              │               │                   │               │
              ▼               ▼                   ▼               ▼
          03:12:00        03:41:00        03:03:00            03:27:00
              │               │                   │               │
              └───────────────┴─────────┬─────────┴───────────────┘
                                        ▼
                      500 requests per minute for the whole app
                          N × calls spread over 60 minutes  →  OK
```

* **Пакетируйте триггеры событий базы данных.** Массовая запись, например синхронизация почтового ящика, создаёт одно событие на запись — тысячи за считанные секунды, а один запуск на событие означает один запрос на запись. Установите `batchMode: true`, объявите `updatedFields` и сократите пакет до уникальных записей перед вызовом API. См. [Пакетная обработка событий базы данных](https://docs.twenty.com/ru/developers/extend/apps/logic/logic-functions.md#batching-database-events).

* **Выполняйте один запрос на пакет, а не на запись.** Лимит учитывает запросы, а не записи: один `createMany` с `upsert: true`, содержащий 200 записей, — это один запрос, а 200 отдельных обновлений — это 200 запросов. Читайте с фильтрами `in`, а записывайте с помощью `createMany` или `updateMany`.

* **Объединяйте всплески в одну отложенную задачу.** Когда события выявляют объём работы, превышающий само событие, постановка задачи в очередь для каждого события умножает её на размер всплеска. Поставьте в очередь одну задачу с `delayMs` и детерминированным `jobId`, включающим временное окно; очередь игнорирует идентификатор, который уже содержит. См. [Выбор собственного идентификатора задачи](https://docs.twenty.com/ru/developers/extend/apps/logic/background-jobs.md#choose-your-own-job-id).

* **Распределяйте fan-out с помощью `delayMs`.** Задачи, поставленные в очередь без задержки, становятся доступными одновременно и расходуют бюджет в одну и ту же минуту. Сгруппируйте их в слоты на минуту и отложите следующую страницу на количество использованных слотов. См. [Фоновые задачи](https://docs.twenty.com/ru/developers/extend/apps/logic/background-jobs.md).

* **Повторяйте запрос с `429` с отсрочкой или повторно ставьте его в очередь с задержкой.** Клиентский SDK выдаёт исключение при любом ответе не из диапазона 2xx, а запуск, ожидающий внутри, расходует собственный тайм-аут. Повторяйте попытки с экспоненциальной отсрочкой и ограничением числа попыток либо передавайте работу обратно в очередь с задержкой.

* **Кэшируйте неизменяемые данные.** Поиск при каждом событии требует запроса для значения, фиксированного во время подключения, например идентификатора или идентификатора команды. Сохраните его в [хранилище ключей и значений](https://docs.twenty.com/ru/developers/extend/apps/logic/key-value-store.md) при регистрации подключения.

* **Пакетируйте запросы к внешним API.** У внешнего сервиса есть собственное ограничение частоты запросов, а у логической функции — тайм-аут, поэтому `n` запросов на запуск умножают оба показателя. Если поставщик предоставляет пакетную конечную точку, отправляйте один запрос для всего пакета вместо одного на запись и разбивайте пакет на части согласно максимальному размеру у поставщика.

* **Сделайте параметры по умолчанию редактируемыми пользователем.** Значение, жёстко заданное в логической функции или в Workflows, например порог, размер пакета или параметр по умолчанию, подходит для одной рабочей области и требует нового релиза для каждой другой. Объявите его как переменную приложения со значением по умолчанию и считывайте её во время запуска, чтобы администраторы рабочей области могли настраивать её в настройках приложения. См. [Конфигурация приложения](https://docs.twenty.com/ru/developers/extend/apps/config/application.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.