Превышено время CPU Apex — причины и решения

Кратко
Apex CPU time limit exceeded — это ошибка Salesforce, которая возникает, когда транзакция использует слишком много процессорного времени. В статье объяснено, почему это случается, как быстро проверить распространённые причины и какие практические шаги предпринять для исправления и профилактики.
Ключевые причины
- Логирование включено (LoggingEnabled = true). Дополнительная запись логов увеличивает нагрузку на CPU при массовых операциях.
- Триггер выполняется несколько раз. После выполнения триггера срабатывают workflow/process builder, которые обновляют запись и снова вызывают триггер.
- Глубокие вложенные циклы. Три и более уровней вложенности приводят к экспоненциальному росту операций и времени выполнения.
- Код в управляемых пакетах. Внешний (managed) код может быть «чёрным ящиком» и потреблять много CPU без возможности правки — требуется помощь вендора.
- Множество автоматизаций на одном объекте. Если на один объект навешано несколько Process Builder / Flow / Workflow, они суммируют нагрузку.
Важно: Перед любыми изменениями проверьте логи и повторите ошибку в тестовом окружении, чтобы не вносить лишних правок в прод.
Как быстро диагностировать проблему
- Включите системные логи и посмотрите статистику по времени CPU для транзакции.
- Найдите повторяющиеся вызовы одного и того же триггера в логах.
- Оцените, есть ли в процессе массовые операции (bulk) или массовые объёмы данных.
- Посмотрите, какие автоматизации запускаются на том же событии (Process Builder / Flow / Workflow).
Предварительные проверки
- Отключите необязательные Process Builder, если это возможно.
- Перезапустите рабочую машину и убедитесь, что локальные проблемы (например, при локальном тестировании через CLI) не влияют.
- Сведите к минимуму одновременные автоматизации на одном объекте.
- Избегайте вложенных циклов. Часто Maps и Set позволяют заменить вложенные for.
Важно: Проверки — это быстрые шаги, не требующие изменений в коде. Если проблема остаётся, переходите к изменениям в коде и конфигурации.
Как исправить проблему
1. Отключите LoggingEnabled (если используется)
Если у вас есть кастомная настройка, которая включает подробное логирование в рабочем процессе, временно выключите её:
- Перейдите в окружение Lightning, нажмите шестерёнку Settings и выберите Setup.

- В поле Quick Find введите Custom Settings и откройте Custom Settings.
- Найдите General Settings и нажмите Manage рядом с ним.

- Нажмите Edit рядом с LoggingEnabled.

