Архитектура системСистемный архитектор

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

Проходите собеседования с ИИ помощником Hintsage

Ответ на вопрос

История: Традиционные платформы электронной торговли полагались на монолитные экземпляры RDBMS с пессимистичными блокировками, которые не справлялись с нагрузками от распродаж, превышающими 100,000 параллельных покупок в секунду. Отрасль перешла к шаблонам CQRS и Event Sourcing, чтобы разъединить потоки чтения и записи, но это привело к усложнению процесса поддержания точности запасов через распределённые WMS и устаревшие ERP хранилища с различными характеристиками задержки.

Проблема: Основная задача заключается в удовлетворении условий теоремы CAP во время сетевых разделений, при этом предотвращая перепродажу — строгое бизнес-правило. Механизмы распределённой блокировки, такие как RedLock, вводят риски задержки и доступности, в то время как модели с чисто конечной согласованностью рискуют продавать фантомные запасы. Кроме того, гетерогенные точки интеграции с устаревшими системами WMS на основе SOAP/XML создают несоответствия и каскады тайм-аутов, что усложняет атомарные границы транзакций.

Решение: Реализуйте Event Store (например, Apache Kafka или EventStoreDB) в качестве источника правды для дельт запасов, используя оптимистичное управление конкурентностью с векторными часами для установления причинного порядка без глобальных блокировок. Используйте Saga orchestration (с помощью Temporal или Camunda) для управления кросс-WMS транзакциями, где локальные резервации сразу фиксируются в хранилище событий, а асинхронное подтверждение от WMS инициирует окончательное распределение или компенсационные выпуски. Для масштабируемости чтения разверните CQRS с CDC через Debezium, проектируя в Redis или Elasticsearch, обеспечивая задержку чтения менее 50 мс, принимая временную устаревание, смягчаемую сроками действия резерваций TTL.

Ситуация из жизни

Во время подготовки к Чёрной пятнице 2022 года глобальный ритейлер модной одежды столкнулся с катастрофическими тайм-аутами баз данных, когда 50,000 параллельных пользователей нацелились на выпуск ограниченной серии кроссовок. Их существующая топология MySQL master-slave испытывала серьёзные конфликты при записи на горячих строках инвентаря, что привело к 12-секундным задержкам оформления заказов и 300 подтверждённым случаям перепродажи, вызванным задержкой репликации между основным и читательскими репликами. Бизнесу потребовалось решение, способное справиться с пиковыми нагрузками от распродаж, сохраняя при этом строгое инвариантное правило, чтобы продаваемые единицы никогда не превышали физические запасы на складе.

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

Альтернативная стратегия заключалась в горизонтальном шардировании базы данных по диапазонам SKU и применении протоколов Двухфазной фиксации для поддержания гарантий ACID в рамках региональных экземпляров PostgreSQL. Это решение обеспечивало сильную согласованность и немедленную точность запасов без сложных схем конечной согласованности, что подходило традиционным транзакционным мышлениям. Тем не менее, недостатки оказались неподъёмными: блокирующая природа 2PC означала, что сбои координатора могли удерживать блокировки базы данных неопределенно долго, а сложность сообщений протокола создавала насыщение сети во время пиковых нагрузок, что в корне нарушало требования доступности, необходимые для круглосуточной глобальной торговли.

Финальная архитектура приняла Event Sourcing с Apache Kafka и Saga оркестрацией, приняв семантику BASE, при этом обеспечивая бизнес-инварианты с помощью компенсационных транзакций. Преимущества включали внутреннюю горизонтальную масштабируемость за счёт разбиения потоков событий, неизменные аудиторские записи, критически важные для анализа мошенничества, и естественную интеграцию с гетерогенными устаревшими WMS через идемпотентные потребители событий. Основные недостатки включали крутой кривой обучения для разработчиков, незнакомых с неизменными паттернами данных, и операционную сложность управления эволюцией схем событий и стратегиями воспроизведения для новых проекций модели чтения.

Комитет по архитектуре выбрал подход Event Sourcing, потому что распродажи в основном приоритизируют доступность и терпимость к разделениям, а бизнес-логика могла учитывать временные мягкие резервации с пяти минутными TTL вместо жестких блокировок базы данных. В отличие от альтернатив с блокировкой, этот дизайн позволил системе оставаться доступной во время сетевых разделений между дата-центрами, обеспечивая возможность покупки для клиентов, даже если подтверждения со склада испытывали задержку. Кроме того, неизменный журнал событий обеспечивал аудируемость, необходимую командам финансов для согласования расхождений с поставщиками сторонних логистических услуг.

Реализация задействовала Kafka Streams для управления локальным агрегированием запасов, Temporal для оркестрации саг через системы SAP и собственные системы WMS, и Redis с кэшированием для оптимизации запросов. Во время последующего события Кибер понедельник платформа успешно обработала 120,000 параллельных покупок с p99 задержкой менее 80 мс и нулевыми инцидентами перепродажи, сохраняя 99.99% доступности, несмотря на смоделированный региональный сбой в us-east-1, который бы сломал предыдущую монолитную архитектуру.

Что кандидаты часто упускают

Как вы предотвращаете фантомные запасы при обработке параллельных резервирований через несколько партиций Kafka без использования глобальных блокировок?

Фантомные запасы возникают, когда параллельные команды читают устаревшие уровни запасов из разных партиций, и обе фиксируют резервации, превышающие фактическую доступность. Чтобы предотвратить это без глобальных блокировок, реализуйте оптимистичное управление конкуренцией, используя номера версий в Event Store; каждый агрегат запасов поддерживает монотонный счётчик, а команды включают ожидаемые версии, при этом хранилище отклоняет добавления, если версии не совпадают, forcing client retries. Кроме того, обеспечьте аффинность партиций путём хеширования SKU к конкретным партициям, поддерживая семантику единого писателя на агрегат и полностью устраняя координацию между партициями.

Какова стратегия компенсационной транзакции, когда устаревший эндпоинт WMS SOAP выдает тайм-аут после того, как локальное хранилище событий уже зафиксировало уменьшение запасов?

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

Как вы обрабатываете воспроизведение событий и восстановление модели чтения, не exposing inconsistent inventory counts to customers during the reconstruction process?

Воспроизведение событий для восстановления моделей чтения CQRS рискует представить временно некорректные уровни запасов, если проекции обновляются постепенно во время выполнения запросов. Решение использует blue-green deployment для моделей чтения: создайте теневую проекцию, потребляющую журнал событий с нулевого смещения, в то время как существующий экземпляр обслуживает трафик, затем атомарно переключите маршрутизацию, когда тень догонит. Кроме того, используйте Kafka сжатие журналов и периодические снимки S3, чтобы сократить время воспроизведения, обеспечивая, чтобы новые проекции минимизировали временной интервал потенциальной несогласованности во время восстановления.