Рив Гош – это одна из крупнейших розничных сетей косметики и парфюмерии. Мы представлены в нескольких каналах продаж: офлайн-магазины, собственный интернет-магазин и все ведущие маркетплейсы.
Каждый канал продаж индивидуален, но все они требуют онлайн информацию об остатках: В офлайн-магазине на кассе мы предлагаем клиенту товар по акции. Причем предлагаем только товар, аналогичный выбранному клиентом, который есть в остатках этой локации. В собственном интернет-магазине есть опция получения товара в магазине сети Ривгош или заказать экспресс-доставку из магазина. Маркетплейсам вообще нужна максимально актуальная информация об остатках – любой отказ от заказа клиента влечет штрафы и снижение рейтинга.
Откуда же эту информацию мы можем выдать? Достоверная оперативная информация о товарных остатках в Рив Гош распределена по разным OLTP системам: часть магазинов в современной централизованной 1С ERP управление холдингом, остальные магазины – в специализированных базах Oracle, распределительные центры – в 1С УПП. Единого оперативного хранилища нет.
Чтобы централизованно выдать остатки, шине данных приходилось последовательно пробегаться по базам данных всех магазинов, агрегировать их и консолидировано выдавать их внешним потребителям этих данных. Магазинов много, и на их последовательно-параллельную обработку уходило порядка 3х часов. Такая задержка с обновлением остатков вызывала много ложных заказов: клиент заказывает товар, товаровед идет его собирать с полок магазина и не находит. Клиент получает отказ, а значит мы теряем рейтинг на маркетплейсах и клиентскую удовлетворенность. Надо было что-то делать.
Для решения проблемы маркетплейсы и собственный интернет-магазин начали переходить на схему запроса остатков «по требованию». Ранее инициатива выгрузки остатков была на стороне шины данных. Как только она заканчивала сбор данных, выгрузка отправлялась всем потребителям. В новой схеме интеграции любой канал продаж мог самостоятельно принять решение, что ему надо обновить остатки. Сторонний сервис приходит в нашу шину данных с запросом на обновление. Шина данных определяла, в какую базу надо сходить за остатками, старалась побыстрее получить данные и возвращала ответ.
Проблему актуальности это решало, но частота запросов выросла так, что забивала канал до магазинных баз и вызывала невероятную нагрузку на сервера учетных систем и саму шины данных. Непреднамеренные DoS шквалы стали появляться регулярно. По разным причинам одиночные запросы могли подвиснуть на 4-5 минут. Даже с небольшим RPS это вызывало ажиотаж на шине. На шине данных количество рабочих процессов за считанные секунды увеличивалось до тысяч, и она просто падала. Посмотрев нагрузку на сервера баз 1С стало ясно, что порядка 30% CPU потребляют запросы остатков. Это тоже вызывало задержки в работе основных процессов товародвижения. Так выкристаллизовалась первая проблема – слишком тяжелая распределенная учетная система слишком медленно отдавала информацию об остатках. На обслуживание одного запроса требовалось слишком много процессорного времени и толщины канала.
Вторая возникшая проблема – наличие технологических окон на обслуживание учетных систем. Розничная сеть все же давала нам 1 свободный час – самый западный магазин закрывается в 01:00 по Мск, а дальний восток открывается в 2:00 по Мск. При этом интернет-магазин и маркетплейсы работают 24х7х365 с SLA выше 99,9%. Наш сервис остатков не должен быть зависим от обслуживания учетных систем и должен работать в режиме 24х7х365. Еще сложнее соблюдать SLA при заборе данных из магазина. В худшем случае оптический канал до магазина может быть физически поврежден и сторонние сервисы получали ответом отказ.
Так стало понятно, что пора выносить эту функцию из учетных систем. Пришло время пилить монолит 1С на микросервисы.
Архитектура
Целевая картина, под которую строили архитектуру, ориентировалась на масштабирование примерно до 2 000 простых и 20 сложных запросов в секунду (RPS). Актуальность – порядка 5 минут. Сложные запросы – передача всех остатков по одному складу/магазину с пагинацией. Простые – запрос остатков по нескольким товарам (до 20 SKU).
В этот же сервис понадобилось добавить и актуальные цены магазинов. В разных розничных магазинах и каналах продаж цена товаров может несколько отличаться. Актуальность данных об ценах столь же критична, как и остатки. Задержка передачи данных приводила к невозможности отгрузки по заявленной цене.
Помимо остатков и цен, по запросу должна предоставляться информация об основных характеристиках товара с возможностью произвольной фильтрации по характеристикам, наличию и ценам.
Так сервис остатков превратился в централизованное хранилище ассортимента и немного превысил рамки понятия микросервис. К сожалению, дальше резать его на кусочки не получилось. Пришлось оставить в таком виде.
В качестве базы данных выбрали Postgres. Основные причины: полноценный SQL, возможность неограниченного масштабирования и, конечно, наличие экспертизы существующей команды.
Так вырисовалась целевая картина: Postgres каждые 5 минут забирает из баз данных учетных систем полные данные по остаткам и ценам, кеширует их у себя. Характеристики товаров достаточно актуализировать раз в несколько часов – они меняются крайне редко.
Шина данных забирает данные в Postgres и отправляет потребителям. Собственный интернет-магазин напрямую ходит в сервис остатков.
Реализация
Самой сложной частью проекта стала переделка всех потребителей. Потребовалось консолидировать все требования к данным, перекладывать их на язык кеширующего сервиса и переписывать код всех интеграций, которые ранее работали напрямую с учетными системами.
Сам по себе микросервис оказался очень простым к реализации, надежным и не требующим существенных расходов на эксплуатацию. Нам не требовалось задумываться о проблемах авторизации, так как все данные внутри сервиса являются публично доступными.
При проектировании распиливания монолита на микросервисы всегда очень сложно выделять автономные сервисы, который будет самодостаточен и при этом функционален. Еще тяжелее распиливать учетные системы. Обычно у них есть какая-то общая таблица проводок, к которой обращаются все бизнес-процессы.
Но не всегда внешним потребителям требуется 100% достоверная информация. В этих случаях есть смысл выделять кеширующие микросервисы и таким образом снимать нагрузку с учетных систем.
Комментарии (9)
anton99zel
05.01.2025 14:14Всё в кучу. И маркеплейсы и все магазины от Калининграда до Владивостока. Во первых, группа магазинов должна работать с одним региональным складом. На этом уже в десятки раз падает нагрузка на базы. Во вторых товарные остатки магазина и маркеплейсов должны быть разделены как физически по разным складам, так и программно. Далее, не базы магазинов надо опрашивать, а магазины должны отправлять данные. Кроме того, не совсем понятно для чего нужны оперативные данные в моменте всех остатков по всей стране. Неужели, если в Москве закажут товар, то его повезут из Владивостока?
Статья обозначила проблему, жаль решение не расписали. Но чую, что у компании большие проблемы с проектированием программных решений и подходом в управлении товарными запасами.
Xexa
05.01.2025 14:14Кроме того, не совсем понятно для чего нужны оперативные данные в моменте всех остатков по всей стране.
Если открыть любой нормальный интернет магазин с распределенной сетью магазинов/складов по стране, то там отображается количество доступного товара + есть ли в "твоём" городе товар.
Т. ч цель понятна. Цель благая и полезная.
Ну и "магазины должны отправлять" - почему и кому должны? Это лишь одна из многих реализаций архитектурных. Она ни лучше и не хуже иных. А кривость работы, это лишь кривость реализации, но не архитектурного подхода.
anton99zel
05.01.2025 14:14Вы правы, что это удобно. Более того, это должно быть. Я упомянул, что для этого не совсем обязательно иметь именно оперативный учёт. Ничего страшного, если покупатель увидит, что на остатке меньше, чем есть или товар отсутствует совсем. Условно, через час информация обновится по магазину в глобальной базе и уже пусть не этот, но другие клиенты смогут узнать информацию о наличии.
Что касается кругового опроса магазинов, то схема когда магазины сами отправляют данные более предпочтительна по причине, что такие данные будут отправлены как только будут готовы. В случае кругового опроса, инициатору надо ждать ответа. А ведь может не дождаться, и хорошо если в таком случае есть многопоточность.
Isiirk
05.01.2025 14:14Не понимаю, зачем вы сделали те же яйца только в профиль... Пулить остатки (характеристики, цены) надо с переферийных баз по мере появления изменений, а не забирать централизованно, тогда у вас всегда будут актуальные остатки в одной БД без задержек и без лишних нагрузок. Про регионы и региональные склады сказали выше, чет у вас ребята явно проблема с проектированием.... И видимо в компании с бизнес процессами...
CrushBy
05.01.2025 14:14Откуда же эту информацию мы можем выдать? Достоверная оперативная информация о товарных остатках в Рив Гош распределена по разным OLTP системам: часть магазинов в современной централизованной 1С ERP управление холдингом, остальные магазины – в специализированных базах Oracle, распределительные центры – в 1С УПП. Единого оперативного хранилища нет.
Что-то не очень понятно. Если погуглить, то выдает, что у Рив Гош сейчас всего 257 магазинов всего. У нас на lsFusion ERP есть FMCG-сети с большим количеством магазинов. И и все магазины и распределительные центры работают в одной базе на одном сервере PostgreSQL без какой-либо кластеризации. И тоже есть доставка, и тоже есть запросы на остатки и комплектации. И то, там нагрузка больше 50% не поднимается. Зачем столько баз ?
Что вы там с OLTP базой делаете ? Сколько у Вас пользователей одновременно работает на 257 магазинов то ?
lair
Хм.
Было:
Стало:
Т.е., процесс, который раньше занимал порядка трех часов, теперь занимает меньше пяти минут?
phaggi
Как я понял, 3 часа было, когда собирали все подряд.
Когда стали собирать более целево, стало быстрее, но возникли шторма.
Теперь выделенная БД сама забирает регулярно только самое необходимое, т.е. нагрузка осталась, но стала прогнозируемой и однообразной; и ее наверное проще распараллелить; а сложные запросы теперь можно обрабатывать не на стороне учетных систем торговых точек и не на шине, а в отдельном хранилище. Наверное, это на какое-то время решит проблему.
Но основная проблема - это проблема дальнейшего роста. Плохо кончится…
lair
То есть все сводится к тому, что шина данных не имела никакого своего накапливающего хранилища?
timka05
Да, статья в стиле как нарисовать сову. Никаких технических подробностей. Как новый сервис забирает данные из нескольких баз 1С? Почему это стало быстрее? Ни-че-го.
Корпоративный блох во всей красе