- В поле Value установите false и сохраните.
LoggingEnabled обычно используется для отладки. В рабочем режиме оно редко нужно и только увеличивает вероятность таймаута.
2. Замените Process Builder на Flow
Process Builder часто добавляет скрытую нагрузку и многократно упоминался в сообщениях об ошибках CPU timeout. Salesforce рекомендует переход на Flow — он более эффективен и гибок.
Когда менять: если у вас сложные цепочки Process Builder с обновлением записей и вызовом других автоматизаций. Переносите логику в Flow, где можно контролировать bulk-обработку и уменьшать число обновлений.
3. Избегайте вложенных циклов и используйте Map-based подход
Типичная ошибка — проход по списку записей и внутри каждого элемента запускать ещё один цикл или запрос. Вместо этого используйте Map и операции в один проход.
Плохой пример (увеличивает CPU из-за внутреннего цикла):
List accList = [SELECT Id, Name FROM Account LIMIT 100];
Set setIds = new Set();
for (Account acc : accList) {
// Дополнительная нагрузка из-за цикла
setIds.add(acc.Id);
} Лучший вариант — использовать Map и обращаться к данным напрямую без вложенных проходов. Пример с сопоставлением по Id:
Map accountsById = new Map([SELECT Id, Name FROM Account WHERE Id IN :someIds]);
// Доступ к записи по accountsById.get(id) — без вложенных циклов Ещё один приём — собрать все Id, выполнить один SOQL-запрос с фильтром IN, затем в одном цикле обновить данные.
4. Bulkify триггеры и избегайте DML в циклах
Всегда пишите триггеры с учётом bulk (работы с коллекцией записей). Не допускайте DML-операций или запросов внутри for-циклов. Собирайте данные в коллекции, выполняйте один DML и один SOQL на «пакет».
5. Проверьте код управляемых пакетов
Если сломалась логика внутри managed package, обратитесь к вендору. Вы как администратор можете временно отключить пакетные автоматизации или изолировать объект, чтобы понять влияние.
Альтернативные подходы
- Использовать @future/@queueable для дорогостоящих операций, которые можно вынести асинхронно.
- Переносить агрегации и тяжёлую обработку на внешние ETL/инструменты, если это уместно.
- Разбивать большие транзакции на несколько меньших батчей.
Когда эти методы не работают
- Если пакетный код вендора выполняет тяжёлые операции и вы не можете его изменить — нужна поддержка вендора.
- Если бизнес-требования заставляют постоянно выполнять тяжёлые вычисления в единой транзакции — необходимо пересмотреть архитектуру.
Практическая методология (мини-план)
- Воспроизведите ошибку в sandbox.
- Снимите лог транзакции и проанализируйте источник CPU-горуза.
- Выполните быстрые исправления: выключите LoggingEnabled, отключите лишние Process Builder.
- Оптимизируйте код: bulkify, убрать вложенные циклы, использовать Map, избегать DML в циклах.
- Перенесите тяжёлую работу в асинхронные процессы при необходимости.
- Тестируйте и валидация: убедитесь, что количество ошибок и время транзакции снизились.
Проверки для ролей
Проверки для администратора
- Отключены ли временно Process Builder для проблемного объекта?
- LoggingEnabled установлен в false в проде и sandbox?
- Какие managed packages взаимодействуют с объектом?
Проверки для разработчика
- Триггер bulk-ready и нет DML/сообщений внутри циклов?
- Используются ли Maps и Set для уменьшения вложенности?
- Есть ли unit-тесты, покрывающие bulk-случаи и моделирующие большую нагрузку?
Критерии приёмки
- Ошибка Apex CPU time limit больше не воспроизводится в sandbox при тех же входных данных.
- Время выполнения транзакции снизилось и устойчиво в пределах дозволенных лимитов.
- Нет регрессий в функциональности, автоматизации работают корректно.
Риски и способы смягчения
- Риск: отключение логирования усложнит диагностику других ошибок. Смягчение: сохраняйте логи перед отключением и включайте выборочно для короткого времени.
- Риск: перенос логики в асинхронный процесс меняет порядок выполнения бизнес-правил. Смягчение: документируйте изменения и обновите тесты.
Краткий глоссарий
- Bulkify — писать код, который корректно обрабатывает коллекции записей.
- DML — операции вставки/обновления/удаления записей в Salesforce.
- Managed package — сторонний пакет с кодом, обновляемый вендором.
Короткая памятка (cheat sheet)
- Проверить логи → выключить LoggingEnabled → отключить ненужные Process Builder → bulkify код → заменить вложенные циклы на Map → если нужно, использовать @queueable.
Итог
- Превышение CPU обычно связано с лишними циклами, логированием, неоднозначной автоматизацией или сторонним кодом.
- Быстрые победы: отключить LoggingEnabled и убрать лишние Process Builder.
- Долгосрочные решения: оптимизация кода, bulk-ориентированная логика и перенос тяжёлых задач в асинхрон.
Если у вас остались вопросы или вы хотите пример мини-рефакторинга вашего триггера — опишите текущую логику в комментарии, и мы поможем с планом правок.
Похожие материалы
RDP: полный гид по настройке и безопасности
Android как клавиатура и трекпад для Windows
Советы и приёмы для работы с PDF
Calibration в Lightroom Classic: как и когда использовать
Отключить Siri Suggestions на iPhone