惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
F
Fortinet All Blogs
阮一峰的网络日志
阮一峰的网络日志
Apple Machine Learning Research
Apple Machine Learning Research
爱范儿
爱范儿
WordPress大学
WordPress大学
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
J
Java Code Geeks
罗磊的独立博客
S
SegmentFault 最新的问题
V
V2EX
V
Visual Studio Blog
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
美团技术团队
博客园 - 三生石上(FineUI控件)
Stack Overflow Blog
Stack Overflow Blog
Y
Y Combinator Blog
MyScale Blog
MyScale Blog
D
Docker
Google DeepMind News
Google DeepMind News
Blog — PlanetScale
Blog — PlanetScale
M
Microsoft Research Blog - Microsoft Research
Martin Fowler
Martin Fowler
S
Secure Thoughts
B
Blog
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Recent Announcements
Recent Announcements
MongoDB | Blog
MongoDB | Blog
C
Cisco Blogs
C
CERT Recently Published Vulnerability Notes
T
True Tiger Recordings
GbyAI
GbyAI
P
Proofpoint News Feed
P
Privacy International News Feed
Jina AI
Jina AI
The Cloudflare Blog
I
Intezer
AWS News Blog
AWS News Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
S
Security Archives - TechRepublic
NISL@THU
NISL@THU
The Register - Security
The Register - Security
Recent Commits to openclaw:main
Recent Commits to openclaw:main
P
Palo Alto Networks Blog
S
Schneier on Security
L
LINUX DO - 热门话题
C
CXSECURITY Database RSS Feed - CXSecurity.com
Security Latest
Security Latest
C
Cybersecurity and Infrastructure Security Agency CISA

Все публикации подряд на Хабре

