company_banner

История архитектуры Dodo IS: путь бэкофиса

    Хабр меняет мир. Больше года мы ведём свой блог. Где-то полгода назад нам прилетел вполне логичный фидбэк от хабровчан: «Додо, вот вы везде говорите, что у вас своя система. А что это за система? И зачем она нужна сети пиццерий?».

    Мы посидели, подумали и поняли, что вы правы. Мы пробуем объяснить всё на пальцах, но выходит рваными кусками и нигде нет полноценного описания системы. Так начался долгий путь сбора информации, поиска авторов и написания серии статей про Dodo IS. Погнали!
    Благодарности: спасибо, что делитесь своим фидбэком с нами. Благодаря ему мы наконец описали систему, составили технорадар и скоро выкатим большое описание наших процессов. Без вас так бы и сидели ещё 5 лет.


    Серия статей «Что такое Dodo IS?» расскажет про:

    1. Ранний монолит в Dodo IS (2011-2015 годы). (In progress...)
    2. Путь бэкофиса: раздельные базы и шина. (You are here)
    3. Путь клиентской части: фасад над базой (2016-2017 годы). (In progress...)
    4. История настоящих микросервисов. (2018-2019 годы). (In progress...)
    5. Законченный распил монолита и стабилизация архитектуры. (In progress...)

    Если интересно узнать что-то ещё — пишите в комментариях.

    Мнение по хронологическому описанию от автора
    Я регулярно провожу встречу для новых сотрудников по теме «Архитектура системы». У нас она называется «Intro to Dodo IS Architecture» и является частью процесса онбординга новых разработчиков. Рассказывая в том или ином виде про нашу архитектуру, про её особенности, у меня родился некоторый исторический подход к описанию.

    Традиционно мы смотрим на систему, как на набор компонентов (технических или более высокоуровневых), бизнесовых модулей, взаимодействующих между собой ради достижения какой-либо цели. И если для проектирования такой взгляд оправдан, то для описания и понимания не совсем подходит. Причин тут несколько:

    • Реальность отличается от того, что на бумаге. Не всё из задуманного получается. А нам интересно, как на самом деле всё оказалось и работает.
    • Последовательное изложение информации. По сути можно пройтись хронологически от начала до текущего состояния.
    • От простого к сложному. Не универсально, но в нашем случае именно так. От более простых подходов архитектура переходила к более сложными. Часто через усложнение решались проблемы скорости реализации и стабильности, а также десятки других свойств из списка нефункциональных требований (вот здесь хорошо рассказано про противопоставление сложности остальным требованиям).


    В 2011 году архитектура Dodo IS выглядела так:



    К 2020 году она немного усложнилась и стала такой:



    Как произошла эта эволюция? Зачем нужны разные части системы? Какие архитектурные решения и почему были приняты? Разберёмся в этой серии статей.

    Первые проблемы 2016 года: зачем сервисам выходить из монолита


    Первые статьи из цикла будут про сервисы, которые первыми отделились от монолита. Чтобы ввести вас в контекст, расскажу, какие проблемы были у нас в системе к началу 2016 года, что нам пришлось заниматься разделением сервисов.

    Единая база MySql, в которую писали свои записи все приложения, существовавшие на тот момент в Dodo IS. Следствия были такие:

    • Большая нагрузка (при этом 85% запросов приходилось на чтение).
    • База разрасталась. Из-за этого её стоимость и поддержка становились проблемой.
    • Единая точка отказа. Если одно приложение, пишущее в базу, внезапно начинало делать это активнее, то другие приложения чувствовали это на себе.
    • Неэффективность в хранении и запросах. Часто данные хранились в некоторой структуре, которая была удобна для одних сценариев, но не подходила для других. Индексы ускоряли одни операции, но могли замедлять другие.
    • Часть проблем сняли сделанные наспех кэши и read-реплики на базы (об этом будет отдельная статья), но они лишь позволили выиграть время и принципиально проблему не решали.

    Проблемой было наличие самого монолита. Следствия были такие:

    • Единые и редкие релизы.
    • Сложность в совместной разработке большого числа людей.
    • Невозможность привносить новые технологии, новые фреймворки и библиотеки.

    Проблемы с базой и монолитом много раз описывались, например, в контексте падений в начале 2018 года (Будь как Мунк, или пару слов о техническом долге, День, когда Dodo IS остановилась. Асинхронный сценарий и История о птице Додо из рода Фениксов. Великое падение Dodo IS), так что особо останавливаться не буду. Скажу только, что нам хотелось дать большую гибкость при разработке сервисов. В первую очередь это касалось тех, которые были самыми нагруженными и корневыми во всей системе — Auth и Трекер.

    Путь бэкофиса: раздельные базы и шина


    Навигация по главе

    1. Схема монолита 2016 года
    2. Начинаем разгружать монолит: отделение Auth и Трекера
    3. Чем занимается Auth
    4. Откуда нагрузки?
    5. Разгружаем Auth
    6. Чем занимается Трекер
    7. Откуда нагрузки?
    8. Разгружаем Трекер

    Схема монолита 2016 года


    Перед вами основные блоки монолита Dodo IS 2016 года, а чуть ниже расшифровка их основных задач.

    Касса Доставки. Учёт курьеров, выдача заказов курьерам.
    Контакт Центр. Приём заказов через оператора.
    Site. Наши сайты (dodopizza.ru, dodopizza.co.uk, dodopizza.by и т.д.).
    Auth. Сервис авторизации и аутентификации для бэкофиса.
    Трекер. Трекер заказов на кухне. Сервис отметки статусов готовности при приготовлении заказа.
    Касса Ресторана. Приём заказов в ресторане, интерфейсы кассира.
    Export. Выгрузка отчётов в 1C для бухгалтерии.
    Оповещения и накладные. Голосовые команды на кухне (например, «Поступила новая пицца») + печать накладных для курьеров.
    Менеджер Смены. Интерфейсы для работы менеджера смены: список заказов, графики производительности, вывод на смену сотрудников.
    Менеджер Офиса. Интерфейсы для работы франчайзи и управляющего: приём сотрудников, отчёты по работе пиццерии.
    Табло Ресторана. Отображение меню на телевизорах в пиццериях.
    Админка. Настройки в конкретной пиццерии: меню, цены, учёт, промокоды, акции, баннеры для сайта и т.д.
    Личный Кабинет Сотрудника. Графики работы сотрудников, информация о сотрудниках.
    Табло Мотивации Кухни. Отдельный экран, который висит на кухне и отображает скорость работы пиццамейкеров.
    Communication. Отправка sms и email.
    FileStorage. Собственный сервис для приёма и выдачи статических файлов.

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

    Начинаем разгружать монолит: отделение Auth и Трекера


    Основные сервисы, которые тогда больше других записывали и считывали из базы:

    1. Auth. Сервис авторизации и аутентификации для бэкофиса.
    2. Трекер. Трекер заказов на кухне. Сервис отметки статусов готовности при приготовлении заказа.

    Чем занимается Auth


    Auth — это сервис, через который пользователи логинятся в бэкофис (на клиентской части отдельный независимый вход). Также к нему обращаются в запросе, чтобы удостовериться, что есть нужные права на доступ, и что эти права не изменились с последнего входа. Через него же происходит вход устройств в пиццерии.

    Например, нам хочется открыть на телевизоре, висящем в зале, табло со статусами готовых заказов. Тогда мы открываем auth.dodopizza.ru, выбираем «Вход как устройство», появляется код, который можно внести в специальной странице на компьютере менеджера смены, указав тип устройства (девайса). Телевизор сам перейдёт на нужный интерфейс своей пиццерии и начнёт отображать там имена клиентов, заказы которых готовы.



    Откуда нагрузки?


    Каждый залогиненный пользователь бэкофиса на каждый запрос ходит в базу, в таблицу пользователей, через sql-запрос вытаскивает оттуда пользователя и проверяет, есть ли у него нужные доступы и права на эту страницу.

    Каждое из устройств делает то же самое только с таблицей устройств, проверяя свою роль и свои доступы. Большое количество запросов в мастер-базу приводит к её загрузке и трате ресурсов общей базы на эти операции.

    Разгружаем Auth


    У Auth изолированный домен, то есть данные о пользователях, логинах или устройствах поступают в сервис (пока будущий) и там остаются. Если они кому-то понадобятся, то он пойдёт в этот сервис за данными.

    БЫЛО. Схема работы изначально была такой:



    Хочется немного пояснить, как это работало:

    1. Запрос извне приходит на бэкэнд (там Asp.Net MVC), приносит с собой куку сессии, которая используется для получения сессионных данных из Redis(1). В ней либо есть информация о доступах, и тогда доступ в контроллер открыт (3,4), либо нет.
    2. Если доступа нет, нужно пройти процедуру авторизации. Здесь для упрощения она показана как часть пути в том же атрибуте, хотя это переход на страницу логина. В случае позитивного сценария мы получим правильно заполненную сессию и перейдём в Backoffice Controller.
    3. Если данные есть, то нужно проверить их на актуальность в базе пользователя. Не изменилась ли его роль, не надо ли его не пускать теперь на страницу. В этом случае после получения сессии (1) надо напрямую сходить в базу и проверить доступы пользователя с помощью слоя логики аутентификации (2). Далее либо на логин-страницу, либо переход в контроллер. Такая вот простая система, но при этом не совсем стандартная.
    4. Если все процедуры пройдены, то пропускаем дальше в логике в контроллерах и методах.

    Данные пользователей отделены от всех других данных, они хранятся в отдельной таблице membership, функции из слоя логики AuthService вполне могут стать api-методами. Границы домена определены вполне чётко: пользователи, их роли, данные о доступах, выдача и отзыв доступов. Всё выглядит так, что можно вынести в отдельный сервис.

    СТАЛО. Так и сделали:



    У такого подхода есть ряд проблем. Например, вызов метода внутри процесса — не то же самое, что вызов по http внешнего сервиса. Латенси, надёжность, поддерживаемость, прозрачность операции совершенно другие. Подробнее именно о таких проблемах рассказывал Андрей Моревский в своем докладе «50 оттенков микросервисов».

    Сервис аутентификации и с ним сервис устройств используются для бэкофиса, то есть для сервисов и интерфейсов, используемых на производстве. Аутентификация для клиентских сервисов (вроде сайта или мобильного приложения) происходит отдельно без использования Auth. Отделение заняло около года, а сейчас мы опять занимаемся этой темой, переводя систему уже на новые сервисы аутентификации (со стандартными протоколами).

    Почему отделение длилось так долго?
    По пути было множество проблем, которые замедляли:

    1. Нам хотелось перевести данные о пользователях, устройствах и аутентификации из баз по стране в одну. Для этого пришлось переводить все таблицы и использование с идентификатора int на глобальный идентификатор UUId (недавно перерабатывали этот код Роман Букин «Uuid — большая история маленькой структуры» и open-source проект Primitives). Хранение данных по пользователям (так как это персональная информация) имеет свои ограничения и для некоторых стран надо хранить их отдельно. Но глобальный идентификатор пользователя должен быть.
    2. Много таблиц в базе имеет аудит информацию о том пользователе, который совершил операцию. Это потребовало дополнительного механизма, чтобы была консистентность.
    3. После создания api-сервисов был долгий и постепенный период перевода на другую систему. Переключения должны были происходить бесшовно для пользователей и требовали ручной работы.


    Схема регистрации устройства в пиццерии:



    Общая архитектура после выделения Auth и Devices-сервиса:



    Примечание. На 2020 год мы работаем над новой версией Auth, который основан на стандарте авторизации OAuth 2.0. Этот стандарт довольно сложный, но пригодится для разработки сервиса сквозной аутентификации. В статье «Тонкости авторизации: обзор технологии OAuth 2.0» мы Алексей Черняев постарался рассказать о стандарте максимально просто и понятно, чтобы вы сэкономили время на его изучение.

    Чем занимается Трекер


    Теперь про второй из нагруженных сервисов. Трекер выполняет двойственную роль:

    • С одной стороны, его задача — показывать сотрудникам на кухне, какие заказы сейчас в работе, какие продукты сейчас нужно готовить.
    • С другой стороны — оцифровывать все процессы на кухне.



    Когда в заказе появляется новый продукт (например, пицца), он попадает на станцию трекера «Раскатка». На этой станции стоит пиццамейкер, который берёт плюшку нужного размера и раскатывает её, после чего отмечает на планшете трекера, что выполнил свою задачу и передаёт раскатанную основу теста на следующую станцию — «Начинение».

    Там следующий пиццамейкер начиняет пиццу, затем отмечает на планшете, что выполнил свою задачу и ставит пиццу в печь (это тоже отдельная станция, которую нужно отметить на планшете). Такая система была с самого начала в Додо и самого начала существования Dodo IS. Она позволяет полностью отслеживать и оцифровывать все операции. Кроме того трекер подсказывает, как готовить тот или иной продукт, проводит каждый вид продукта по своим схемам изготовления, хранит оптимальное время приготовления продукта и трекает все операции над продуктом.

    Так выглядит экран планшета на станции трекера «Раскатка»

    Откуда нагрузки?


    В каждой из пиццерий примерно по пять планшетов с трекером. В 2016 году у нас было больше 100 пиццерий (а сейчас более 600). Каждый из планшетов делает раз в 10 секунд запрос на бэкэнд и выгребает данные из таблицы заказа (связка с клиентом и адресом), состава заказа (связка с продуктом и указание количества), таблицы учёта мотивации (в ней трекается время нажатия). Когда пиццамейкер нажимает на продукт на трекере, происходит обновление записей во всех этих таблицах. Таблица заказа общая, в неё же одновременно идут вставки при принятии заказа, обновления от других частей системы и многочисленные считывания, например, на телевизоре, который висит в пиццерии и показывает готовые заказы клиентам.

    В период борьбы с нагрузками, когда всё и вся кэшировалось и переводилось на асинхронную реплику базы, эти операции с трекером продолжили ходить в мастер-базу. Тут не должно быть никакого отставания, данные должны быть актуальными, рассинхрон недопустим.

    Также отсутствие собственных таблиц и индексов на них не позволяло написать более специфичные запросы, заточенные под своё использование. Для примера, трекеру может быть эффективно иметь индекс на пиццерию на таблице заказов. Мы всегда выгребаем из базы трекера заказы по пиццерии. При этом для приёма заказа не так важно, в какую пиццерию он падает, важнее, какой клиент сделал этот заказ. А значит там нужен индекс по клиенту. Ещё для трекера в таблице заказа не обязательно хранить id напечатанного чека или связанные с заказом бонусные акции. Эта информация наш сервис трекера не интересует. В общей монолитной базе таблицы могли быть только компромиссным вариантом между всеми пользователями. Это было одной из изначальных проблем.

    БЫЛО. Изначально архитектура была такая:



    Даже после выделения в отдельные процессы большая часть кодовой базы оставалась общей для разных сервисов. Всё, что ниже контроллеров, было единым и жило в одном репозитории. Использовались общие методы сервисов, репозиториев, общая база, в которой лежали общие таблицы.

    Разгружаем Трекер


    Главная проблема с трекером в том, что данные должны синхронизироваться между различными базами. Это же и главное его отличие от разделения Auth-сервиса, заказ и его статус могут изменяться и должны отображаться в различных сервисах.

    Мы принимаем заказ на Кассе Ресторана (это сервис), он сохраняется в базе в статусе «Принят». После этого он должен попасть на трекер, где ещё несколько раз изменит свой статус: от «Кухня» до «Упакован». При этом с заказом могут происходить какие-то внешние воздействия от Кассы или интерфейса Менеджера смены. Приведу в таблице статусы заказа с их описанием:


    Схема изменения статусов заказа выглядит так:



    Статусы меняются между разными системами. И здесь трекер не является конечной системой, в которой замыкаются данные. Мы видели несколько возможных подходов для разделения в таком случае:

    1. Концентрируем все действия заказа в одном сервисе. В нашем случае этот вариант требует слишком большого сервиса по работе с заказом. Если бы мы остановились на нём, то получился бы второй монолит. Проблемы бы мы не решили.
    2. Одна система делает вызов в другую. Второй вариант уже интереснее. Но при нём возможны цепочки вызовов (каскадные сбои), связность компонентов выше, управлять этим сложнее.
    3. Организуем события, и каждый сервис обменивается с другим через эти события. В итоге был выбран именно третий вариант, по которому все сервисы начинают обмениваться событиями друг с другом.

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

    К тому времени у нас в стеке уже был RabbitMQ, отсюда и итоговое решение использовать его как брокер сообщений. На схеме показан переход заказа от Кассы Ресторана через Трекер, где он меняет свои статусы и отображение его на интерфейсе Заказы менеджера. СТАЛО:



    Путь заказа по шагам
    Путь заказа начинается на одном из сервисов источников заказа. Здесь это Касса Ресторана:

    1. На Кассе полностью готов заказ, и его пора отправить на трекер. Бросается событие, на которое подписан трекер.
    2. Трекер, принимая себе заказ, сохраняет его в свою собственную базу, делая при этом событие «ЗаказПринятТрекером» и посылая его в RMQ.
    3. В шине событий на заказ уже подписаны несколько обработчиков. Для нас важен тот, который делает синхронизацию с монолитной базой.
    4. Обработчик принимает событие, выбирает из него значимые для него данные: в нашем случае это статус заказа «ПринятТрекером» и обновляет свою сущность заказа в основной базе.


    Если кому-то нужен заказ именно из монолитной таблицы orders, то можно считать его и оттуда. Например, такое нужно интерфейсу Заказы в Менеджере Смены:



    Все другие сервисы тоже могут подписаться на события заказа из трекера, чтобы использовать их для себя.

    Если через некоторое время заказ берётся в работу, то его статус сначала изменяется в своей базе (базе Трекера), а потом сразу генерируется событие «ЗаказВРаботе». Оно также попадает в RMQ, откуда синхронизируется в монолитной базе и доставляется другим сервисам. На этом пути могут быть разные проблемы, подробнее о них можно посмотреть в докладе Жени Пешкова про детали реализации Eventual Consistency в Трекере.

    Итоговая архитектура после изменений в Auth и Трекере





    Подводя промежуточный итог: изначально у меня была мысль упаковать девятилетнюю историю системы Dodo IS в одну статью. Хотелось быстро и просто рассказать об этапах эволюции. Однако сев за материал, я понял, что всё гораздо сложнее и интереснее, чем кажется.

    Размышляя над пользой (или её отсутствием) от такого материала, я пришёл к выводу, что непрерывное развитие невозможно без полноценных летописей событий, развёрнутых ретроспектив и анализа своих прошлых решений.

    Надеюсь, что вам было полезно и интересно узнать про наш путь. Сейчас я стою перед выбором, какую часть системы Dodo IS описать в следующей статье: пишите в комментариях или голосуйте.

    Only registered users can participate in poll. Log in, please.

    О какой части Dodo IS вам бы хотелось узнать в следующей статье?

    • 24.6%Ранний монолит в Dodo IS (2011-2015 годы)14
    • 24.6%Первые проблемы и их решения (2015-2016 годы)14
    • 19.3%Путь клиентской части: фасад над базой (2016-2017 годы)11
    • 36.8%История настоящих микросервисов (2018-2019 годы)21
    • 45.6%Законченный распил монолита и стабилизация архитектуры26
    • 29.8%Про дальнейшие планы развития системы17
    • 19.3%Не хочу ничего знать про Dodo IS11
    Dodo Engineering
    О том, как разработчики строят IT в Dodo

    Similar posts

    Comments 35

      +3
      По-моему это объяснение необходимости микросервисов какое-то странное. В плане трекера вы получили бы абсолютно то же быстродействие просто используя кэш в оперативке. 5 тысяч планшетов делают обновление раз в 10 секунд, то есть 500 запросов в секунду. Это же ни о чём. Увеличив количество планшетов в 10 раз получите 5000 запросов в секунду всего-то (ну то есть с огромным запасом). Просто банальная выборка из ОЧЕНЬ маленькой базы Редиса или чего-то ещё. Где тут какие-то нагрузки вы нашли? По-моему вы или велосипед ради велосипеда написали, либо причины всё-таки другие.
        +2
        Тоже очень удивился такой мотивации. И переход на микросервисы кажется надуманным. Типа, «чтобы было, потому что сейчас так модно». Еще всегда задавался вопросом: «Зачем сети пиццерий команда в почти полторы сотни разрабов?!».
          +1
          В подавляющем большинстве случаев истинная причина перехода на микросервисы — потому что модно.
            +3
            В плане трекера вы получили бы абсолютно то же быстродействие просто используя кэш в оперативке

            Но трекер нельзя кэшировать, ему нужны самые оперативные данные

            Просто банальная выборка из ОЧЕНЬ маленькой базы Редиса

            Редис подходит только для кэша, который вы не боитесь потерять — он не гарантирует сохранность всех данных, которые содержит. Пиццерии нельзя терять заказы и продукты в заказах. Да и в сам Редис данные должны сначала попасть :-)
              0
              «Но трекер нельзя кэшировать, ему нужны самые оперативные данные» конечно можно и нужно. Никто не предлагает делать кэш на минуту.

              «Редис подходит только для кэша, который вы не боитесь потерять — он не гарантирует сохранность всех данных, которые содержит. Пиццерии нельзя терять заказы и продукты в заказах. Да и в сам Редис данные должны сначала попасть :-)»
              и в чём проблема? Мастер-данные храните где-угодно, по мере изменения данных делаете кэши протухшими и обновляете данные.
                +3

                Кэш, даже, допустим, в 5 сек. + обновление раз в 10 сек может привести к тому, что продукт не выйдет на нужный трекер в течение 20 сек. Умножаем на кол-во планшетов в пиццерии и получаем 100 сек промедления только на стороне Dodo IS. На кухне за это время можно приготовить парочку пицц. А для пользователя это будет выглядеть как "пицца не протыкивается" — не переходит с одного планшета на другой. Кроме того, если заказ отменяют, кухне надо знать об этом максимально быстро, ччтобы лишнего не наготовить.
                В комментарии ниже автор статьи говорит о том, что инвалидация кэша была нетривиальной задачей. Без разматывания клубка тут как-то сложновато.
                Кэш в памяти в распределенной системе без инвалидации — отсутствие консистентности. На одной машине кэш обновился, на другой еще нет, в итоге получаем, что пицца на планшете скачет — то есть, то нет. Это очень критичная проблема для кухни.
                Использование рэдиса в качестве распределенного кэша приводит к проблемам производительности. Если рэдис заэвиктит еще актуальную запись (а он это делает регулярно), снова начинаются скачки, но теперь уже лейтенси.
                Но рэдис тут больше по надежности не подходит.

              +4
              Саша, привет!
              Проблемы, конечно, не в одном трекере — с него начали т.к. он один из самых критичных для бизнеса. Будучи выделенным в полноценный микросервис он прекрасно держит нагрузки.

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

              Например, сложности возникают в тот момент когда два десятка сервисов (в статье упомянуты только самые крупные) ходят в одну и ту же неоптимальную для каждого из них базу и она становится единой точкой отказа. При этом в ней появляются огромные таблицы типа списка заказов с кучей полей, бОльшая часть из которых нужна лишь какому-то небольшому количеству клиентов.
              И кеши тут уже не сильно помогут, особенно когда критична скорость обновления и консистентность данных: в идеале они должны прилететь на соответствующий планшет до того как пиццамейкер физически передаст раскатанное тесто со станции раскатки на станцию начинения.
              Отдельная беда — когда какой-нибудь из сервисов неудачным запросом лочит лишние строки в этой гигантской таблице с заказами: вся система начинает складываться как карточный домик.

              Кстати, от Редиса, который у нас используется повсеместно (на картинках в статье далеко не все уместилось), мы в последнее время отказываемся, т.к. с ним внезапно всплывает много проблем: SLA в Ажуре всего 99.9, высокая стоимость, плюс куча способов выстрелить себе в ногу, чем регулярно пользуются разработчики (например случайно засовывая туда большие объекты в перемешку с мелкими).

              Ну и не забывай что это все создавалось не в 2020м году крупной международной компанией, а в 2011м в режиме стартапа, когда было важно не держать 5к RPS, а быстро реализовать критичные для выживания бизнеса фичи)
                0
                О, привет! Я ж не против микросервисов, я против такого объяснения их необходимости) в целом-то да, монолит зло) Я более чем за похожую событийку, просто кмк именно решение сходу — закэшировать всё с минимальным временем протухания и параллельно прекратить наращивать большие таблицы, а потом уже устраивать такую ревизию.
                  +1
                  Так примерно и произошло — сначала обмазали все кешами и выделили read-реплики базы, оптимизировали что можно, а потом пошли пилить монолит)
                +3
                Наверное не стоило начинать не с начала, а где-то с середины. Отсюда тема проблем не совсем раскрыта, согласен. Я постараюсь в следующих статьях эту тему объяснить более подробно.
                Пока вот примерно так:
                1. Сначала была именно база редиса, она была маленькая и оттуда весь трекер считывал. Потом по надежности редис нам не подошел. Отказались от него в пользу реляционной базы.
                2. Кэш в памяти есть и используется для редко меняющихся данных, типа справочников. Данные по состоянию заказа должны быть максимально актуальны. Еще до выделения отдельной базы мы проводили работы по созданию кэшей и по выделению реплик(они были на рисунках). Это сильно помогло, но данные трекера по статусам заказа нельзя было перевести на на кэш, не обеспечив его инвалидацию. А это не тривиальная задача. При этом у нас балансировка идет на N серверов, никаких sticky session нет и не хотелось делать
                  0
                  Вы сомневаетесь что современные веб разработчики могут медленными сделать всего 500 rps? :-D
                  0
                  Вы довольны текущей архитектурой? Что хочется изменить или добавить?
                    +3
                    В целом, отдельные хранилища и синхронизация через RMQ вполне хорошо работает. Все плюсы не буду расписывать, напишу только минусы:
                    1. Работать разработчикам с этим стало сложнее. И дело не в понимании общих концепций, в в том, что все эндпойнты должны быть коммутативны и идемпотентны. Это трубует хорошей проработки взаимосвязи событий между собой. Удлинняет разработку, приводит в сложным ошибкам
                    2. Избыточность хранения данных. Да, тот же Заказ хранится в нескольких базах и от этого он независим и удобен в использовании по месту. Но как только надо сделать новый сервис сразу встает вопрос про хранение этого Заказа в нем. А есть мелкие сервисы, которые даже не модифицируют особо Заказ, а просто должны его корректно отобразить, обогатив теми или иными данными. Это приводит к созданию хранилищ, в дополнительным тратам времени и денег на них.
                    3. Локальные проблемы: использование RMQ не позволит масштабироваться по количеству подписчиков и паблишеров. При росте этого количества, надо будет больше мощности. Redis в Azure оказался проблемным и мы его теперь не используем. Это скорее конкретные сервисы и тулы, не особо влияющие на архитектуру
                      +4
                      Прекрасно написано.

                      Полностью согласен. Вопрос один, а стоит ли овчинка выделки?

                      Ваши проблемы были и есть не в монолите, как таковом, а в его качестве архитектуры и реализации.

                      Например, сразу что приходит на ум по статье. Зачем в БД хранить все поля заказа в одной таблице? Что мешает разделить их по таблицам и джойнить в нужных сервисах нужные таблицы?

                      Второй момент состоит в том, что в случае, когда запросов на чтение существенно больше запросов на запись, большинство проблем с БД решаются увеличением RAM на СУБД. Делается партишенинг, нужный партишен должен быть полностью в оперативной памяти. В любом случае, 500 запросов в секунды не есть проблема для любой БД, даже при использовании EF какого-нибудь.

                      Как вы написали, любое усложнение архитектуры увеличивает требования к разработчикам и создают проблемы гораздо более высокого уровня, часть из которых решить надежно вообще сложно. Если у вас с десяток локальных хранилищ заказов, то нужно их синхронизировать. Чтобы их синхронизировать, нужны очереди. Очереди должны быть отказоустойчивыми с гарантией доставки. Т.е. вместо того, чтобы оптимизировать структуру данных в БД и переписать говнокод, вы создали кучу новых проблем, добавив в систему новых элементов, каждый из которых может отказать, которые нужно админить и с которыми также нужно уметь работать.

                      Ну и напоследок. Я бы для небольшой компании размещаться в облаке не стал. Мой опыт говорит, что физический инстанс БД работает минимум в 3-4 раза быстрее и есть возможность сконфигурить его под себя, например, добив оперативы до 768ГБ или поставив быстрые SSD. И да, обслуживание железных серверов будет дешевле, чем специалисты по Azure.
                        +3
                        Вопрос один, а стоит ли овчинка выделки?

                        Да. При продуманной реализации такой распределенной архитектуры(а она оказалась по факту продуманной), проблемы нагрузки на базу ушли. Разработка между разными сервисами стала сложнее, но разработка внутри сервиса скорее упростилась и даже ускорилась.

                        Ваши проблемы были и есть не в монолите, как таковом, а в его качестве архитектуры и реализации.

                        Для меня они были и там и там. Монолит(а скорее единая база) сам по себе стопорил и не позволял развиваться.
                        Например, использование одной большой базы приводит к сложностям в обслуживании. Недавно мы перетаскивали монолитную базу в полностью managed mysql(т.е. как saas, вместо iaas) и это заняло более 6 месяцев, причем большая часть работы была по устранению различных плохо написанных и старых вещей. Когда их писали, причем, они вполне могли не быть плохо написанными.
                        Еще одна большая база приводила к тому, что вертикально ее масштабировать мы в какой-то момент просто не могли. Стоимость сервера была слишком большая для нас.
                        Сейчас монолитная база, которая остается, тоже вызывает проблемы. Происходят вынужденные даунтаймы системы из-за обслуживания при рестартах. При отдельных хранилищах у сервисов локальные даунтаймы не сказываются на работе всей системы.

                        Зачем в БД хранить все поля заказа в одной таблице? Что мешает разделить их по таблицам и джойнить в нужных сервисах нужные таблицы?

                        Дополнительные расходы на создание заказа. При чтение придется делать джойны, которые замедлят риды. Всем не угодишь и кому-то удобна одна структура, кому-то другая. На самом деле там уже были дополнительные таблицы, в которых раскладывалась часть информации по заказу(не упоминал об этом, чтобы не усложнять), получалось много джойнов, а с ними и запросы, требующие вычислительных ресурсов.

                        большинство проблем с БД решаются увеличением RAM на СУБД. Делается партишенинг, нужный партишен должен быть полностью в оперативной памяти

                        Уперлись в размеры базы. В какой-то момент стоимость следующего по мощности сервера была бы выше месячных трат на разработку в IT. Партишенинг пробовали, как раз по заказу. В MySql он не дал ожидаемого эффекта. Производительность базы не изменилась. Тогда мы сидели на 5.6, может сейчас что-то лучше стало, не могу сказать. ForeighKey тоже не использовали.
                        EF не было, писали запросы сами на sql и маппили их через простые мапперы.

                        Если у вас с десяток локальных хранилищ заказов, то нужно их синхронизировать. Чтобы их синхронизировать, нужны очереди. Очереди должны быть отказоустойчивыми с гарантией доставки

                        Да, в итоге так и сделали. Не сразу догадались до этого, но сейчас эта система работает надежно. Потребовала использования паттерна локальных очередей. Т.е. перед отправкой события мы его сохраняем в таблиц local_queue, а если в rmq событие не отправилось, то у нас оно прикопано в своей базе. В rmq же очереди с durable все. Есть еще механика redelivery, если подписчик не готов принять событие. Когда это все сделали, то поняли, что какая-нибудь Кафка для такого подходил лучше. Ну чтож, пока живем на RMQ.

                        Напоследок про облака. Находясь там мы резко снижаем проблему «нужно админить», поэтому дальше только в облаке, никакого своего железа не планируем делать.
                          0
                          Не хочется спорить не зная технических подробностей, поэтому очень бы хотелось услышать какие-то количественные подробности.

                          Лично мой опыт говорит, что до 2-3k RPS держит любое более-менее правильно написанное решение. Лично с MySql не работал, но MS Sql, Oracle или Postgres справляются.

                          Более того, масштабировать, особенно с учетом, что у Додо франшизы и облако вообще легко — крупные тинанты крутятся в своем спейсе отдельно. Данные за прошедшие периоды лишь реплицируются в основную БД.

                          Но даже если это и не так, то я лично работал на проекте, где в БД было больше 10 млрд. записей, и он работал. А мне кажется, что вряд ли у вас в день бывает миллион заказов?

                          Поэтому мне все-таки кажется, что проблема не в монолите была и есть. Может быть проблема в MsSql, может быть в кривой архитектуре, но как ни крути, параметры вашей системы ни разу не говорят о высокой нагрузке.

                          Ну и напоследок. Я не знаю какие у вас представления о своем железе. Обслуживание десятка серверов, опять таки из практического опыта вам обойдется в 60 тыс рублей в месяц. Аренда сервера вида 2 × Intel Xeon Gold 6240R 2.4ГГц, 48 ядер 384 ГБ RAM 60 тыс. рублей. Azure MySql 32 ядра 320 Гб будет стоить под 200 тысяч, при этом эти 32 ядра можно смело делить на 3. Да, геморроя может и побольше, но экономия на лицо и производительность в разы выше.
                          +1
                          Здравствуйте!
                          Приятно снова читать комментарии матерого архитектора! (если что, ни разу не сарказм)

                          Зачем в БД хранить все поля заказа в одной таблице? Что мешает разделить их по таблицам и джойнить в нужных сервисах нужные таблицы?

                          — объем кода, завязанного на эти поля. чтобы свести таблицу до рабочего состояния надо переписать пол системы. и это при том, что большая часть разработчиков продолжает писать в ней код с бизнес-фичами. Если всех бросить на переписывание, бизнес взвоет — он не может себе позволить не делать новых фич так долго.
                          — Был определенный технический дедлайн, особенно на выделение трекера — к лету 2017 база перестала бы справляться, а скейлить вверх машины под ней было уже некуда. Оценка выделения сервиса из монолита более предсказуема и надежна, чем оценка рефакторинга того же монолита, поэтому сделали, что успели.
                          — это же не спасет от тяжелых операций — придется джойнить все ту же гигантскую таблицу с заказами на такие же по размеру таблицы с другими полями. Тут надо скорее паттерн использования полей менять, а это значит, что надо проектировать всю систему или значительную ее часть заново.
                          — отказоустойчивость системы — очень плохо когда слишком жирный отчет (для этого, конечно, есть АХД) или ошибка в коде какого-нибудь экрана выдачи заказов кладут мастер базу и перестает работать вся система. Если же экран выдачи заказов выделен в отдельный сервис, то его баги влияют только на него и позволяют пиццерии и дальше обрабатывать заказы. Маленькие сервисы и откатывать проще в случае проблем. Большой монолит деплоится и откатывается сильно дольше.

                          Очереди должны быть отказоустойчивыми с гарантией доставки.

                          Да, и это быстро стало проблемой, но она была довольно быстро решена и все новые сервисы сразу закладывают и гарантию доставки, и коммутативность событий, и еще несколько важных пунктов.

                          вместо того, чтобы оптимизировать структуру данных в БД и переписать говнокод, вы создали кучу новых проблем

                          Новые проблемы появлялись постепенно, не влияли или сильно меньше влияли на здоровье мастер-базы, их было проще исправить, чем старые проблемы, потому что код был локализован и более качественно написан.

                          и с которыми также нужно уметь работать.

                          Вот тут закрался главный сюрприз для нас после начала массового отделения сервисов. На продакшене мы особых проблем не испытываем, а вот тестовые стенды для разработчиков получили удар откуда не ждали — необходимость синхронизировать локальные хранилища привела к тому, что стенды стали разваливаться, а команды, работавшие с ними, не понимали, почему, и что с этим делать. Сейчас мы лучше умеем с этим работать, но определенно еще есть куда стремиться.

                          По остальным пунктам, считаю, что мне не хватает экспертизы, чтобы поддержать дискуссию. Извините :-)
                            0
                            Оценка выделения сервиса из монолита более предсказуема и надежна, чем оценка рефакторинга того же монолита, поэтому сделали, что успели.

                            Почему? Разве это не то же самое + накладные расходы на то, что с сервисом надо учитывать все особенности сетевого взаимодействия?

                              0
                              Когда у тебя есть понятный и, главное, сильно ограниченный бизнесовый домен, ты один раз его задизайнил и тебе понятно, что делать, где могут быть подводные камни, тебе не нужно разбираться в чужом коде, в чужой логике мышления. У тебя есть ожидаемое бизнесом поведение и места стыковки с остальной системой. Остается реализовать план. На пути реализации тоже бывают сложности, конечно, но в них ты редко зависишь от внешних акторов.
                              Когда у тебя огромный комок сильно связанного кода написанного на основе разных подходов к проектированию и разработке, в котором никто не знает всех нюансов, то очень трудно предсказать, где что отвалится, когда ты потянешь за ниточку, о каких бизнес-процессах ты еще не знаешь, и сколько времени тебе понадобится, чтобы разобраться во всей паутине.
                                +1

                                А где тут "сетевое взаимодействие"? Этот отдельный домен может быть модулем, а не сервисом. Если никто не знает всех нюансов, то как они потом переключат использование на сервис — там будут абсолютно те же самые проблемы что и в случае использование отдельного модуля + дополнительные проблемы связанные с сетевым взаимодействием.


                                В случае рефакторинга хотя бы есть инструменты позволяющие делать его автоматизировано и возможность не ломать существующие тесты.


                                Попробуйте в уме разделить проблемы на логические и технические: выделение абстракции внутри кода и обеспечения того, чтобы эта абстракция была реализована именно в виде отдельного сервиса (не библиотеки, модуля, класса или чего-там-есть в вашем любимом ЯП).


                                Логические проблемы будут примерно такие же, а технических больше при выделении в отдельный сервис.

                                  0
                                  А где тут «сетевое взаимодействие»

                                  Это места стыковки с остальной системой. Само по себе взаимодействие клиента с сервером не изменилось, изменился путь данных от бекэнда трекера до других частей системы. И тут было много нового, но не критично.


                                  Логические проблемы будут примерно такие же

                                  Тут не соглашусь. Чтобы выделить сервис, вовсе не обязательно выковыривать код его бизнес-логики из старой системы. Нам хорошо известны сценарии работы трекера, поэтому его просто написали с нуля. Т.е. тут мы с легаси кодом поступили как с черным ящиком — нам не нужно знать, как он устроен внутри, чтобы понять, какой результат получится при определенных действиях. Подсматривали, конечно, но не занимались копи-пастой.

                                    +1
                                    Это места стыковки с остальной системой. Само по себе взаимодействие клиента с сервером не изменилось.

                                    То есть при выделении сервиса никаких новых сетевых взаимодействий не возникло?


                                    Чтобы выделить сервис, вовсе не обязательно выковыривать код его бизнес-логики из старой системы.

                                    Попробуйте заменить в этих рассуждениях "сервис" на "модуль" или "сборку" — получится то же самое, нет?


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


                                    Но логически можно выделить отедльный модуль и без выделения в сервис. Кроме, того, что сетевое взаимодействие накладывает дополнительные ограничения на связанность (например, в DDD есть правило, что нельзя делать ссылки внутрь агрегатов, но, насколько я знаю, нет способа проверять это правило. В случае сервисов не получится его не соблюдать так как нельзя делать ссылок внутрь сервиса)


                                    Я пытаюсь понять, что для вас было главным — технические проблемы или логические.


                                    Где-то у Фаулера я видел рекомендации сначала, выделять отдельные модули, потом, отделять хранилища (условно, чтобы модули взаимодействовали внутри с ограничениями характерными для микросервисов, но in-process), потом, выделять микросервисы.

                              0
                              объем кода, завязанного на эти поля. чтобы свести таблицу до рабочего состояния надо переписать пол системы. и это при том, что большая часть разработчиков продолжает писать в ней код с бизнес-фичами. Если всех бросить на переписывание, бизнес взвоет — он не может себе позволить не делать новых фич так долго.


                              Опять таки возвращаемся в архитектуру. В нормальной монолитной архитектуре не должно быть так, что разделяя таблицу на подтаблицы нужно переписывать половину системы. Все что дальше Data Access Layer понятия не должно знать, что и где и как хранится.

                              Оценка выделения сервиса из монолита более предсказуема и надежна, чем оценка рефакторинга того же монолита, поэтому сделали, что успели

                              Мой 15 летний опыт работы говорит о том, что так думают только оптимисты.

                              это же не спасет от тяжелых операций — придется джойнить все ту же гигантскую таблицу с заказами на такие же по размеру таблицы с другими полями. Тут надо скорее паттерн использования полей менять, а это значит, что надо проектировать всю систему или значительную ее часть заново.


                              Если вам нужно джойнить миллиард записей, то да, не спасет. Однако мой опыт говорит о том, что обычно используют фильтры, ограничивая выборку, например никому не вперлись заказы за январь 2012 года, а вот заказы за вчера интересны. Эта ограниченная выборка может лежать хоть в мат. вьюхе.

                              очень плохо когда слишком жирный отчет (для этого, конечно, есть АХД) или ошибка в коде какого-нибудь экрана выдачи заказов кладут мастер базу и перестает работать вся система.

                              Тут также вопросы. Отчеты никогда не должны строится извините за расизм с мастер-реплики. Также вообще непонятно, как ошибка в коде бизнес-логики влияет на падение базы. Я вижу вариант только дедлоков, но откуда у вас гонки за ресурсами в простой как апельсин ERP?
                                0
                                Опять таки возвращаемся в архитектуру

                                в свежем стартапе на 3-5 пиццерий никто не задумывается, как это будет работать на 100 пиццериях. Отсюда проблемы с архитектурой. У монолита с архитектурой плохо, и это одна из главных проблем. Если бы первоначальные авторы системы могли придумать сходу архитектуру, рассчитанную на 600 пиццерий, то они бы не были разработчиками для маленького стартапа — таких людей только в больших корпорациях найдешь. Но даже если бы они, все же, писали ее для Федора в далеком 2011 году, они бы не успели ее в срок. Тогда скорость разработки была сильно важнее задела на будущее.

                                Мой 15 летний опыт работы говорит о том, что так думают только оптимисты.

                                А мы оптимисты! Вот как оценивать рефакторинг монолита с высокой связностью кода и кучей мест, в которых даже старожилы уже не разбираются? Может, у вас есть проверенный подход, занимающий не больше недели?

                                обычно используют фильтры, ограничивая выборку

                                Посыл был в том, что загруженность БД не принципиально меняется, если после разделения таблицы джойнить все обратно — фильтровать по 1кк заказам с 30 столбцами или фильтровать 10 табличек по 1кк строк и 3 столбцами каждая и джойнить результат друг на друга.

                                Также вообще непонятно, как ошибка в коде бизнес-логики влияет на падение базы

                                плохой запрос в базу, ошибка в политике ретраев, ддосящая базу, и да, дедлоки

                                Отчеты никогда не должны строится извините за расизм с мастер-реплики

                                да, тут разночтений нет, но отдельная БД появилась не сразу
                                  0
                                  У монолита с архитектурой плохо

                                  Еще раз. У монолита с архитектурой все прекрасно. Плохо с архитектурой у плохого монолита. В монолите также прекрасно присутствуют сервисы, которые могут пилиться отдельными командами. В монолите никто не мешает использовать несколько источников данных и прочее. Единственное, что плохо делает монолит — это скейлится, но, повторюсь это играет роль на уровне десятков тысяч запросов в секунду.
                                  И то, если писать нормальный код, то у меня на локальном компьютере связка .Net Core + EF Core держала под 10к запросов. А с учетом того, что в нормальном проде надо бы сервера 4, то 20-30к запросов вполне достижимо.

                                  Посыл был в том, что загруженность БД не принципиально меняется, если после разделения таблицы джойнить все обратно — фильтровать по 1кк заказам с 30 столбцами или фильтровать 10 табличек по 1кк строк и 3 столбцами каждая и джойнить результат друг на друга.


                                  Вы не совсем правы. Наймите себе DBD/DBA, он вам поможет. Серьезно.
                                  Если кратко. Наивысшая производительность — когда вся таблица в памяти. Очень сложно запихивать в память таблицы с кучей столбцов, которые редко используются. Особенно, если там еще и варчары. Соответственно данные делятся. Часто используемые остаются в основной таблице. Редко используемые — выносятся в свои таблицы. В результате эти дополнительные данные будут джойнится редко и в самый последний момент. Более того, 1кк записей не в отчетах — это ахтунг. Если такое нужно в логике (непонятно зачем), то это не должно покидать СУБД, иначе невозможно говорить о производительности вообще.
                                  Может быть у вас еще и SELECT * используется?

                                  плохой запрос в базу

                                  Как может быть плохой запрос в базу, если он зашит в систему? Или у вас можно было дать доступ для аналитики в боевой инстанс?
                                    0
                                    Единственное, что плохо делает монолит — это скейлится

                                    Причем в данном случае масштабируется довольно хорошо — под каждую пиццерию выделенная виртуалка с копией монолита. Тем самым решается куча вопросов:
                                    • Отказоустойчивость — упавшая одна виртуалка никак не влияет на все остальные 599
                                    • Надежность — все обращения в рамках одного «физического» компьютера, нет обращений по сети. Опционально можно разве что разбить сервер-приложений и сервер-БД
                                    • Распределение нагрузки — думаю такой выделенный монолит легко выдержит нагрузку с одной пиццерии
                                    • Своего рода CDN — для Владивостока виртуалка поднимается в дата центре во Владивостоке
                                    • Тестировать обновления — можно только на одной машине, опять же все остальные спокойно работают
                                    • Можно вообще подумать в сторону копии в самой пиццерии с синхронизацией в облако — тогда отсутствие интернета не застопорит работу пиццерии. Но тут конечно больше вопросов, чем ответов
                                    • Можно даже распределить финансовые затраты — стоимость аренды сервера вешается на пиццерию


                                    Проблем сходу видно три:
                                    • Пользователи — должны иметь сквозной идентификатор, потому что один и тот же человек может приехать в другой город и заказать пиццу там — вот для них надо выделить общую базу
                                    • Консолидированные отчеты по всем пиццериям — но тут достаточно сливать все данные из всех мастеров в одну отдельную БД с такой же структурой и поменять строку подключения в админке
                                    • Обновление всех этих монолитов, но с микросервисами такая же фигня
                                      0
                                      1. Это очень дорого. 600 виртуалок с полноценной системой, пусть даже маленьких, против 3-4 пожирнее сейчас.
                                      2. Как вписать в вашу модель сайт и мобильное приложение, раз уж всё — один сплошной монолит?
                                      3. Конкретно в нашем случае система не сможет так работать, под это надо затачивать
                                      4. Чисто по бизнес-логике многими настройками пиццерии заведует Управляющая Компания централизованно, а если делить настройки, то постоянно придется тасовать их туда-сюда — накладно в разработке
                                      5. Сейчас при ошибках в релизе или от падения мы можем руками быстренько подхачить данные сразу во всех пострадавших пиццериях. Если будет по виртуалке на пиццерию, это превратится в неподъемную задачу.
                                      6. Ну очень долгий релиз, да к тому же с даунтаймом (если по одной виртуалке на пиццерию).
                                      7. Обслуживать такое кол-во виртуалок надо уметь. Мы пока не готовы.

                                      Надежность — все обращения в рамках одного «физического» компьютера, нет обращений по сети. Опционально можно разве что разбить сервер-приложений и сервер-БД

                                      У нас проблемы очень редко связаны с сетью, чаще с БД или ресурсами на самой виртуалке. К тому же сеть останется, бэкэнд же не прямо на планшете загружен — за ним еще по сети сходить надо.
                                      0
                                      У монолита с архитектурой все прекрасно

                                      Это у нашего монолита все плохо, а не у у монолита как класса

                                      Наивысшая производительность — когда вся таблица в памяти

                                      Это возможно реализовать с managed базами? Что-то мне подсказывает, что не будет нужного уровня управляемости…

                                      Как может быть плохой запрос в базу, если он зашит в систему

                                      Ошибка разработчика — не учел размеры таблицы, индексы, в назании поля ошибся, да мало ли что — Со всяким бывает.

                                      Или у вас можно было дать доступ для аналитики в боевой инстанс

                                      До разделения базы на мастер, рид и АХД и такое бывало. Но это не относится к вопросу разделения на сервисы
                                        0
                                        Еще раз повторю, Azure это для либо маленьких компаний, с платой 30 баксов в месяц, либо для очень больших, для которых 100к баксов в месяц лишних на сервера — не деньги.

                                        В случае средних компаний нет никакого смысла иметь облако. Нет таких нагрузок, где нужно масштабирование уровня поднять быстро 100 инстансов. А переплата гигантская, на более-менее вменяемых инстансах переплата будет раз в 10-15. А экономии нет, так как человек с хорошими знаниями Azure стоит дороже чем админы на аутсорсе (ну или свой в штате).

                                        Если уж совсем хотите заморочиться — ну поднимите себе HyperV Server и устройте себе свое облако.

                          0
                          а почему именно выбрали RabbitMQ и MySQL?
                            0
                            Тут все просто. RMQ у нас уже был к этому моменту, мы его пробовали в предрасчетах и реализации CQRS на учете. Там надо было обрабатывать расход и списания ингредиентов. Я об этом не упоминал, чтобы не усложнять. Тогда показалось, что он вполне неплох. Кафку мы не хотели дополнительно привносить, она еще тогда не была в Azure как managed service.
                            MySql был с самого начала, выбор в пользу массовой, простой и условно бесплатной базы. Отсюда такое странное сочетание .net + mysql.
                              0
                              Я конечно не знаю, как сейчас живет MySql, но мой опыт говорит, что в более-менее нагруженных проектах MySql ломается чаще, чем работает.
                                0
                                а ZeroMQ и PostgreSQL не пробовали?
                                  0
                                  Расскажите какие железки под базы данных сейчас выделены.
                                  И если не секрет, какой примерно объем данных в целом.
                                +1
                                Очень интересно, огромное спасибо за материал.

                                Если данные есть, то нужно проверить их на актуальность в базе пользователя. Не изменилась ли его роль, не надо ли его не пускать теперь на страницу. В этом случае после получения сессии (1) надо напрямую сходить в базу и проверить доступы пользователя с помощью слоя логики аутентификации (2).


                                Не очень понятна роль (1) здесь. Все равно мимо (2) не пройти — ну так сразу и посылать туда куку.

                                Only users with full accounts can post comments. Log in, please.