4817
Блог про архитектуру, работу и карьеру в ИТ. Автор - бэкендер, тимлид, руководитель разработки. Истории из опыта, инструменты и лайфхаки. Написать мне: @miralasse
Приключения моего лендинга
Вообще я хотела вчера написать технический пост. В бэклоге есть идеи, одну я обдумывала, набросала черновик и собиралась выделить несколько часов на написание.
Но пришлось заниматься немного другим.
С чего все началось.
Для курса по Кафке мне нужен был сайт. Обдумав функциональность, я решила запилить его на WordPress, чтобы легко дописывать тексты в случае необходимости. Плюс изначально задумывала больше форм.
Свою платформу для школы я не разрабатывала, все же это задача посерьезнее, требует времени, а время лучше уделить подготовке самого курса, поэтому платформу выбрала готовую.
Сайт сделала, осталось выбрать хостинг. Тогда были проблемы у ДЦ Яндекса, многие уходили на Селектел, так что я решила, что у них и так много нагрузки сейчас, и выбрала Бегет.
Задеплоила, всё ок.
И ровно в день анонса курса прилетают новости про критические уязвимости в PHP. Помните такие?
Читаю официальные рекомендации, советуюсь со знакомым лидом, у которого проекты на PHP. Получается, что проще всего поднять версию PHP. Захожу в настройки хостинга, а в списке доступных нужной версии нет 🙈 Ну оно и логично, патчи только вышли. Что делать?
С одной стороны, вряд ли кому-то нужен мой сайт, чтобы его взламывать.
С другой стороны, так ли мне нужен бэкенд на лендинге? Тем более от лишней формы я отказалась: протестировала пользовательский путь и решила, что чем меньше обязательных полей, тем лучше.
Когда проблемы с моими сервисами на работе, я умею смотреть на ситуацию спокойно: сначала быстрый фикс, если нужно, потом разбираем подробно и продумываем, как сделать, чтобы ситуация не повторилась. Даже если что-то серьезно сломалось, я примерно понимаю, что делать, и не воспринимаю каждую проблему как личную неудачу.
Тут же вроде и не баг, сайт работает, можно было даже просто забить и подождать новой версии PHP на хостинге, но я весь день правила мелкие огрехи на сайте, наконец анонсировала его, и тут такое. Я конечно расстроилась и стала ругать себя, зачем вообще притащила этот PHP и Wordpress. Как говорится, "так исторически сложилось" и "архитектура спроектирована под первоначальные требования".
Поговорила с друзьями, пришли к решению переписать сайт на статику. Спасибо Кодексу, получилось это сделать в тот же вечер, параллельно отвечая на вопросы ребят про курс. Большое спасибо всем, кто купил в тот же день! ❤️
Задеплоив новый вариант, я выдохнула, посмотрела на работающий сайт и подумала, что теперь все ок 😅
Но у инфраструктуры были другие планы.
Вчера утром я вижу новости, что у Бегета серьезные технические проблемы. Пу-пу-пу… Захожу на сайт - лежит. Моя первая мысль была: "ну, хорошо, что переписала, статический сайт перенести будет проще".
Вздыхаю, выбираю другой хостинг, переношу сайт.
Параллельно приходит письмо от платформы школы, что у них тоже проблемы с хостингом, и что по основному адресу они не доступны, но есть резервный. Ура, школа не подвела! Меняю на сайте ссылки, деплою заново.
Остается перепривязать домен к новому хостингу, домен у меня на reg.ru. Захожу в личный кабинет, чтобы поменять адрес, и ... у них тоже проблемы! 🤬
Официально ничего не писали, но в личный кабинет я не могла зайти около часа или двух. Наконец получилось, перепривязала домен, подключила сертификат, всё на удивление быстро обновилось, и сайт снова заработал. Фуууууух!
Вот так, казалось бы, простой одностраничник устроил мне приключение с уязвимостями, переездом и недоступными сервисами.
На работе я привыкла думать о том, что сервис может упасть (а для чего тогда все эти Transactional Outbox-ы и прочее), а баги - это вообще часть нашей профессии, их главное вовремя исправить. Но когда делаешь что-то свое, во что вложила столько времени и сил, почему-то каждый такой сбой воспринимается гораздо болезненнее, и профессиональное спокойствие куда-то испаряется. Рада, что во втором раунде смогла взять себя в руки и реагировать на ситуацию "по-рабочему".
В общем, сайт работает, курс готовится к запуску, а я очень надеюсь, что на этом приключения моего лендинга закончились.
Куда пропали 11 заказов
Однажды бизнес пришел к нам с багом: в отчете из нашей системы не хватает 11 позиций, которые есть в других системах.
Давайте для примера считать, что это были заказы.
«В отчете за прошлую неделю не хватает 11 заказов. В системе оформления они есть, менеджеры с ними работали, а в отчет они почему-то не попали. Можете проверить?»
Ребята, я сделала курс по Кафке! 🎉
Кажется, по моим постам вы уже знаете, что мне нравится подробно разбирать технические темы. Хотелось сделать курс, который будет полезен и тем, кто только знакомится с Кафкой, и тем, кто уже с ней работает и хочет разобраться глубже. Чтобы было много примеров, схем, практики и ответов на вопросы "а почему это работает именно так?"
Очень рада представить вам:
🎉 Кафка для самых маленьких 🎉
Курс подойдет, если вы:
✔️ еще не работали с Кафкой или уже сталкивались, но не погружались в детали
✔️ знакомы с базовой теорией, но не хватает практических кейсов, хочется разобрать реальные паттерны работы и истории с прода
✔️ проектируете интеграции или пишете код с ИИ, но не всегда можете объяснить его решения
✔️ хотите расширить и систематизировать знания, усилить экспертизу
Для каких профессий:
🟢 системных аналитиков
🟢 бэкенд-разработчиков на любом языке
🟢 QA-инженеров
🟢 архитекторов
Очень кратко, что будет:
➡️ сначала разберемся, в каких сценариях нужна Кафка, подробно посмотрим, как она устроена внутри, научимся работать с ней через UI-инструменты
➡️ затем разберем продюсеров, что такое идемпотентный продюсер, где нужно использовать outbox, на какие настройки обратить внимание
➡️ будет большущий модуль про консьюмеры: про настройки, идемпотентность, гарантии доставки, consumer lag, DLQ, static membership, расчет партиций под нагрузку, транзакции
➡️ разберемся с CDC через Kafka Connect, настроим свой CDC
➡️ научимся проектировать интеграции через Кафку, тестировать и мониторить.
Я сильно заморочилась с тем, чтобы курс при всей своей насыщенности оставался понятным: там много схем, все сложные механизмы я разбираю пошагово на конкретных примерах, даю ответы на частые вопросы с собесов, а также рассказываю реальные истории.
Моя цель, чтобы после обучения у вас сложилась цельная картина работы Kafka: как она устроена, как с ней работать и как принимать обоснованные технические решения в реальных задачах.
Формат: видеоуроки + живые созвоны + чат.
Старт: 9 ноября 2026
Длительность: 5 недель
🔗 САЙТ КУРСА
🔗 САЙТ КУРСА
🔗 САЙТ КУРСА
Случился у меня тут спор со знакомым архитектором. Тема довольно душная, потому что можно и так и так 😂
Рассказываю: в один топик Kafka отправляют события разных видов (для примера OrderCreated и OrderUpdated) в формате JSON. Где лучше разместить поле eventType - в headers или в value?
У сообщения в Kafka есть:
🔴Key - ключ сообщения, например ID заказа.
🔴Value - содержимое сообщения.
🔴Headers - дополнительные метаданные в формате ключ-значение.
🔴Служебные метаданные, например timestamp.
Вариант 1. Хранить eventType в headers
Событие создания заказа будет содержать отдельно:
headers:
eventType: OrderCreated
value:
{
"orderId": "123",
"total": 1500
}
headers:
eventType: OrderUpdated
value:
{
"orderId": "123",
"status": "PAID"
}
value:
{
"eventType": "OrderCreated",
"payload": {
"orderId": "123",
"total": 1500
}
}
value:
{
"eventType": "OrderUpdated",
"payload": {
"orderId": "123",
"status": "PAID"
}
}
Как race condition оставил без электричества 50 миллионов человек
14 августа 2003 года. Оператор энергосети США Джим смотрит на экран: всё спокойно, никаких аварийных предупреждений. Джим не знает, что система уже перестала показывать ему реальную ситуацию.
В это же время в энергосети нарастала аварийная ситуация, которая через несколько часов оставит без электричества около 50 миллионов человек в США и Канаде.
И одну из ключевых ролей в этой истории сыграла ошибка синхронизации в коде.
➡️ Что произошло?
Энергокомпания FirstEnergy использовала систему управления электросетями GE XA/21, которая была написана на C/C++ и работала с 1995 года.
Система получала информацию с оборудования, отслеживала состояние электросети и предупреждала операторов о проблемах, например, об отключении линии электропередачи.
14 августа в 14:14 модуль обработки предупреждений перестал работать в штатном режиме.
Позднее инженеры General Electric (GE) выяснили, что причиной была race condition - гонка между двумя параллельно выполнявшимися процессами.
Оба процесса имели доступ к одной структуре данных в оперативной памяти. Из-за бага они смогли одновременно получить доступ на запись, в результате данные оказались повреждены, а приложение, обрабатывавшее предупреждения, попало в бесконечный цикл.
Интересный факт, что эта ошибка могла возникнуть только если оба процесса столкнулись в коде практически одновременно: окно, в котором это было возможно, измерялось миллисекундами!
И именно 14 августа сложилась нужная комбинация событий.
Но основная проблема заключалась в том, что модуль предупреждений не завершился с ошибкой, а завис в бесконечном цикле и перестал выполнять основную функцию. При этом система никак не сообщила операторам, что они больше не получают предупреждения об авариях.
В тот день было жарко. Из-за нагрева и нагрузки несколько ЛЭП провисли, коснулись близко растущих деревьев и отключились. Но из-за внешне невидимой проблемы Джим и другие операторы не увидели на своих экранах никаких алертов.
Тем временем новые события продолжали поступать, но зависший обработчик больше не мог их обрабатывать. Очереди росли, входные буферы переполнялись.
Продолжение ⬇️
Недавно рассказывала про Avito.Tech.Conf — конференцию для лидов и менеджеров. На выходных я посмотрела запись онлайн-трансляции.
Хочу рассказать про два доклада, которые мне особенно понравились.
1️⃣ Доклад Артёма Арюткина о том, почему ускорять написание кода с помощью AI недостаточно.
Как мы знаем по собственному опыту, написание кода занимает только часть времени инженера. Остальное: разобраться в постановке, уже существующем коде, другой документации, с кем-то поговорить, потестить сделанное, провести код-ревью коллег, ну и, конечно, переписка и созвоны.
Последние пару лет индустрия в основном пыталась решить вопрос ускорения написания кода с помощью AI, и во многом это получилось. Но сразу появилось следующее бутылочное горлышко — код-ревью. Сейчас ускоряют его, а значит появится следующее.
Поэтому задачей руководителя становится не ускорение именно написания кода, а поиск мест, где конкретно в нашем процессе теряется время, и расшивание именно этих ограничений.
Причем определить это основываясь только на жалобах команды не всегда получится. Мне понравился пример из доклада: если разработчику приходится периодически перезапускать долгую сборку, он может вообще на это не жаловаться. Просто привыкает и начнет закладывать это время в оценку задачи.
"Это невозможно, и сейчас я объясню почему"
Представьте ситуацию.
Дисклеймер: пример вымышленный, проблема реально встречающаяся 🐤
Вы разрабатываете сервис приема заказов order-service. В зависимости от состава заказа и истории покупателя вы можете предлагать разные виды оплаты: предоплату, частичную предоплату, постоплату. Логика анализа состава заказа реализована в order-service, а за информацией по покупателю он обращается в client-service другой команды.
Фронт вызывает:POST /orders
{
"clientUid": "some-uuid",
"products": [{...}, {...}]
}
{
"orderUid": "some-uid",
"orderStatus": "WAITING_FOR_CONFIRMATION", //ожидает подтверждения
"paymentType": "ON_DELIVERY" //оплата при получении
}client-service отвечал order-service за 500 мс. Но что-то пошло не так, и теперь он отвечает по 20 секунд order-service 30 секунд, сам order-service отрабатывает не более 500 мс, но часть запросов от фронта стала падать по таймауту.client-service.order-service работает так:Например, в пуле 10 соединений.
Первые 10 запросов заняли все соединения и на 20 секунд ушли ждать client-service.
11-й запрос уже не может начать работу с БД и ждет свободное соединение.
Через ~20 секунд оно освобождается, 11-й запрос сохраняет заказ, но теперь ему предстоит еще 20 секунд ждать ответ client-service.
Итого около 40 секунд end-to-end: 20 сек ждал в очереди к БД + 20 сек ждал ответ от client-service.
Для фронта 11-й запрос упадет по таймауту, хотя сам client-service для каждого отдельного вызова отвечает за 20 секунд.
client-service - подойдет, если много заказов от одних и тех же клиентов, но надо уточнять, как инвалидировать кэш, как часто обновляется ответ по клиенту (может он исчерпает лимит на постоплату, а мы из-за кэша не заметим)client-service paymentType определять только по локальной логикеclient-service, почему сервис вместо 500 мс стал отвечать 20 секунд 😅
Читать полностью…
Только не говорите, что ещё не успели зарегаться на Avito.Tech.Conf 👀
Ко мне пришли ребята из Авито.Тех и попросили рассказать про Avito.Tech.Conf.
Это конференция для тимлидов, руководителей и всех, кто управляет процессами и командами. Собираются большим, но уютным коммьюнити, чтобы обсудить наболевшее, послушать доклады и просто пообщаться и переключиться 🚀
Какие темы будут обсуждаться:
– Как меняется сетап команды в эпоху AI-агентов;
– Как при этом не стать «эффективным менеджером» в плохом смысле;
– Когда стоит отказаться от стабильности и выбрать фокус на личных целях;
– Как расти без роста штата на практике.
Кстати, ещё выкатили обновление программы — а там в панельной дискуссии участвует учёный-астрофизик Владимир Сурдин! Он расскажет о личном пути в науке, о том, какие загадки космоса по-прежнему по-настоящему увлекают физиков и астрономов, и почему фундаментальные исследования важны, даже если их эффект нельзя положить в презентацию с ROI.
26 сентября в Москве, участие бесплатное.
Мест на офлайн уже не осталось, но можно зарегистрироваться на онлайн-трансляцию!
Так что если думаете — не думайте. Забирайте последние места по ссылке.
Куда пропало обращение
Однажды от руководителя техподдержки пришло письмо, суть которого сводилась к следующему:
"В выгрузке потерялось обращение 58122. Его кто-то удалил?"
12:41:03 Creating ticket, id=58122
🎞 Готова запись стрима про Кафку:
https://youtu.be/2aRKsD-MWDA
Большое спасибо всем, кто пришел, слушал и задавал вопросы! Благодаря вам получилось очень живое и интересное обсуждение 💖
Соотношение чтений к записям (read/write ratio)
На всех собесах, где поднимались архитектурные вопросы (сисдиз или нет), интервьюер никогда не подсвечивал это сам, но обычно одобрительно кивал, когда я озвучивала эту характеристику системы. Давайте посмотрим, для чего это может быть полезно.
➡️ На сисдиз собесе
1️⃣Влияет на выбор подходящей БД
Архитектура хранения зависит в том числе от того, будут ли в системе преобладать чтения или записи, а также от абсолютных значений нагрузки: условно 1000 RPS или 1000000 RPS.
Для систем с преобладанием чтения (read-heavy) стоит выбирать хранилище, оптимизированное для сценариев чтения (поиск, фильтрация). Например, реляционку или подходящую NoSQL БД.
Для write-heavy систем часто хорошо подходят базы данных на основе LSM-деревьев (на мой вкус про них очень хорошо написано в кабанчике), например Cassandra.
К сожалению, я не работала с Кассандрой в проде, но однажды успешно прошла собес предложив и обосновав ее использование для проектируемой системы.
Конечно, и PostgreSQL можно использовать в write-heavy сценариях, особенно если нам важны полноценные ACID-транзакции. Вопрос скорее в масштабе нагрузки и других требованиях системы.
2️⃣Для предложения кэширования или наоборот асинхронной обработки
Для read-heavy систем, если одни и те же данные читаются многократно, часто ставят кэш перед БД, чтобы снизить нагрузку на нее.
Для write-heavy кэш, конечно, не решит проблему высокой нагрузки на запись. Тут, если нужно и система позволяет, может пригодиться асинхронный подход: между приемом данных и их обработкой можно поставить очередь. Она позволит сглаживать пики нагрузки и обрабатывать записи с той скоростью, которую выдерживает downstream-система.
Конечно, если средняя скорость поступления данных постоянно выше скорости обработки, очередь проблему не решит. Обычно на сисдизах тут появляется горизонтальное масштабирование обработчиков.
3️⃣Для обсуждения репликации
Для read-heavy систем может использоваться подход, когда запись производится на мастер (лидер), а часть чтений уходит на реплики.
Для write-heavy систем репликация тоже нужна, в первую очередь для отказоустойчивости (если лидер вышел из строя, переключились на реплику).
➡️ В жизни
На практике мы не часто сталкиваемся с построением с нуля большой системы с выбором всех технологий. Тем не менее оценка соотношения чтений и записей может пригодиться и для локальных доработок.
Например, когда проектируем новую фичу, требующую новых таблиц в БД:
Если будет heavy-read, то можно сразу задуматься об индексах. По каким полям будет частый поиск? Фильтрация? Сортировка?
Если будет heavy-write, то наоборот не стоит перегружать таблицу индексами, чтобы не замедлять запись.
Если нагрузка высокая, и данные при записи сильно отличаются от того, что нам удобно для чтения, можно поддерживать отдельную read-модель: сохранять исходные данные в одной таблице, а асинхронно наполнять другую, оптимизированную под конкретные запросы. Я на практике встречала такой подход.
А что преобладает в вашей части продукта?
🦆 - чтения
🐼 - записи
🦄 - в разных частях по-разному
Сегодня новый #женя_есть_вопрос
Женя, привет!Читать полностью…
Сейчас я лид мобильной разработки в большом финтехе: у меня 10 прямых и около 25 непрямых репортов — в основном разработчики.
Чем дольше работаю, тем сильнее ощущение, что мобильная разработка для меня становится слишком узким направлением. В текущей роли я постоянно пересекаюсь с backend, архитектурой и другими частями продукта, а иногда сам пишу небольшие backend-фичи. При этом кажется, что рынок для backend-разработчиков существенно шире, чем для mobile.
Из-за этого начал всерьёз рассматривать переход из mobile в backend.
Но здесь есть развилка. Можно попробовать сделать переход внутри текущей компании, однако это, скорее всего, означает заметную потерю в деньгах и достаточно большой риск: на первых квартальных ревью отсутствие production-опыта в backend почти наверняка будет восприниматься как снижение уровня. При этом формального процесса перехода у нас нет — всё достаточно неформально, и позиции T-shaped инженера тоже не существует.
Второй вариант — продолжать развиваться в текущем направлении, а backend постепенно осваивать самостоятельно и уже потом искать внешнюю позицию. Но тогда возникает вопрос, насколько разумно фактически начинать карьерный трек заново после нескольких лет роста в leadership.
Как бы вы смотрели на такую ситуацию? Насколько оправдан переход из mobile в backend на этом этапе карьеры, и какой путь вы бы выбрали: внутренний переход с временным откатом по уровню/деньгам или подготовку к внешнему переходу без потери текущего уровня?
Спасибо
➡️ Keyset-пагинация (seek-метод)
Если при OFFSET мы говорим БД:
"пропусти первые 70 000 записей и дай следующие 500",
"дай следующие 500 после той записи, на которой мы закончили прошлый раз".
SELECT ...
FROM payments
WHERE id > 70000
ORDER BY id
LIMIT 500;
SELECT ...
FROM payments
WHERE (created_at, id) < ('2026-09-08 12:30:00', 70000)
ORDER BY created_at DESC, id DESC
LIMIT 500;
(created_at, id).GET /payments?limit=500&lastId=1000
{
"items": [...],
"lastId": 1500 //если идем по возрастанию
}{
"items": [...],
"cursor": "eyJpZCI6ODQyMX0"
}GET /payments?limit=500&cursor=eyJpZCI6ODQyMX0
В случае OFFSET пагинации на UI часто показывают общее количество страниц. Для получения totalElements обычно выполняют дополнительный SELECT COUNT(*).
Если мы реализовали keyset-пагинацию и хотим в ответе бэка сообщить, есть ли еще данные или это конец списка, то это можно реализовать без дополнительных запросов в БД.
Просто делаем исходный запрос с LIMIT на 1 больше:
... LIMIT 501;
Если вернулась 501 строка, то отвечаем:
{
"items": [...], // тут запрошенные 500
"hasNext": true // есть еще записи, запрашивайте
}
Если вернулось меньше 501 строки, то отвечаем:
{
"items": [...], // тут запрошенные 500
"hasNext": false // больше записей нет
}
Давайте разбираться.
Мне кажется, проблема тут не столько в системном аналитике, сколько в лиде. Насколько я поняла, лид сейчас не хочет отстаивать ваши интересы и привлекать лида аналитики к решению вопроса.
Возможно, у аналитика действительно не хватает опыта или знания проекта, но специалиста можно вырастить. Лид аналитики может выделить наставника, который будет помогать и ревьюить, может порекомендовать какие-то обучающие материалы, может договориться о помощи от разрабов и QA, если не хватает знаний проекта. Да просто может тактично обозначить человеку, что такая проблема есть. Возможно, ваша СА уделяет много внимания бизнес-части и не подозревает, что есть пробелы в технической части.
Тем не менее, не зная подробностей, не рискну предлагать разговаривать с аналитиком. Люди разные, у тебя накопилось недовольство, как бы это не привело к конфликту. Вместо этого предлагаю зайти через изменение процесса.
Ты не написала, есть ли у вас грумминг, буду исходить из того, что есть. Если нет, предлагай его внедрить.
Нужно на ретро или другой встрече попросить готовить постановку хотя бы за день до грумминга, чтобы разработчики могли ознакомиться заранее.
До грумминга внимательно читай спеку, ищи пропущенные части, а на грумминге обо всем этом спрашивай. Не конфликтуй! Без претензий, просто задавай вопросы. Привлекай других разрабов к обсуждению, правьте связи в БД, добавляйте эндпойты прямо на созвоне. Подробности СА может расписать потом, если требуется, главное сами новые задачи не потерять.
Если у вас разрешены сторонние ИИ, можно отдавать спеку на ревью кодексу или клоду, чтобы он накидал вопросов с опорой на кодовую базу. Он может проверить существующие эндпойнты и найти те, которые затронет новая фича.
Еще привлекай QA к анализу спеки заранее. Если тестировщик читает постановку, когда код уже готов, то даже с пробелами в спеке он составит свое представление о функциональности и пойдет тестить, что там разрабы сделали.
А если тестировщик использует shift-left: проверяет специфицикации и пишет тест-кейсы еще по постановке, то он может задать даже больше вопросов, чем разраб. Ведь QA знают проект лучше всех.
Проси QA посмотреть спеку до грумминга, задавай ему/ей вопросы, пробуй применить его экспертизу по продукту к этой задаче заранее.
Да, это отнимет у тебя время на подготовку к груммингу, но зато снимет большую часть срочных доработок потом. Плюс такое поведение покажет тебя ответственным и вовлеченным сотрудником. Если на грумминг ходит ваш менеджер, он это заметит. Это может даже привести к твоему повышению.
А еще возможно, что такие обсуждения помогут вашему аналитику прокачаться. Если проблема не в том, что человек просто забивает, а в недостатке опыта, то она может заметить свои частые ошибки, почитать статьи про них и вырасти как специалист.
📍Задать свой вопрос можно здесь: https://forms.gle/SPE6NEALG9vcnF3s7
Ребят, что бы вы рекомендовали в такой ситуации?
В комментах под одним из постов развернулось обсуждение того, что считать микросервисом.
Еще в начале карьеры разработчика я услышала и запомнила два забавных определения:
Если над сервисом работает команда, которую можно накормить всего 2 пиццами, то это микросервис
Логика микросервиса должна полностью умещаться в голове разработчика.
Микросервисы — это сервисы, которые:
independently deployable - можно поставлять независимо (без необходимости одновременно выкатывать другие сервисы)
loosely coupled - слабо связаны друг с другом
Обычно они организованы вокруг бизнес-возможностей, а отдельный сервис часто находится в зоне ответственности одной небольшой команды.
Микросервисы — независимо релизящиеся сервисы, смоделированные вокруг бизнес-домена.
Микросервис — это сервис с маленьким публичным интерфейсом.
Куда пропали 11 заказов - развязка
В прошлом посте у нас пропали 11 заказов: у продюсера были, в Кафке были, а консьюмер почему-то их то ли не получил, то ли получил и не сохранил, не записав ничего в логи. А потом снова стал нормально сохранять.
В чем же было дело?
Спасибо, что предлагали варианты в комментариях к первому посту! Были интересные мысли и правильные догадки 🔥
Конечно, дело было в свежем релизе 😅 Несмотря на то, что он прошел успешно, и в нем не менялась ни бизнес-логика, ни явные настройки консьюмера, он содержал рефакторинг с делением на два микросервиса и изменением имен микросервисов. Имя микросервиса, в котором был и остался консьюмер, изменилось.
А имя консьюмер-группы group.id этого консьюмера в блоке настроек было задано не конкретной строкой, а как переменная, ссылающася на имя микросервиса.
group.id = ${APPLICATION_NAME}
APPLICATION_NAME изменилось в конфигах, то и group.id консьюмера изменился.auto.offset.resetearliest - читать с самого раннего доступного сообщения (там допустим сообщения 7-дневной давности)latest - начать читать свежепоступающие сообщенияby_duration - появилась в Kafka 4.0. Позволяет начать читать сообщения за определённый период до текущего момента.none - выбросить исключение, если нет сохраненного оффсета.auto.offset.reset: latest), пропустив таким образом нападавшие за время отсуствия консьюмера заказы. При этом сама Kafka ничего не потеряла.
Как я стала разработчиком
Еще до разработки я работала менеджером проектов. Причем у меня даже была карьера: я выросла до лида проджектов в аутсорсе, прошла пару обучений на PM и даже подумывала о сертификации PMP. Но в какой-то момент меня стало тяготить то, что я ничего не могу делать своими руками. Ну типа вот дизайнер - он делает макеты, разрабы - пишут код, они умеют что-то конкретное. А мой обычный список дел был: "подготовить план спринта для обсуждения", "пропушить то-то", "договориться с заказчиком ", "сделать презентацию" и т.д., и мне казалось, что уметь тут особо нечего.
Сейчас я понимаю, что управлять проектом так, чтобы были довольны и бизнес-заказчик, и твой руководитель, и команда разработки — это совсем не просто, и одной ответственности здесь мало, нужные навыки приобретаются с опытом. Но тогда меня одолевал синдром самозванца.
Я смотрела на ребят-разрабов, которые получали от меня постановку, а выдавали реально работающее ПО, и думала, что хочу так же. Хочу уметь что-то создавать, а не только организовывать.
Но пока я думала эти мысли и сомневалась в себе, мне предложили перейти в крупную компанию с повышением зарплаты. Я решила, что мое то ли выгорание, то ли непонятно что пройдет со сменой работы, и согласилась.
Фотка к посту как раз из офиса той компании, 2016 год.
Давайте сегодня обсудим кейс: как пережить пик запросов. Кейс и для собеса и для жизни 😃
Представьте ситуацию: вы работаете над образовательной платформой, на которой студенты проходят курсы и выполняют задания.
Есть два сервиса:
• сервис проверки заданий, который проверяет ответы студентов и выставляет оценки - им занимается другая команда
• сервис результатов и статистики - это наш сервис.
Сервис проверки заданий отправляет вашему сервису HTTP-запрос с оценкой студента. Наш сервис должен сохранить оценку в PostgreSQL и перерассчитать средний балл по студенту.
В обычное время это занимает 300 мс: сохраняем оценку, перерассчитываем и сохраняем средний балл и отвечаем 200 OK.
У каждой оценки есть уникальный UUID, в БД на это поле установлен unique constraint, поэтому повторные запросы легко отличить от новых и исключить дублирование.
Недавно вы реализовали новые фичи: теперь сервис не просто считает средний балл, а делает более широкую сводную статистику с рейтингами студентов. Для этого он выбирает данные из БД и выполняет довольно много вычислений в памяти приложения. Теперь обработка каждой оценки с расчетом всего этого добра занимает по несколько секунд, но в таймаут укладываемся.
Наступает последний день сдачи и куча студентов начинает сдавать работы на проверку. Количество запросов от сервиса проверок резко возрастает. Наш сервис перегружен, обработка запросов растягивается. Плюс сервис проверок ретраит запросы. Хотя дубли мы отсекаем до основных расчетов, ретраи все равно создают дополнительную нагрузку.
Образовательная платформа активно развивается, подобные пики будут повторяться, нужно что-то предпринимать.
Как можно решить эту проблему?
Обычно для ограничения количества запросов используют Rate Limiter, но это защитный механизм. Сюда же Circuit Breaker на вызывающей стороне. Это отличные паттерны, они помогают уменьшить нагрузку на наш сервис, но оставляют проблему вызывающей стороне. Здесь же хочу разобрать подходы, как справляться с нагрузкой, а не отбиваться от нее.
Варианты:
1️⃣Вертикальное или горизонтальное масштабирование
Если от нагрузки страдает сам сервис, а не БД, то можем выделить ему больше ресурсов или поднять несколько инстансов.
Плюсы:
➕ просто, если есть инфраструктура, не нужны добработки
Минусы:
➖ дополнительные ресурсы
➖ нагрузка на БД может вырасти от увеличения параллельных запросов
2️⃣При поступлении запроса только сохранять оценку в БД, а статистику пересчитывать отдельно фоновой джобой по расписанию.
Но нужно продумать, как ограничивать нагрузку от фоновой джобы, чтобы ее вычисления не нагружали сервис дополнительно, мешая приему новых оценок.
Плюсы:
➕БД у нас уже есть, дополнительная инфраструктура не требуется
Минусы:
➖ статистика обновляется с задержкой, а не сразу после получения студентом новой оценки
➖нужно учесть, чтобы несколько экземпляров джобы не выполняли один и тот же пересчёт одновременно
3️⃣Договориться с сервисом проверки, чтобы вместо синхронных запросов, слали нам сообщения в очередь сообщений (или выполняющую ее роль Кафку).
Плюсы:
➕убираем зависимость сервиса проверки от скорости нашей обработки
➕ очередь буферизирует пиковую нагрузку
Минусы:
➖ работа для другой команды
➖ новая инфраструктура
➖ следить за длительностью обработки сообщения и общим лагом обработки
Если сообщения поступают быстрее, чем мы успеваем обрабатывать, лаг обработки и задержка статистики будут расти и расти.
4️⃣Или если коллеги пока не готовы, мы можем сделать себе внутреннюю очередь: при получении HTTP-запроса сразу отправляем событие в Кафку и возвращаем 200 OK после подтверждения успешной записи. В БД на этом этапе ничего не сохраняем, поэтому нам не требуется Transactional Outbox для согласования двух записей.
Наш консьюмер читает события из Кафки, сохраняет оценки в БД и делает нужные расчеты с дедупликацией по UUID.
Плюсы:
➕ быстро отвечаем сервису проверки
➕ очередь буферизирует пиковую нагрузку
Минусы:
➖ новая инфраструктура
➖ следить за длительностью обработки сообщения и общим лагом обработки.
Какой вариант вы бы выбрали?
Продолжение истории 2003 года
В системе было предусмотрено резервирование.
Когда в 14:41 отказал основной сервер, система автоматически переключилась на резервный. Но на резервный сервер переехали те же приложения, включая неисправный обработчик предупреждений.
В 14:54 отказал и резервный сервер.
IT-специалисты перезапустили основной сервер, проверили, что он работает и нужные процессы запущены, вот только обработчик предупреждений по-прежнему не выполнял свою функцию.
В 15:05 отключилась одна из важных высоковольтных линий, затем ещё несколько.
В диспетчерской Джима раздался телефонный звонок. В трубке звенел взволнованный голос коллеги из American Electric Power. Он сообщал об отключении линии электропередачи. Но у Джима на экране никаких предупреждений не было. Более того, это был уже второй звонок по поводу той же линии. Однако Джим и его коллеги, не зная о неисправности системы, не смогли правильно оценить ситуацию.
В 16:05 отключилась ещё одна критически важная линия. Началось каскадное отключение энергосистемы. В течение нескольких минут без электричества остались огромные территории США и Канады и около 50 миллионов человек. В отдельных районах электричество не могли восстановить до 4-х суток.
➡️ Разбор аварии
После аварии инженеры GE начали искать причину. Код модуля обработки предупреждений содержал около миллиона строк C/C++, а вся система — порядка четырёх миллионов.
По словам представителей GE, к тому моменту программное обеспечение накопило суммарно более трех миллионов часов эксплуатации у разных клиентов без проявления именно этого дефекта.
Разработчикам потребовалось около восьми недель, чтобы воспроизвести ошибку. Для этого они намеренно замедляли выполнение программы, добавляя задержки в код и подавая на вход разные последовательности событий.
Так им удалось воспроизвести состоние гонки и выяснить, почему обработчик уходил в бесконечный цикл.
Официальное расследование выявило целый набор причин аварии: проблемы с обслуживанием ЛЭП, недостаточную осведомленность диспетчеров, ошибки в организации работы и другие факторы.
➡️ Выводы:
1️⃣Даже годами работающий код может содержать ошибку, для проявления которой нужна редкая последовательность событий и совпадение по времени.
2️⃣Наличие резервного сервера не гарантирует устойчивости системы, если оба сервера подвержены одному программному дефекту.
3️⃣Живое приложение не обязательно означает правильно работающее приложение. Для критичных модулей желательно предусмотреть возможность проверять не только состояние, но и способность выполнять бизнес-функцию.
У меня об этом есть и своя история, к счастью не такая масштабная, когда формирование отчета зависало без каких-либо ошибок в логах.
А были ли у вас ситуации, когда алерты не приходили?
Redis vs Valkey
По моим ощущениям про Valkey стали говорить всё больше, решила сделать небольшой обзорный пост без погружения в технические тонкости работы кластеров.
➡️ Что это такое
Redis — это хранилище типа "ключ-значение", которое держит данные в оперативной памяти. Благодаря этому он работает очень быстро, поэтому обычно используется как кэш.
Valkey — изначально форк Redis и его прямой конкурент.
При этом и Redis, и Valkey умеют сохранять данные на диск, реплицироваться и работать кластером. Да и других фич много, например:
🔴TTL - можно задать время жизни ключа, после которого он автоматически удалится (например, чтобы хранить смс-код 60 секунд)
🔴Атомарный счетчик - можно атомарно увеличить или уменьшить значение (например, чтобы посчитать количество запросов).
🔴Sorted Set - множество элементов с числовым score, которое автоматически поддерживается отсортированным (например, удобно формировать оперативный рейтинг игроков).
➡️ Немного истории
До 2024 года Redis распостранялся под лицензией, которая позволяла брать код, модифицировать и даже строить на его основе коммерческие Redis-as-a-Service (так делали AWS, Google Cloud и др.)
В 2024 Redis Ltd. предложили две новые лицензии:
1️⃣Redis можно бесплатно использовать внутри компаний, но нельзя делать конкурирующий managed service
2️⃣Можно предоставлять Redis-as-a-Service, но возникают серьёзные требования по открытию исходников инфраструктуры этого сервиса
Для компаний, которые просто использовали Redis как кэш внутри своих продуктов, почти ничего не изменилось. А вот облачным провайдерам стало сложнее продавать Redis-as-a-Service.
На основе версии под старой лицензией (Redis 7.2.4) сделали форк (независимую копию) и назвали его Valkey.
Valkey стал активно развиваться параллельно Redis.
Он имеет опенсорсную лицению, на ней можно делать облачные managed service.
Redis в 2025 добавил третий вариант лицензии со своими особенностями.
Valkey сохранил совместимость с Redis 7.2 и более ранними версиями, поэтому со старых Redis можно переехать без изменения кода. В новых версиях продукты постепенно расходятся.
➡️ Базовое сравнение
▶️Лицензия
У Valkey: BSD-3-Clause.
У Redis: RSALv2, SSPLv1 или AGPLv3.
Для обычной компании, которая использует Redis внутри своего продукта, особо разницы нет. Если же вы делаете Redis-as-a-Service, то лицензия Valkey проще.
▶️Функциональность
Основная функциональность (кэширование, sorted sets, TTL и др.) есть и там и там.
Redis активно развивает специализированные фичи:
full-text search
vector search
time series
Bloom filter
top-k
Часть из них есть в модулях Valkey.
Сравнение по фичам на сайте Redis
▶️Производительность
Valkey сильно оптимизирует производительность и при сравнении с Redis 7 обычно выигрывает, а вот с Redis 8 - не всегда. Насколько я разобралась, все зависит от операции и характера нагрузки.
Ссылки на разные бенчмарки приложу в комментах.
▶️Однопоточность и использование остальных ядер
У Redis есть особенность: выполнение команд идет последовательно в одном потоке.
Условно для наглядности:
CPU
core 1 ██████████
core 2 ██
core 3 ██
core 4 ██
Недавно знакомый разработчик ходил на собеседование, и там его спросили:
"Что такое идемпотентность?"
POST /orders/search
{...}
Idempotency-Key.
В прошлой жизни, когда я была менеджером проектов, одним из первых мест работы у меня был большой телеком. И там у меня была потрясающая руководительница, к которой я могла прийти с любой проблемой, а она всегда говорила:
"Ничего страшного, сейчас разберёмся, что-нибудь придумаем"
"Ко мне ты можешь приходить с любыми вопросами. Но если ты хочешь расти дальше, то выше руководители будут другие. К ним нельзя приходить только с проблемой, они будут ждать, что ты сразу и решение предложишь, а они его или утвердят или скорректируют".
"Написала уже два письма, ходила в личку, но пока ничего не согласовали и сроков не называют. Пока продолжу их пинговать?"
Куда пропало обращение - развязка
В прошлом посте у нас загадочно пропало обращение 58122. В БД его не было, хотя 58121 и 58123 были, а в логах нашли запись о создании:
12:41:03 Creating ticket, id=58122
org.postgresql.util.PSQLException: ERROR: null value in column "department_id" of relation "ticket_status_history" violates not-null constraintticket с новым id, ticket_status_history тоже добавлялась первая запись для этого обращения.ticket_status_history не заполнилось. В БД ушел запрос с null в обязательном поле. На это БД логично ответила ошибкой нарушения non-null constraint и откатила всю транзакцию:BEGIN
INSERT INTO ticket ...
-- sequence выдала 58122
INSERT INTO ticket_status_history ...
-- ERROR
ROLLBACK
В доке оказывается так и написано, что значения из sequence не используются повторно, чтобы избежать блокировки параллельных транзакций, получающих числа из одной и той же sequence. Причем пропуски могут появляться не только из-за rollback: например, при INSERT ... ON CONFLICT значение из sequence может быть получено еще до обнаружения конфликта. Поэтому PostgreSQL sequence не подходят как источник непрерывных последовательностей.
Ссылка на доку PostgreSQL на английском
Ссылка на доку на русском от PostgresPro
🔗 Подборка постов про Кафку
Как обещала на стриме, собрала посты про Кафку в удобное оглавление:
Устройство:
🟢 Топики и партиции
🟢 Брокеры и репликация
🟢 Типы топиков
🟢 Продюсеры
🟢 Консьюмеры и консьюмер-группы
😱 История про захлебнувшихся консьюмеров
🔥 NEW: Очереди в Кафке
🟢 NEW: Diskless Topics
Паттерны:
🟣 Transactional Outbox
🟣 Dead Letter Queue
🟣 Идемпотетный консьюмер
Дополнительно:
🟡 Гарантии доставки в Кафке
🟡 Может ли нарушаться порядок сообщений
🟡 Как посчитать количество партиций
🟡 Карточки про Кафку и RabbitMQ
Сегодня стрим по Кафке в 19:00
Планируем не в формате доклада, а в формате вопрос-ответ, чтобы было веселее. Андрей будет задавать свои и ваши вопросы, попробуем смоделировать на симуляторе разные ситуации.
Приходите!
🔗 Ссылка на Timepad
Привет!
У тебя уже очень крутая карьера и хороший управленческий опыт. Лидить 10 человек напрямую и 25 косвенно в большом финтехе — это серьезный уровень 🔥
Из вопроса мне не совсем понятно, хочешь ли ты перейти в бэк и сохранить лидскую позицию или хочешь стать бэкенд-разработчиком 🤔
➡️ Если речь про переход в разработчики, то при выборе отталкивайся от того, как тебе будет комфортнее:
🔴при внутреннем переходе ты сможешь освоиться, собрать кейсы и при смене работы будешь чувствовать себя уверенно. Обсуди с руководителем, получится ли не просесть по деньгам, если ты будешь закрывать задачи?
я в свое время выбрала переход с откатом по деньгам, но не готова это рекомендовать, особенно сейчас
🔴последние годы столько людей устраиваются мидлами без опыта работы вообще, так что ты как опытный в разработке человек наверняка сможешь подготовиться и найти работу бэком сразу, если задашься такой целью. Ты еще и истории реальные на собесе расскажешь.
"насколько разумно фактически начинать карьерный трек заново после нескольких лет роста в leadership"
Любите ли вы Кафку так, как люблю её я?
Мы с Андреем из Yet Another Analyst решили сделать коллаб — совместный стрим про Кафку.
Поговорим про распределение сообщений, консьюмер-группы, масштабирование, можно ли сломать порядок сообщений, какие гарантии доставки можно реализовать в Кафке.
Посмотрим очень классный симулятор работы Кафки!
Пишите вопросы в комментах к этому посту, постараюсь на стриме на все ответить.
🗓 Когда: в среду 16 сентября в 19:00
🔗 Ссылка на Timepad
Пагинация при работе с БД
Хочу рассказать не только базовую теорию, но и про подводные камни и практические подходы к оптимизации.
➡️ OFFSET пагинация
Это самый распространенный вариант.
С фронтенда приходит запрос вида:
GET /orders?page=3&size=20
SELECT ...
FROM orders
ORDER BY created_at DESC, id DESC
LIMIT 20
OFFSET 40;
{
"items": [...],
"page": 3,
"size": 20,
"totalElements": 247,
"totalPages": 13
}OFFSET 40 говорит базе, что нужно пропустить первые 40 записей и начать отдавать, начиная с 41-й (для 3-й страницы). Однако база не может сразу прыгнуть на 41-ю строку. Она работает так:Из-за большего OFFSET записи с последних страниц будут отдаваться дольше, чем с первых, но иногда возможен лайфхак с разворотом сортировки.
Например, если мы показываем на UI сначала самые свежие заказы, то последняя страница заказов будет содержать самые ранние:
SELECT ...
FROM orders
ORDER BY created_at ASC, id ASC
LIMIT 20;
Оговорюсь, что такой запрос подходит для задачи "дай 20 самых ранних записей", но не является способом получить последнюю страницу классической пагинации, потому что возможно следующее несоответствие:
Всего 53 записи, размер страницы 20.
1 стр: 1-20 - 20 строк
2 стр: 21-40 - 20 строк
3 стр: 41-53 - 13 строк
ORDER BY created_at ASC LIMIT 20 вернет 20 самых старых строк вместо 13.
SELECT ...
FROM payments
ORDER BY created_at DESC, id DESC
LIMIT 500
OFFSET 70000;
Пятничный привет! Меня не было неделю — навалилось много дел, в том числе связанных с тем, что уже второй год 1 сентября для меня снова День знаний 😄
Вожу в школу своего второклассника.
Сегодня я уже немного выдохнула, надеюсь дальше продолжать в обычном режиме.
В рубрику #женя_есть_вопрос прислали непростой вопрос про ситуацию на работе:
Привет, очень надеюсь на твой совет.Читать полностью…
У нас в команде есть бэки, фронты, бизнес-аналитик и два системных. Постановка по задачам зона ответственности системных аналитиков. Я работаю в разработке. Одна из аналитиков всё время недоописывает все входящие в доработку таски. Если нужно доработать больше 2-3 ручек, то наверняка какую-то она в постановке пропустит. Получается DoR есть, но только формально, фактически многие детали не учитываются. По технической части тоже не все гладко, например, путаница в связях one-to-many и many-to-many, но это не так критично.
Системные аналитики декомпозируют свои истории, и задачи раздают разным бэкендерам параллельно. Так как часть задач не описана, они не поступают в разработку. Их приходится потом доделывать тому, кто это обнаружит когда будет заниматься другой задачей, или тому, на кого тестировщик решит завести баг. Столкнувшись с подобными срочными доделками, я начала сама перед разработкой созваниваться с аналитиками и вытягивать из них полную постановку, а потом ещё синхронизироваться с другими разработчиками, которые работают над этой же историей. Но всё это занимает довольно много времени, которого никто дополнительно не даёт.
Пробовала разговаривать со своим лидом, он сказал, что проблему видит, но ссориться с лидом аналитиков, который всегда прикрывает своих, не хочет. Разговор прошел в пустую. С коллегами тоже говорила. Фронтендеры не делают по аналитике, а ждут наш бэк и делают по нему, так что им некритично качество постановки, главное, что макеты дали и бэк готов. Другие бэки согласны, что проблема есть, но похоже ничего делать с этим они не хотят.
Может ты предложишь, что можно сделать?
Прочитала на Хабре интересный анализ рынка труда 2026 года.
Один из основных выводов, который сильно перекликается с моими наблюдениями:
Российский IT‑найм — это несколько гигантов.
1% работодателей публикует 29% всех вакансий. Верхние ~40% компаний закрывают более 80% рынка.
Размер работодателя не связан с уровнем зарплат — гиганты платят не больше остальных.
среди удалённых вакансий 59% живут вне hh против 14% у офисных