Как продвигать агентство недвижимости: от вывески до прямых эфиров MCP для GitHub + GitLab: инженерный гайд 2026 Вы платите OpenAI $20 в месяц, а он зарабатывает на вас ещё $100 млн за полтора месяца. И это только начало ИИ забирает работу «белых воротничков»: чему учить детей, чтобы выжить в будущем Практический ИИ-агент Python: LangGraph + Qdrant Как я делал ping и traceroute на iOS без entitlements — и почему это оказалось проще, чем UMP-консент для AdMob 4 MVP за 4 месяца, 30 холодных DM, 1 регистрация: building in public по-русски VPS-бастион: доступ к домашнему серверу без белого IP Kampus AI — нейросеть для генерации учебных работ для студентов и школьников Игры, помогающие продавать — примеры интересных рекламных акций с видеоиграми €500 в Telegram Ads принесли сделку на 350 000 ₽. Разбор B2B-кампании Чтение на выходные: «Разработка игр и теория развлечений» Рафа Костера Личный архив: сбор, бэкап, таймлайн фотографий INFOSTART TECH EVENT или INFOSTART A&PM EVENT — как понять, куда вам нужнее? Peer testing на основе Закона Линуса Релиз GitLab 19.0: ИИ-оркестрация, которая наконец-то догнала темп написания кода Как бизнесу оценить готовность к аттестации по новому Приказу ФСТЭК № 117 Технический гайд по сторис – часть 4: как мы добавили видео формат Представительство в арбитражном процессе: правовые различия между внешним защитником и инхаусом «Где новые фичи?» — Как AI-миграция легаси вернет IT-бюджет бизнесу Что нужно знать работнику про увольнение Новые требования Москвы к ЦИМ для АГР: готовый инструмент для проектировщиков в nanoCAD BIM Строительство WireGuard: простота и надёжность современного VPN-туннеля или секретное рукопожатие в тёмной комнате Выйдет ли GTA 6 в 2026 году, и чего ждать от игры Как меня назвали «невовлечённым», а я нашёл офшоры на Кипре Как LLM научила рекомендательную модель видеть больше, чем историю взаимодействий От хаоса к экосистеме: Модель зрелости комьюнити в бизнесе Свет, тьма, VEML7700 и Python Сказ о том, как мы процессы разработки в GRI меняли. Часть 2 Майский «В тренде VM»: громкие уязвимости в Linux, ActiveMQ, SharePoint и Acrobat Reader Статический анализ, заряженный ИИ: как LLM ищут уязвимости в коде и где их границы Блок “Процессы” и почему мы называем его нашим мини-n8n Как поменялся рынок интернет-рекламы: сравнение первых кварталов 2025 и 2026 годов: исследование click.ru Мониторинг Kerio Connect через Zabbix 7: разбор шаблона без агентов и regex по DAT 671 Allow в Claude Code за день: как родился сетап Spec-build 3 известные интересные задачи на логику Как айтишнику позаботиться о менталке и не перерабатывать OpenAI vs Anthropic: битва экс-коллег за корпоративного клиента и $1 трлн на IPO SEO для интернет-магазина в 2026: что поменялось и как с этим работать Сможете ли вы спроектировать Maven‑монорепозиторий для 5 микросервисов? 6 неудобных вопросов про американское произношение, которые айтишники боятся задать Неожиданная встреча: теория графов вновь помогла решить проблему в анализе Фурье Иллюзия трансформации: почему компании платят за спектакль вместо изменений AMD представила Ryzen 9 PRO 9965X3D и еще 5 процессоров, которые пойдут далеко не всем История IDE в Google Первые отзывы на новинки о System Design Влияние параметра planner_upper_limit_estimation на планы выполнения и профиль нагрузки PostgreSQL при использовании 1C Границы 100% разработки с агентами Быстрый OCR на основе Paddle Дооснащение любительской электровакуумной мастерской. Вакуумметр, течеискатель, полярископ Mythos: модель, о которой Anthropic не говорит. Реверс по жертвам — от 27-летней дыры в OpenBSD до побега из песочницы Как использовать Qwen3.7-Max и Grok Build 0.1 для ИИ-агентов в России Suricata IPS NFQueue with nDPI. Часть VI Важные изменения в защите информации в России: что нового? В чем секрет достоверного замедления биологического старения? Вредное ускорение: Умный светофор на перегруженных перекрестках Как сисадмин написал свою библиотеку для Jira на Ruby: история Rujira Сломанный найм: почему рынок труда превратился в казино и что с этим делать Физики нашли свидетельства того, что Вселенная не идеально однородна, вопреки стандартной модели космологии Вопросы на собеседованиях, к которым лучше готовиться заранее Что детектировал детектор таксофонных карт? Как работают выделенные ядра в облачном сервере: от планировщика Linux до тестов производительности Математика кластеров: разбираемся в умной кластеризации данных на примере нашей системы поиска аномалий в логах. Часть 1 Ответы с «деврел‑супервизии», вопрос седьмой: выгорание, когда от вас ждут вечный драйв и креатив Если пропустили Claude последние 3 месяца: топ-5 фич с юзкейсами и история про $400K в Bitcoin Проектируем с нуля калькулятор на FPGA. Части 4 и 5: Фреймворк и оборудование Почему 10× от AI могут дать только лояльные сотрудники Speech-to-LaTeX: распознавание математических выражений и предложений в LaTeX Что внутри портфолио продуктовых и ux/ui-дизайнеров из Т-Банка, Додо, Figma, Альфы, Revolut? Чем заменить Excel в 2026 году: обзор российского ПО и других аналогов Как Rust обрабатывает repr и ABI на границе с C: что ломается и почему 5 промтов, чтобы подготовить презентацию в нейросетях через SpeShu.AI Каггл «200 ёлочек 2025»: призы уже раздали, но мы и за идею задачу укладки порешаем. Часть 1 Как ФНС стала data-driven за 5 лет: минус треть штата, плюс 20 новых цифровых сервисов Как настроить кастомную авторизацию в FESB и сохранить стандартный заголовок Как CISO защищаются от прошлого, игнорируя будущее Заменит ли ИИ разработчиков — и что с этим делать компании Влияние AI на позиции QA в 2026 году Я устал гадать, мне лучше или хуже, и сделал систему непрерывного измерения температуры Исходный код Jedi Academy переполнен яростными комментариями разработчиков ИИ существовал до компьютеров: Крышесносные примеры, часть 2 Тупик на игровом поле: почему образовательные и научные настольные игры в 2026 году сжимаются Ускоряет ли нас AI-coding или просто удорожает? Почему иврит лучше учить как систему, а не как набор слов: опыт с HebrewGlot Как я без навыков fullstack-разработчика сделал свой SaaS Паттерн Backend for Frontend (BFF) в разработке современных приложений «Продай мне этот космолёт» или история любви к симуляторам. От космосима X-Tension до ActorModel/DoD/ECS архитектуры. Ч3 Архитектурные компромиссы в разработке игр Ваш Kubernetes упал: найдёте root cause за 15 минут? Надо ли бороться с анизотропией эмбеддингов Злоумышленник публикует .bash_history: смотреть без регистрации и СМС Разбираемся в ML без воды: от базы до Attention. Часть 3 Как я сделал утилиту для автоматизации ручных тестов Почему факты не работают: шесть причин, по которым люди верят слухам Neko — собираем музыкальный гаджет в домашних условиях AI Evals: Почему без оценки качества ваш продукт стоит на месте Астрологическая схемотехника Безопасный Docker с torque Spring AI: феноменология цифрового сознания, или Как я перестал бояться и полюбил облачные модели [Перевод] Torque: релизы на автопилоте
История одного // todo, который ждал своего часа пол года
igoresha_s · 2026-05-22 · via Все публикации подряд на Хабре

