The Jerusalem PostBasketball: Micic puts up 31 points in Tel Aviv derby victoryZDF heuteEntdecken Sie das ZDF-NachrichtenstudioSRF NewsChef der Freien Wähler – Bayerischer Wirtschaftsminister Hubert Aiwanger ist totVarietyUFC’s Event Sellout Spree Powers TKO Q3 EarningsStraits Times SportMercedes can add F1 constructors' title to Antonelli's crownBBC عربيترامب يُقر بأن الحرب مع إيران أدّت إلى خسارة الانتخابات، لكنه يؤكد أنه "سيفعل ذلك مجدداً" إذا لزم الأمرBBC BusinessPound rises to its highest level since the Brexit referendumCBC NewsNASA's Swift space telescope plunges to Earth after 22 years in orbitHipertextualUn ciberataque altera los bancos de sangre en Francia y enciende las alarmas en Europa3DNewsКитайские ИИ-гиганты выстроились за видеокартами Nvidia, но получат лишь 1 % от заказанногоNHK 社会【動画】Pickup NEWS(11月5日)BBC News BrasilTalarico, o religioso democrata que falou em Deus 'não binário' e arrasou no conservador Texas
The Daily Newsstand · Free, Always
Wednesday, November 4, 2026

[Перевод] Сотни миллионов лишних запросов в неделю: что кодинг-агент нашёл в трейсах Sentry

Translate

Последние несколько недель я с помощью кодинг-агента искал и чинил проблемы с производительностью бэкенда Sentry. Отправной точкой служили трейсы, которые Sentry отправляет сам себе.

Большинство правок меняли строку-две. Но вместе они убрали сотни миллионов запросов в неделю, а некоторые дорогие выборки ускорились в сотни и тысячи раз (подробности ниже).

Как я направил агента на наши трейсы

Я подключил агента (в данном случае Claude с Opus 5.5) к Sentry MCP, указал ему нашу организацию и проект в Sentry и попросил найти проблемы на горячих путях, похожие на небольшой PR про производительность, который я подготовил сам:

поищи через sentry mcp ещё похожие быстрые победы вокруг проблем на горячих путях

По моему опыту, с последними несколькими релизами моделей уже не нужно так тщательно выстраивать промпт или контролировать контекст агента (например, подсовывать ему конкретные спаны). Начать можно с такого общего запроса:

Найди все критические пути в моём приложении. Через Sentry MCP найди их трейсы и способы их оптимизировать.

После моего первого промпта агент отсортировал спаны по тому, как часто они выполнялись и сколько длились. Для каждого кандидата он давал файл и строку, оценку недельного объёма и ссылку на пример трейса. Когда список редел, я направлял его в новое место. Промпты были короткими и в основном сводились к вопросам вроде этих:

  • «поищи проблемы с большой нагрузкой на базу»

  • «это изменение чем-нибудь рискованно?»

  • «ты проверил новый запрос через EXPLAIN, как мы делали раньше?»

  • «rpc тоже может быть интересен»

  • «кажется, тут есть чей-то PR, который уже может это чинить»

  • «давай не будем трогать эту часть»

Затем я выбирал проблему и просил агента разобраться глубже, прежде чем что-либо менять. Он читал код и историю git и рассказывал, что может сломаться. Если исправление выдерживало проверку, он открывал черновик PR.

Кодовая база Sentry большая, и есть области, которые я знаю недостаточно хорошо, чтобы уверенно их менять. Я уводил агента от таких мест, проверял, не идёт ли там уже работа, и продолжал искать что-нибудь поменьше. Результаты становятся намного лучше, если агент может проверять изменения на базе в режиме только для чтения, а не гадать.

Инструментация Django SDK как отправная точка

