>_ Hello World Приветствую!
Меня зовут Алексей. Я Backend разработчик с 6 годами опыта, последние 1.5 года руковожу командой в позиции Team Lead
За это время я побывал в разных ролях — от активного участника и инициатора технических обсуждений и решений до наблюдателя за тем, как формируются и развиваются решения в командах. Эта комбинация наделила меня ценным опытом и пониманием работы процессов.
Когда я только начинал свой путь в IT, меня вдохновляла и мотивировала возможности учиться абсолютно бесплатно. Главное - вовлеченность, горящие глаза и время. Индустрия дала мне все необходимое, чтобы строить свою карьеру: от решений редких кейсов в дальних углах интернета до ответов опытных коллег на StackOverflow и Habr. Буду постепенно выкладывать застоявщиеся мысли, оформлять их в посты и делиться с вами. Рассказывать об опыте построение карьеры в IT. И параллельно с вами учиться новому
На этом канале я буду писать о том, что считаю важным для инженеров и лидеров:
• Архитектура Backend: устойчивость и нагрузка
• Управление командами и процессами
• Развитие хард- и софт-скиллов в IT
• Практические разборы из своей работы
• Использование AI в командах, процессах и продуктах
Канал будет авторским, живым и максимально полезным для всех, кто в IT.
Подписывайтесь - впереди много интересного!
Please open Telegram to view this post
VIEW IN TELEGRAM
❤3⚡2😁2💯2🤯1
Серии постов:
Django Blue Green:
- Blue Green №1
- Blue Green №2
- Blue Green №3
Saga Pattern:
- Saga №1
- Saga №2
- Saga №3
Django Blue Green:
- Blue Green №1
- Blue Green №2
- Blue Green №3
Saga Pattern:
- Saga №1
- Saga №2
- Saga №3
✍1👍1🤔1🤯1
Буду постепенно запускать серии постов, наполняя контентом этот канал и параллельно изучая механизмы навигации между постами в Телеграм. Начну с темы, по которой сейчас веду работы подготовки к докладу Moscow Python MeetUp - Django Blue Green Deployment
В серии постов я хочу рассказать, как мы внедряем BG в большой Django-монолит, какие ограничения упираются в СУБД, какие в процессы, а какие вообще в Django. И о том, как мы постепенно решаем эти проблемы...
• Как всё началось:
Летом 2023 года один релиз прошёл настолько плохо, что мы впервые всерьёз задумались о полноценном механизме отказоустойчивых релизов. Основная боль быстро стала очевидной: сложные изменения схемы БД делают откат болезненным и иногда невозможным, особенно когда релиз сопровождается миграциями.
Мы сравнили канареечные релизы и BG. Для нас критичным было быстрое переключение между окружениями — значит BG.
И сразу столкнулись о главный вопрос:
- Как поддерживать две версии кода с разными моделями в БД, при этом имея всего одну БД?
• Идея:
Мы решили, что схема БД должна временно удовлетворять требованиям _обоих_ окружений. Для этого стандартные Django-команды
Все миграции мы классифицировали на три группы:
- Конструктивные
(
— те, что расширяют схему и безопасны для обеих версий кода.
- Деструктивные
(
— операции, которые ломают совместимость.
- Невозможные для BG
(
— операции, при которых единая схема не может одновременно поддерживать обе версии.
На этапе Blue мы выполняем только конструктивные миграции — так новая версия получает всё, что ей нужно, а старая продолжает работать.
После успешного релиза и наблюдения за стабильностью — запускаем Green, где выполняется деструктивная часть. С этого момента откат на прошлую версию уже рискован, но и не нужен.
В следующих постах я покажу техническую сторону — как мы адаптировали DevOps-процессы, патчили Django и перестраивали миграции под две фазы. Если вам интересна инженерная кухня BG на большом монолите — будет полезно.
Blue-Green деплоймент — это подход, который позволяет выкатывать новую версию сервиса без простоя и риска «положить» прод. Суть проста: одновременно существует два окружения — Blue и Green. Одно обслуживает пользователей, другое — готовится к релизу. В нужный момент трафик переключается между ними.
В серии постов я хочу рассказать, как мы внедряем BG в большой Django-монолит, какие ограничения упираются в СУБД, какие в процессы, а какие вообще в Django. И о том, как мы постепенно решаем эти проблемы...
• Как всё началось:
Летом 2023 года один релиз прошёл настолько плохо, что мы впервые всерьёз задумались о полноценном механизме отказоустойчивых релизов. Основная боль быстро стала очевидной: сложные изменения схемы БД делают откат болезненным и иногда невозможным, особенно когда релиз сопровождается миграциями.
Мы сравнили канареечные релизы и BG. Для нас критичным было быстрое переключение между окружениями — значит BG.
И сразу столкнулись о главный вопрос:
- Как поддерживать две версии кода с разными моделями в БД, при этом имея всего одну БД?
• Идея:
Мы решили, что схема БД должна временно удовлетворять требованиям _обоих_ окружений. Для этого стандартные Django-команды
makemigrations и migrate мы разделили на две фазы — Blue и Green.Все миграции мы классифицировали на три группы:
- Конструктивные
(
CreateModel, AddField, AddConstraint, AddIndex) — те, что расширяют схему и безопасны для обеих версий кода.
- Деструктивные
(
RemoveField, DeleteModel и т.п.) — операции, которые ломают совместимость.
- Невозможные для BG
(
AlterField, критичные AlterTable и т.п.) — операции, при которых единая схема не может одновременно поддерживать обе версии.
На этапе Blue мы выполняем только конструктивные миграции — так новая версия получает всё, что ей нужно, а старая продолжает работать.
После успешного релиза и наблюдения за стабильностью — запускаем Green, где выполняется деструктивная часть. С этого момента откат на прошлую версию уже рискован, но и не нужен.
В следующих постах я покажу техническую сторону — как мы адаптировали DevOps-процессы, патчили Django и перестраивали миграции под две фазы. Если вам интересна инженерная кухня BG на большом монолите — будет полезно.
❤1🤔1🤯1💯1
Всем привет! Начинаю заметки о том, как мы работаем над архитектурными практиками в компании - как принимаем технические решения на уровне компании, и чем чреват локальный подход к разработке. В этом посте — про то, как у нас появились архитектурные комитеты и ADR.
➡️ Зачем это вообще понадобилось
Пока компания росла, а команды формировались - долгое время казалось, что каждая фича локальна и живёт внутри своего проекта. На практике изменения в данных и API тихо расходились между сервисами, и проблемы всё чаще всплывали на стыках.
Самым болезненным симптомом оказалось дублирование доменных сущностей. Каждая продуктовая команда, не осознавая этого, начинала хранить «свою» версию данных — с локальными особенностями и логикой. В итоге любая небольшая фича, требующая интеграции между командами, переставала быть маленькой: сроки растягивались из дней и недель в месяцы.
В какой-то момент стало понятно: проблема не в конкретных решениях, а в отсутствии общего архитектурного контура.
ℹ️ Появление архитектора как точки перелома
Изначально архитектор подключился к компании для взращивания технических лидов. Лиды приходили к нему с болями в проектах, мы разбирали контекст, собирали мини-дорожные карты улучшений и уходили их прорабатывать.
Со временем на эти сессии начали приходить и разработчики — уже с конкретными фичами и архитектурными вопросами. Стало ясно, что обсуждать решения заранее — полезно, но делать это в произвольном формате сложно масштабировать. Нужен был общий, понятный всем шаблон обсуждения.
Так у нас и появился формат ADR.
ℹ️ Что такое ADR у нас
ADR (Architecture Decision Records) — это единый формат, который позволяет быстро погрузить всех участников архитектурного комитета в контекст обсуждаемой проблемыю
Важно: для нас ADR — это не столько документ, сколько процесс согласования архитектурных изменений
Комитет собирается два раза в неделю, встречи длятся 1...3 часа в зависимости от количества тем. Участвуют разработчики, лиды, архитектор, CTO — все, кому интересно обсудить проблему, важно найти решение.
↗️ Почему это оказалось полезным
Появилось одно место, где архитектура предприятия складывается в общую картину, а не остаётся набором локальных решений. Появились общие практики и точка истинности для архитектурных договорённостей.
Но лично для меня самое ценное — это то, что у нас появилось пространство, где мы регулярно и открыто обсуждаем технологии, ограничения, спорим, брейнштормим, экспериментируем на ходу и подсвечиваем друг другу зоны роста. В какой-то момент архитектура перестает быть чьей-то ролью и становится общим процессом - и это, пожалуй, главный признак зрелости.
Позже сделаю пост о том, как появление архитектурных комитетов отразилось на архитектурной зрелости нашего предприятия:
Хороший материал по этой теме
Теги: #ADR
➡️ Зачем это вообще понадобилось
Пока компания росла, а команды формировались - долгое время казалось, что каждая фича локальна и живёт внутри своего проекта. На практике изменения в данных и API тихо расходились между сервисами, и проблемы всё чаще всплывали на стыках.
Самым болезненным симптомом оказалось дублирование доменных сущностей. Каждая продуктовая команда, не осознавая этого, начинала хранить «свою» версию данных — с локальными особенностями и логикой. В итоге любая небольшая фича, требующая интеграции между командами, переставала быть маленькой: сроки растягивались из дней и недель в месяцы.
В какой-то момент стало понятно: проблема не в конкретных решениях, а в отсутствии общего архитектурного контура.
ℹ️ Появление архитектора как точки перелома
Изначально архитектор подключился к компании для взращивания технических лидов. Лиды приходили к нему с болями в проектах, мы разбирали контекст, собирали мини-дорожные карты улучшений и уходили их прорабатывать.
Со временем на эти сессии начали приходить и разработчики — уже с конкретными фичами и архитектурными вопросами. Стало ясно, что обсуждать решения заранее — полезно, но делать это в произвольном формате сложно масштабировать. Нужен был общий, понятный всем шаблон обсуждения.
Так у нас и появился формат ADR.
ℹ️ Что такое ADR у нас
ADR (Architecture Decision Records) — это единый формат, который позволяет быстро погрузить всех участников архитектурного комитета в контекст обсуждаемой проблемыю
Важно: для нас ADR — это не столько документ, сколько процесс согласования архитектурных изменений
• Команда приносит контекст проблемы, варианты решений и ограничения системы
• Вместе обсуждаем риски и влияние на другие сервисы
• Фиксируем контракт изменений и итоговое решение
Комитет собирается два раза в неделю, встречи длятся 1...3 часа в зависимости от количества тем. Участвуют разработчики, лиды, архитектор, CTO — все, кому интересно обсудить проблему, важно найти решение.
↗️ Почему это оказалось полезным
Появилось одно место, где архитектура предприятия складывается в общую картину, а не остаётся набором локальных решений. Появились общие практики и точка истинности для архитектурных договорённостей.
Но лично для меня самое ценное — это то, что у нас появилось пространство, где мы регулярно и открыто обсуждаем технологии, ограничения, спорим, брейнштормим, экспериментируем на ходу и подсвечиваем друг другу зоны роста. В какой-то момент архитектура перестает быть чьей-то ролью и становится общим процессом - и это, пожалуй, главный признак зрелости.
Позже сделаю пост о том, как появление архитектурных комитетов отразилось на архитектурной зрелости нашего предприятия:
Хороший материал по этой теме
Теги: #ADR
❤1🤯1💯1
Всем привет! Продолжаю серию постов про Blue Green Django Deployment
В этом посте хочу разобрать, из каких этапов реально состояла реализация BG - со стороны DevOps и со стороны разработки - и к каким выводам мы начали приходить ещё в процессе внедрения
1️⃣ Инфраструктура
Со стороны DevOps задача сначала выглядела относительно прямолинейно:
Но довольно быстро стало ясно, что application-core - не единственный участник релиза:
Во время деплоя мы также выкатываем набор сервисов, которые:
Поэтому, чтобы разрешить эту проблему пришлось:
В CI/CD это вылилось в раздельные пайплайны.
В итоге релиз-инженер не “деплоит”, а нажимает кнопку вида activate {color} - за которой стоит цепочка из 8–9 job, которые:
С точки зрения эксплуатации - огромный шаг вперёд.
С точки зрения Kubernetes -ничего магического, просто много аккуратной работы.
2️⃣ Django и двухфазные миграции
А вот со стороны разработки всё было сильно менее линейно.
Идея патчинга Django-механизма миграций сначала выглядела как чистый инженерный челлендж:
1. Написать команду, аналог
2. Пропатчить migrate, чтобы он принимал флаги
Технически это означало:
Немного сахара вокруг Django internals, немного monkey-patching’а —
и прототип действительно заработал
ℹ️ Но…
Чем дальше мы углублялись, тем отчётливее становилось несколько вещей:
Формально механизм работал. Фактически — мы начали:
⏸️ И в какой-то момент вопрос перестал быть техническим:
В следующем посте - момент, когда этот вопрос перестал быть философским
и стал причиной отказа от решения, в которое уже было вложено слишком много
В этом посте хочу разобрать, из каких этапов реально состояла реализация BG - со стороны DevOps и со стороны разработки - и к каким выводам мы начали приходить ещё в процессе внедрения
1️⃣ Инфраструктура
Со стороны DevOps задача сначала выглядела относительно прямолинейно:
• два полноценных prod-окружения: Blue и Green
• один ingress и переключение трафика «по кнопке»
• единая СУБД
• возможность деплоить и тестировать неактивное окружение независимо от активного
Но довольно быстро стало ясно, что application-core - не единственный участник релиза:
Во время деплоя мы также выкатываем набор сервисов, которые:
• шарят конфигурацию с монолитом
• имеют собственный lifecycle
• не должны перезапускаться синхронно с core
Поэтому, чтобы разрешить эту проблему пришлось:
• жёстко отделить core от сервисов
• описать их entrypoint.sh и поведение при старте/остановке
• сформировать правило:
- core деплоится отдельно и независимо от всех сервисов
В CI/CD это вылилось в раздельные пайплайны.
В итоге релиз-инженер не “деплоит”, а нажимает кнопку вида activate {color} - за которой стоит цепочка из 8–9 job, которые:
• Корректно завершают "shared resources"
• переключение ingress на новый application-core
• healthcheck
• И только потом: поочерёдный подъём зависимостей
С точки зрения эксплуатации - огромный шаг вперёд.
С точки зрения Kubernetes -ничего магического, просто много аккуратной работы.
2️⃣ Django и двухфазные миграции
А вот со стороны разработки всё было сильно менее линейно.
Идея патчинга Django-механизма миграций сначала выглядела как чистый инженерный челлендж:
1. Написать команду, аналог
makemigrations, которая делит миграции на Blue (конструктивные) и Green (деструктивные)2. Пропатчить migrate, чтобы он принимал флаги
migrate --blue / --green и выполнял отфильтрованный набор миграций Технически это означало:
• повторить большую часть логики makemigrations
• переопределить MigrationWriter → PatchedMigrationWriter
• на этапе записи миграции разделять операции
• добавить две стратегии:
- определение типа операции
- определение конкретной операции (с нюансами)
Немного сахара вокруг Django internals, немного monkey-patching’а —
и прототип действительно заработал
ℹ️ Но…
Чем дальше мы углублялись, тем отчётливее становилось несколько вещей:
• Django migration operations - это не контракт с СУБД, а декларация намерений
• одна и та же операция может транслироваться в SQL по-разному
• порядок операций иногда важнее их формального типа
• зависимости между миграциями начинают жить своей жизнью
Формально механизм работал. Фактически — мы начали:
• усложнять один из самых критичных участков фреймворка
• вводить вторую модель мышления поверх стандартных миграций
• перекладывать сложность с процесса на код
⏸️ И в какой-то момент вопрос перестал быть техническим:
Мы правда делаем систему надёжнее -
или просто строим очень умный костыль,
который однажды станет нашей главной точкой отказа?
В следующем посте - момент, когда этот вопрос перестал быть философским
и стал причиной отказа от решения, в которое уже было вложено слишком много
👍1😁1🤯1
Всем привет!
Этим постом запускаю серию про паттерн Saga - не в теории, а через реальный путь: от сломанной консистентности в распределенной системе до решения, поселившегося в продакшене. И к выводам, которые мы смогли вынести из этого опыта
В этой серии:
ℹ️ Контекст:
В начале было слово, и словом этим было VCard.
Так мы называем наш сервис виртуальной карты, который позволяет гостям пользоваться преимуществами программы лояльности при заказах в ресторанах.
Работает это так:
Пользовательский флоу элементарный.
Системный - нет
➡️ Решение “в лоб”:
Когда мы реализовывали этот флоу, о паттерне Saga мы ещё не знали.
Решение выглядело логично:
Никакой магии,
никакой надёжности,
никакой согласованности 🙂
🧪 Тестирование:
На тестовом стенде всё работало как надо! Иногда всплывали редкие аномалии - примерно один случай из ста.
Мы списывали это на:
Обвиняли что угодно, даже ретроградный Меркурий,
лишь бы не откладывать релиз нового пользовательского сервиса
🛑 Продакшен:
Продакшен очень быстро показал,
насколько сильно мы ошибались.
И за эту ошибку пришлось заплатить кратно
С ростом нагрузки редкие сбои
превратились в поток инцидентов
Стало совершенно ясно:
• это не единичные баги
• и не «особенности окружения»
❗️Это - системная проблема в реализации сервиса
⏯️ В следующий раз я расскажу:
• как мы начали разбирать эти инциденты
• какие закономерности нашли
• и почему в итоге упёрлись в вопрос консистентности
в распределенной системе
Этим постом запускаю серию про паттерн Saga - не в теории, а через реальный путь: от сломанной консистентности в распределенной системе до решения, поселившегося в продакшене. И к выводам, которые мы смогли вынести из этого опыта
В этой серии:
• как я вообще на него наткнулся
• почему «простые» решения перестают работать
• как сохранять консистентность в распределённых системах
ℹ️ Контекст:
В начале было слово, и словом этим было VCard.
Так мы называем наш сервис виртуальной карты, который позволяет гостям пользоваться преимуществами программы лояльности при заказах в ресторанах.
Работает это так:
• пользователь сканирует QR-код на столе
• видит заказ в мобильном приложении
• применяет карту - копит бонусы или списывает уже накопленные
Пользовательский флоу элементарный.
Системный - нет
➡️ Решение “в лоб”:
Когда мы реализовывали этот флоу, о паттерне Saga мы ещё не знали.
Решение выглядело логично:
• холдируем бонусы в CRM
• последовательно выполняем операции на кассе
• фиксируем результат
• при ошибке - откатываем все
Никакой магии,
никакой надёжности,
никакой согласованности 🙂
🧪 Тестирование:
На тестовом стенде всё работало как надо! Иногда всплывали редкие аномалии - примерно один случай из ста.
Мы списывали это на:
• ограниченные ресурсы стейджа
• нестабильность окружения
• тестовые данные
Обвиняли что угодно, даже ретроградный Меркурий,
лишь бы не откладывать релиз нового пользовательского сервиса
🛑 Продакшен:
Продакшен очень быстро показал,
насколько сильно мы ошибались.
И за эту ошибку пришлось заплатить кратно
С ростом нагрузки редкие сбои
превратились в поток инцидентов
Десятки тысяч заказов в день → около сотни недовольных пользователей ежедневно → десятки сходящих с ума специалистов тех.поддержки обрабатывающие обращения клиентов
Стало совершенно ясно:
• это не единичные баги
• и не «особенности окружения»
❗️Это - системная проблема в реализации сервиса
⏯️ В следующий раз я расскажу:
• как мы начали разбирать эти инциденты
• какие закономерности нашли
• и почему в итоге упёрлись в вопрос консистентности
в распределенной системе
❤1😁1🤯1
Продолжение серии статей про паттерн Saga. В прошлый раз мы остановились на том, что после релиза стало понятно - что проблема системная. И что проблема сама по себе никуда не денетеся. Но еще непонятно, в чем собственно проблема
❗️Инциденты повторялись.
❗️Поддержка тонула в обращениях.
❗️Поведение системы выглядело нестабильным и непредсказуемым.
Некоторое время мы пробовали фиксить конкретные баги, но это оказалось лишь лечением симптомов, что в итоге лишь усложяно систему, не решая системных проблем Мы перестали чинить отдельные баги и начали разбираться, что вообще происходит.
🔍 Что мы сделали:
И довольно быстро стало ясно - это не хаос. Все инциденты укладывались в несколько повторяющихся сценариев:
Сценарии разные, последствия разные - но эффект всегда один
❗️Нарушена консинстентность данных в системе
Система регулярно оказывалась в состоянии, когда часть шагов выполнена, а часть - нет
Мы не могли точно ответить:
❓ Ключевой вопрос:
В какой-то момент вопрос стал предельно конкретным:
С этим вопросом я пошёл искать ответы, тогда еще с поисковиком. Были же времена...
▶️ И тут появляется Saga
Через несколько статей я наткнулся на упоминание паттерна Saga, а затем — на его классическое описание на microservices.io
Saga предлагает смотреть на бизнес-операцию не как на одну транзакцию, а как на последовательность локальных шагов с описанием явных компенсирующими действий на каждом этапе
▶️ В следующих постах я расскажу:
• Как мы реализовывали Saga технически
• Почему выбрали режим оркестрации
• С какими граблями столкнулись в продакшене
❗️Инциденты повторялись.
❗️Поддержка тонула в обращениях.
❗️Поведение системы выглядело нестабильным и непредсказуемым.
Некоторое время мы пробовали фиксить конкретные баги, но это оказалось лишь лечением симптомов, что в итоге лишь усложяно систему, не решая системных проблем Мы перестали чинить отдельные баги и начали разбираться, что вообще происходит.
🔍 Что мы сделали:
• усилили логирование
• добавили трейсы
• начали группировать инциденты
И довольно быстро стало ясно - это не хаос. Все инциденты укладывались в несколько повторяющихся сценариев:
• Нарушение идемпотентности при слабой сети
• блокировка заказа операцией на кассе или официантом
• таймауты операций, когда касса нагружена
Сценарии разные, последствия разные - но эффект всегда один
❗️Нарушена консинстентность данных в системе
Система регулярно оказывалась в состоянии, когда часть шагов выполнена, а часть - нет
Мы не могли точно ответить:
• что сейчас происходит с заказом
• какие действия уже применены
• и что именно нужно откатить
❓ Ключевой вопрос:
В какой-то момент вопрос стал предельно конкретным:
Как сохранять консистентность в распределённой системе
без общей транзакции?
С этим вопросом я пошёл искать ответы, тогда еще с поисковиком. Были же времена...
▶️ И тут появляется Saga
Через несколько статей я наткнулся на упоминание паттерна Saga, а затем — на его классическое описание на microservices.io
Saga предлагает смотреть на бизнес-операцию не как на одну транзакцию, а как на последовательность локальных шагов с описанием явных компенсирующими действий на каждом этапе
▶️ В следующих постах я расскажу:
• Как мы реализовывали Saga технически
• Почему выбрали режим оркестрации
• С какими граблями столкнулись в продакшене
microservices.io
Microservices Pattern: Pattern: Saga
Implement transactions using a saga, which is sequence of local transactions
😁1🤔1🤯1🥴1
Заканчиваю серию постов про паттерн Saga. В прошлый раз я остановился на моменте, когда стало понятно, что без управления распределенной транзакцией проблему консистентности не решить.
В этом посте - подробнее о реализации Saga в системе
➡️ Выбор подхода:
Saga можно реализовать через:
• хореографию
• оркестрацию
В режиме оркестрации есть один объект, управляющий запуском этапов в разных системах.
В режиме хореографии системы сами определяют, когда и какой этап запустить.
Мы выбрали оркестрацию
Причины:
При этом сервис масштабируется горизонтально,
и управление шагами передаётся между инстансами через Kafka.
⚜️ Базовая идея реализации:
Главное ограничение - пользовательский флоу нельзя менять.
То есть имплементация Saga в проект должна быть бесшовной
Поэтому архитектура построена так:
Это позволило:
🧩 Типы Saga в системе:
В системе выделены три основных сценария:
• COMMIT - применение карты в заказ или досписание бонусов
• CANCEL - отмена применённой карты
• SUBSCRIBE - изменения заказа, инициированные кассой
Каждый сценарий разбит на атомарные шаги
с компенсирующими действиями: блокировка заказа, выполнение бизнес-условий в CRM и на кассе, разблокировка заказа
🛑 Broken Saga:
Если во время отката что-то пошло не так - Saga помечается как Broken
Дальше:
Это не идеальное решение, но понятное и лёгкое в поддержке.
🚨 Продакшен-грабли:
После релиза система работала стабильно.
Ровно до первого серьёзного пика нагрузки.
Из-за задержек на стороне кассовых демонов:
Проблему пришлось решать экстренно:
🔸 Усиливали демонов
🔸 Переезжали на новые топики
🔸 Принудительно откатывали активные Saga
После переработки флоу обработки Broken Saga эти проблемы нас больше не беспокоили.
✅ Итоги:
Saga не упростила систему. Она сделала её управляемой, но при этом значительно сложнее
Что изменилось:
🔹 Восстановилась консистентность
🔹 Пользовательский флоу стал стабильным
🔹 Поддержка перестала вручную разруливать зависшие списания
Новые баги появились, но теперь они локализованы внутри Saga, а не размазаны по всей системе.
🔄 Промежуточный вывод:
Я однозначно напишу отдельный пост-рефлексию об этом опыте, но, завершая серию, хочу зафиксировать:
В этом посте - подробнее о реализации Saga в системе
➡️ Выбор подхода:
Saga можно реализовать через:
• хореографию
• оркестрацию
В режиме оркестрации есть один объект, управляющий запуском этапов в разных системах.
В режиме хореографии системы сами определяют, когда и какой этап запустить.
Мы выбрали оркестрацию
Причины:
• сложная работа с кассовым оборудованием
• необходимость строгого контроля последовательности шагов
• таймауты, ретраи и явные переходы состояний
При этом сервис масштабируется горизонтально,
и управление шагами передаётся между инстансами через Kafka.
⚜️ Базовая идея реализации:
Главное ограничение - пользовательский флоу нельзя менять.
То есть имплементация Saga в проект должна быть бесшовной
Поэтому архитектура построена так:
• перед стартом Saga сохраняется копия заказа
• каждый шаг изменяет только копию
• основной заказ обновляется только после успешного завершения всех шагов
Это позволило:
• безопасно выполнять откаты
• избежать полусломанных состояний
• понимать текущее состояние транзакции
🧩 Типы Saga в системе:
В системе выделены три основных сценария:
• COMMIT - применение карты в заказ или досписание бонусов
• CANCEL - отмена применённой карты
• SUBSCRIBE - изменения заказа, инициированные кассой
Каждый сценарий разбит на атомарные шаги
с компенсирующими действиями: блокировка заказа, выполнение бизнес-условий в CRM и на кассе, разблокировка заказа
🛑 Broken Saga:
Если во время отката что-то пошло не так - Saga помечается как Broken
Дальше:
• сага уходит в фоновую обработку
• выполняются повторные попытки отката
• при неудаче требуется ручное вмешательство
Это не идеальное решение, но понятное и лёгкое в поддержке.
🚨 Продакшен-грабли:
После релиза система работала стабильно.
Ровно до первого серьёзного пика нагрузки.
Из-за задержек на стороне кассовых демонов:
• шаги перестали укладываться в TTL
• Saga начали падать на этапе блокировки заказа
• для обработки Broken Saga первый шаг всё ещё пытался заблокировать заказ
• задержка на кассе спровоцировала лавинообразное создание саг, приведя систему к отказу
• в отдельных случаях на один заказ создавались сотни Saga
Проблему пришлось решать экстренно:
🔸 Усиливали демонов
🔸 Переезжали на новые топики
🔸 Принудительно откатывали активные Saga
После переработки флоу обработки Broken Saga эти проблемы нас больше не беспокоили.
✅ Итоги:
Saga не упростила систему. Она сделала её управляемой, но при этом значительно сложнее
Что изменилось:
🔹 Восстановилась консистентность
🔹 Пользовательский флоу стал стабильным
🔹 Поддержка перестала вручную разруливать зависшие списания
Новые баги появились, но теперь они локализованы внутри Saga, а не размазаны по всей системе.
🔄 Промежуточный вывод:
Я однозначно напишу отдельный пост-рефлексию об этом опыте, но, завершая серию, хочу зафиксировать:
Saga свою задачу решила - она позволила взять под контроль состояния системы и управлять откатами на разных этапах. Однако цена этого решения оказалась высокой: архитектура усложнилась, требования к поддержке выросли, а жесткий контракт, сформированный уже после выхода в продакшен, заметно ограничил дальнейшее развитие сервиса.
👍3💯3❤2🤯1🥴1
Вот уже третий год подряд в первых числах января я иду покупать себе дневник - датированный ежедневник
Первый дневник, в 2024 году, я купил по простой причине: мысли перестали умещаться в голове. Их было много, структуры не было никакой. Работа, решения, ответственность, постоянный внутренний диалог - всё это не затихало ни на минуту. Мне нужно было место, куда я могу все это выгрузить
Так я и пришёл к своему первому дневнику
Я начал писать от руки - без формата, структуры и ожиданий. Просто выгружать бардак из головы
Уже через несколько месяцев, впервые перечитывая записи, я заметил сразу несколько вещей: у меня сформировалась привычка, появился свой формат, а вместе со структурой в письме - структура начала появляться в моей жизни
⏺️ Дополню: мне действительно нравится свобода своего формата письма. А еще мне нравится мой почерк - когда я не спешу, он доставляет мне почти эстетическое удовольствие. Это, безусловно, сыграло положительную роль для формирования привычки
Я продолжаю работать с дневниками уже третий год не потому, что это «просто полезная привычка», а потому что эффект от неё реально ощущается:
• письмо замедляет и приводит в состояние рефлексии,
• структура в записях постепенно переносится в решения и повседневность
• затих внутренний гомон, остался лишь один - голос, повторяющий... Так, стоп, это не сюда
ℹ️ Но главную ценность ведения дневника для себя я обнаружил в том же, в чем ее обнаруживали еще столетия назад наши предшественники:
Сила дневников в том, чтобы их перечитывать
Ты читаешь записи годовой давности и понимаешь: события ты помнишь, а вот себя в них - совсем нет. Эмоции кажутся чрезмерными, выводы - поспешными, уверенность - наивной.
Но в какой-то момент происходит интересная вещь: когда перечитываешь не факты, а свои эмоции годовалой давности - внутри что-то щелкает
Медитация, среди прочего, учит нас замечать непостоянную природу наших мыслей.
Дневники показывают нам непостоянную природу наших более длительных состояний.
В следующий раз немного расскажу о формате своих ежедневных записей и об отслеживание целей с помощью дневника
Please open Telegram to view this post
VIEW IN TELEGRAM
⚡3🤔1🤯1🥴1
❗️ Конец Blue-Green: мы выбрали процесс вместо костыля
В прошлой части остановился на вопросе: «строим систему надёжнее или умный костыль, который однажды станет нашей главной точкой отказа?»
Сегодня - ответ. И он не тот, который я планировал
➡️ Куда мы пришли технически
К моменту ревью всё почти работало:
DevOps выкатили Blue-Green в продакшен: два окружения, единый ingress, переключение трафика по кнопке
Со стороны Django: пропатченная команда makemigrations, разделение операций на конструктивные и деструктивные, тулинг для двухфазных миграций
На горизонте - переход на дневные релизы. Цель, ради которой всё затевалось
Оставалось пройти код-ревью и описать процесс работы с новой схемой
🛑 Тех.лид пришёл с альтернативой
На ревью пришёл наш тех.лид - назовём его Серёга. И принёс ссылку на статью про паттерн Expand & Contract. По его мнению - вот правильный способ решать совместимость схемы БД. А моя реализация ему «не нравится»
Я уперся рогами. Двухфазные миграции работали, корнер-кейсы отрабатывали - я готов был защищать решение до последнего
🔍 Архитектурный комитет как место для дебатов
На архком мы приходили не раз и не два
Серёга стоял на том, что задача решается процессом. Я - на том, что процесс нужно подкрепить технологией, чтобы переход был безболезненным
Каждый раунд уходили в детали, прорабатывали корнер-кейсы, возвращались с новыми аргументами. Серёга не мог доказать, что моё решение не работает. Я не мог честно объяснить, зачем оно нужно поверх процесса
В фоне всё это время висел один вопрос: «зачем»
💡 Кто потребитель схемы БД
Озарение пришло на одном из таких ADR. Простой вопрос: на кого вообще рассчитан этот механизм совместимости?
Я строил систему, которая поддерживает консистентность для двух потребителей - Blue и Green. То есть для двух версий бэкенда
Но у схемы БД потребителей сильно больше: смежные сервисы, отчёты, аналитика, фоновые задачи, внешние интеграции. Технология даёт контракт ровно для двух клиентов. Для всех - нужны бесконечные деньги и ресурсы, которыми мы, к сожалению, не обладаем
А корректно работающий процесс - покрывает 100% случаев. Не в вакууме, а в реальной жизни. Это не так элегантно, как техническое решение в идеальных условиях. Но несоизмеримо эффективнее
✅ Финал
Я сдался. Признал капитуляцию, отпустил рога и пошёл внедрять процесс
Цель осталась той же - Zero Downtime и дневные релизы. Изменились только средства. Через неделю после последнего архкома мы выкатили первый релиз в дневное время. Без костыля, только на процессе
Эта история длиной в 3.5 года потом легла в основу моего доклада на Moscow Python №107. Финальный тезис серии и доклада совпадает:
А Серёга оказался единственным голосом разума на всю компанию. Я слишком долго не верил, что проблему нельзя решить технически. В итоге пришёл к тому, что можно - но не полностью
В прошлой части остановился на вопросе: «строим систему надёжнее или умный костыль, который однажды станет нашей главной точкой отказа?»
Сегодня - ответ. И он не тот, который я планировал
➡️ Куда мы пришли технически
К моменту ревью всё почти работало:
DevOps выкатили Blue-Green в продакшен: два окружения, единый ingress, переключение трафика по кнопке
activate {color}Со стороны Django: пропатченная команда makemigrations, разделение операций на конструктивные и деструктивные, тулинг для двухфазных миграций
На горизонте - переход на дневные релизы. Цель, ради которой всё затевалось
Оставалось пройти код-ревью и описать процесс работы с новой схемой
🛑 Тех.лид пришёл с альтернативой
На ревью пришёл наш тех.лид - назовём его Серёга. И принёс ссылку на статью про паттерн Expand & Contract. По его мнению - вот правильный способ решать совместимость схемы БД. А моя реализация ему «не нравится»
Я уперся рогами. Двухфазные миграции работали, корнер-кейсы отрабатывали - я готов был защищать решение до последнего
🔍 Архитектурный комитет как место для дебатов
На архком мы приходили не раз и не два
Серёга стоял на том, что задача решается процессом. Я - на том, что процесс нужно подкрепить технологией, чтобы переход был безболезненным
Каждый раунд уходили в детали, прорабатывали корнер-кейсы, возвращались с новыми аргументами. Серёга не мог доказать, что моё решение не работает. Я не мог честно объяснить, зачем оно нужно поверх процесса
В фоне всё это время висел один вопрос: «зачем»
💡 Кто потребитель схемы БД
Озарение пришло на одном из таких ADR. Простой вопрос: на кого вообще рассчитан этот механизм совместимости?
Я строил систему, которая поддерживает консистентность для двух потребителей - Blue и Green. То есть для двух версий бэкенда
Но у схемы БД потребителей сильно больше: смежные сервисы, отчёты, аналитика, фоновые задачи, внешние интеграции. Технология даёт контракт ровно для двух клиентов. Для всех - нужны бесконечные деньги и ресурсы, которыми мы, к сожалению, не обладаем
А корректно работающий процесс - покрывает 100% случаев. Не в вакууме, а в реальной жизни. Это не так элегантно, как техническое решение в идеальных условиях. Но несоизмеримо эффективнее
✅ Финал
Я сдался. Признал капитуляцию, отпустил рога и пошёл внедрять процесс
Цель осталась той же - Zero Downtime и дневные релизы. Изменились только средства. Через неделю после последнего архкома мы выкатили первый релиз в дневное время. Без костыля, только на процессе
Эта история длиной в 3.5 года потом легла в основу моего доклада на Moscow Python №107. Финальный тезис серии и доклада совпадает:
Не пишите костыли, процессы рулят
А Серёга оказался единственным голосом разума на всю компанию. Я слишком долго не верил, что проблему нельзя решить технически. В итоге пришёл к тому, что можно - но не полностью
Prisma's Data Guide
Using the expand and contract pattern | Prisma's Data Guide
In this article, we introduce the expand and contract pattern to help migrate data and clients to a new schema.
👍2😁1🤯1