Уровень сложностиСредний

Время на прочтение4 мин

Охват и читатели37

Кейс

Пятница, вечер. Сижу дома, ноут закрыт, телефон рядом, звонок - Grafana OnCall, P1

Открываю: 30% 5xx на критичной ручке загрузки SKU. Иду в логи и через минуту понимаю, что проблема не там. Какой-то соседний сервис посыпался и потащил за собой загрузку. Какой именно - пока неясно

Через несколько минут я понимаю, что лёг наш сервис заказов. Через десять - что лёг по OutOfMemoryError. Через сорок - что виноват комментарий, который висел в коде пол года:

// todo: тут N+1 на invoice — надо переделать через entity graph 
return orders.stream()
  .map(mapper::toDto)
  .toList();

Я открывал этот файл раз пятьдесят. Видел этот todo каждый раз. Тикета на него не завёл ни я, ни кто-либо в команде. В пятницу вечером он сработал.

Ниже - короткая история о том, как deprecated endpoint, известный N+1 и фронтовый чекбокс «Выбрать все» сложились в одну хорошую причину больше не оставлять // todo без тикета

Декорации

Spring Boot, Hibernate, PostgreSQL. Три реплики в k8s за балансировщиком, heap по 700 MB. Сервис обслуживает кабинет продавцов - заказы, статусы, накладные

Главный фигурант - ручка GET /orders. Помечена @Deprecated ещё пол года назад. Возвращает все заказы продавца без пагинации. Под ней SQL с JOIN’ами по нескольким связанным таблицам, и тот самый // todo в маппере

Рядом давно живёт новая пагинированная ручка. Фронт частично мигрировал, но на странице «Все заказы» осталась старая. Почему - расскажу ниже, и это самая интересная часть истории

Что произошло за две минуты

20:07 UTC. Алерт на соседнем сервисе - 30% 5xx на ручке загрузки SKU. В его логах Read timed out на запросах в наш сервис заказов

В наших подах java.lang.OutOfMemoryError: Java heap space. Три пода легли с интервалом в секунды, k8s SIGKILL’нул и поднял заново. Через две минуты всё работает. Через тридцать минут алерт авто-резолвится

Восстановилось само. Но «само починилось» - самая опасная фраза в постмортеме, потому что значит «починится и в следующий раз, и в следующий, пока не починится навсегда».

Чтение heap dump

Дамп на 721 MB, Eclipse MAT, два главных suspect’а. И оба из одного и того же HTTP-треда обработки GET /orders. Но в разных фазах

Фаза 1: парсинг ResultSet. 145 MB. Главный класс org.postgresql.core.Tuple, 49 051 штука. То есть до того, как Hibernate собрал хоть один entity, в памяти лежало 49 тысяч сырых JDBC-строк. Это не баг Hibernate это банальный JOIN, который умножает строки.

