Бэкенд-разработчик платформы Яндекс Еды обнаружил значительный перерасход процессорных мощностей в PHP-монолите. В период повышенной нагрузки заметил, что количество запросов в секунду (RPS) в пиковые вечерние часы совпадает с числом ядер CPU, выделенных на монолит, а 100% загрузка одного ядра приходится на 2,5 RPS.
Для поиска причины использовали профилировщик Perforator, установленный на большинстве хост-машин в дата-центрах. Поскольку стандартная версия не поддерживала PHP, разработчик запустил локально и на отдельной хост-машине специально собранную версию инструмента с поддержкой PHP. В процессе возникли сложности с верификатором ядра eBPF, который отклонял программу из-за превышения лимита в 1 миллион инструкций.
Проблема с eBPF была решена путем изменения структуры цикла размотки Python-стека, чтобы упростить отслеживание состояний верификатором. Также было исправлено решение с макросом PHP_TRACE, который выталкивал счетчик цикла на стек, из-за чего верификатор считал цикл бесконечным. Печать отладочного вывода была перемещена ниже по коду.
Профилирование показало, что потребление CPU было вызвано функцией поддержки атрибута http.route для OpenTelemetry. Метод getRouteCollection не возвращал закешированный список роутов, а каждый раз собирал его заново, читая YAML-файлы и аннотации в PHP-файлах. Проблему исправили, сохраняя роуты на этапе сборки контейнера и используя кеш в рантайме.


