Вынос товарных остатков из 1С в микросервис
Рив Гош — это одна из крупнейших розничных сетей косметики и парфюмерии. Мы представлены в нескольких каналах продаж: офлайн-магазины, собственный интернет-магазин и все ведущие маркетплейсы.
Каждый канал продаж индивидуален, но все они требуют онлайн информацию об остатках: В офлайн-магазине на кассе мы предлагаем клиенту товар по акции. Причем предлагаем только товар, аналогичный выбранному клиентом, который есть в остатках этой локации. В собственном интернет-магазине есть опция получения товара в магазине сети Ривгош или заказать экспресс-доставку из магазина. Маркетплейсам вообще нужна максимально актуальная информация об остатках — любой отказ от заказа клиента влечет штрафы и снижение рейтинга.
Откуда же эту информацию мы можем выдать? Достоверная оперативная информация о товарных остатках в Рив Гош распределена по разным 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% достоверная информация. В этих случаях есть смысл выделять кеширующие микросервисы и таким образом снимать нагрузку с учетных систем.