Значительная часть кода Sentry написана на Django, и автоматическая инструментация Sentry Django SDK дала нам хорошую основу:

  • В описании спанов запросов Django лежит сам SQL. Кроме того, в наши трейсы попадают обращения к кэшу, RPC между силосами, задачи и маршруты API. Группировка спанов по описанию позволила агенту превратить данные трейсов в ранжированный список дорогой работы.

  • Если у спана базы данных были атрибуты источника запроса, они давали агенту файл и строку, откуда запрос выполнялся.

  • Атрибут user_agent показал, что один нагруженный эндпоинт в основном дёргает sentry-cli set-commits --auto, постранично проходя по репозиториям организации при каждой сборке. У одной организации было около 16 тысяч репозиториев, и делала она это примерно каждые 40 секунд. Отсюда появились правка в CLI, которая избегает постраничного обхода, и индекс на сервере, удешевляющий запросы для уже установленных версий CLI.

  • Каждый PR ссылается на пример трейса с лишним запросом или вызовом, чтобы ревьюеру было немного проще разобраться. В одном трейсе вебхук дважды выполнял один и тот же запрос на 140 мс.

Водопад трейса Sentry: вебхук дважды выполняет один и тот же запрос SELECT DISTINCT sentry_grouplink, за 137 мс и 140 мс

Водопад трейса Sentry: вебхук дважды выполняет один и тот же запрос SELECT DISTINCT sentry_grouplink, за 137 мс и 140 мс

Результат удаления запроса в трейсе виден сразу. Чтобы понять, насколько это важно, я также смотрел в Sentry на задержку запросов и нагрузку на базу.

Проверка изменений в запросах

При изменениях в SQL агент запускал EXPLAIN ANALYZE до и после на зеркале базы, доступном только для чтения, и сравнивал результаты. Нагляднее всего получилось с выборкой из GroupHistory (GroupHistory хранит каждую смену статуса у issue). Запрос искал самую новую строку сразу по нескольким статусам, поэтому Postgres читал и сортировал все подходящие строки:

-- до
SELECT * FROM sentry_grouphistory
WHERE group_id = %s AND status IN (1, 2, 11, 12, 13)
ORDER BY date_added DESC LIMIT 1

-- после: по одной выборке на статус, максимум берём в Python
(SELECT date_added FROM sentry_grouphistory
 WHERE group_id = %s AND status = 1
 ORDER BY date_added DESC LIMIT 1)
UNION ALL ...

Для самой загруженной группы на зеркале время упало с 935 мс до 0,4 мс при прогретом кэше.

Прим. пер.: в описании PR условия замера другие. Для группы со 130 тысячами строк истории старый запрос занимал примерно 935 мс на холодном кэше и примерно 274 мс на прогретом, новый занял 0,4 мс. Оценка «в тысячи раз» из начала статьи держится на холодном замере. Группы для проверки выбирали среди худших: у обычных запрос и раньше занимал около 2 мс, а теперь укладывается в миллисекунду.

Там же точнее названа причина. Индекс (group, status, date_added) не умеет отдавать строки сразу нескольких статусов от новых к старым. У групп, которые постоянно переходят между resolved и regressed, таких строк десятки тысяч. Запрос при этом остаётся одним.

Результаты

Примечание. Объёмы запросов и суточная экономия — это оценки по сэмплированным спанам и наблюдаемому трафику. Я сравнивал работу базы в расчёте на запрос, чтобы изменения трафика нельзя было ошибочно принять за рост производительности.

Прим. пер.: по статистике GitHub размер перечисленных правок заметно различается: от +1/−3 строки у PR 127494 до +54/−22 у PR 127496.

Как попробовать на своём приложении

Агенту нужен доступ к вашим трейсам. Дальше попросите его найти проблемы с производительностью в приложении. Можно начать с общего запроса и спускаться к конкретным областям:

  • «Найди транзакции, где один и тот же запрос с разными параметрами выполняется в одном трейсе больше 10 раз (N+1)».

  • «Найди SELECT * и широкие запросы на горячих путях, где используется всего несколько колонок».

  • «Найди медленные запросы с ORDER BY ... LIMIT, постраничным выводом через OFFSET или LIKE '%...', которые, скорее всего, не используют индекс».

Выберите проблему и попросите агента разобраться глубже, прежде чем что-либо менять. Пусть он прочитает код и историю git и оценит, что может сломаться. После этого пусть открывает PR.

View the original on Хабр →

KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.