Фаза 2: маппинг в DTO. 155 MB. Главный класс SellerOrder (наша JPA-сущность), 23 730 штук. Hibernate дедуплицировал 49k JDBC-строк до 24k уникальных entity (нормальное поведение session cache). А дальше маппер тронул lazy-поле invoice.acceptedDate у каждого и Hibernate сходил в БД 24 тысячи раз. Тот самый // todo.

Один HTTP-запрос ≈ 300 MB heap. Два таких параллельно от одного клиента — и всё.

Балансировщик равномерно раскидал запросы по подам. Поэтому легли все три одновременно.

Важная деталь про композицию

Каждый из двух багов по отдельности прод бы не уронил.

  • N+1 на lazy invoice без раздутого ResultSet’а - 24k SELECT’ов и ~155 MB. Медленно, но не смертельно

  • SELECT без LIMIT’а, но без N+1 — 145 MB на ResultSet, мапнули, отпустили. Медленно, но не смертельно

Они срабатывают вместе, потому что N+1 удерживает entities в session до конца маппинга, а ResultSet ещё не успел освободиться к моменту его начала. Память не переиспользуется - она копится

Главный takeaway: // todo это не одиночная мина, это компонент сборки. Сам по себе он долго может лежать тихо и ждать, пока рядом не появится второй компонент.

Почему дожило до прода

Тут самое неловкое.

Deprecated endpoint не убрали, потому что на фронте на странице списка заказов есть чекбокс «Выбрать все». Он выделяет все заказы продавца для массовых операций (печать накладных, и тд)

Если перевести фронт на пагинированную ручку, «Выбрать все» начнёт выделять только текущую страницу. С точки зрения продавца это поломка: он жмёт «Выбрать все», подтверждает, и узнаёт, что половина заказов осталась неподтверждённой

Чтобы перевести фронт чисто, нужно либо переделать UX (отдельная операция «выделить вообще все»), либо завести бэкендовую массовую операцию (POST /orders/bulk-action), либо тянуть все ID отдельной лёгкой ручкой и работать по ним пагинацией

Любой из вариантов это продуктовая работа, а не техдолг. Поэтому она не делалась силами бэкенда. И поэтому @Deprecated стоял, а endpoint жил

Это не оправдание. Это объяснение. Разница в том, что объяснение позволяет принять решение: либо запускаем продуктовый трек на UX-миграцию, либо признаём, что endpoint остаётся, и тогда обязаны выставить ему лимиты и метрики

Не было ни того, ни другого.

Что бы я сделал по-другому

Не «починить N+1 пол года назад». Это слишком просто и неправда - мы видели todo сотни раз и осознанно не приоритизировали. Значит, дело не в дисциплине, а в недостающем сигнале

  1. Метрики использования на каждый deprecated endpoint. @Deprecated без счётчика обращений это надпись на двери, в которую всё ещё ходят люди. Простой счётчик в Prometheus с разбивкой по клиенту показал бы, что 80% трафика идёт от 3–4 крупных продавцов с десятками тысяч заказов. И «когда-нибудь уберём» сразу превращается в «вот эти три клиента сломаются, давайте поговорим»

  2. Hard limit на SQL-уровне. LIMIT 5-10к и явная ошибка, если строк больше. Пять минут работы, и OOM превратился бы в 500-ку с понятным сообщением

  3. Тикет на каждый // todo. Todo без тикета это известная команде проблема, невидимая планированию. Команда знает, что плохо. Менеджмент видит зелёный спринт. Когда срабатывает все удивлены, но удивлены по-разному

Эпилог

// todo мы убрали. Endpoint всё ещё @Deprecated, но теперь у него есть жёсткий лимит и счётчик обращений. Продуктовый трек на UX-миграцию запланирован

Heap dump лежит у меня локально на случай, если кто-то захочет посмотреть, как выглядит ResultSet на 49 000 строк в Eclipse MAT. Зрелище, на самом деле, поучительное: видишь не код, а его физическое воплощение в памяти. И понимаешь простую вещь - байты не врут. В отличие от наших оценок «ну там не должно быть много»