Почему и как в Prozorro.Sale перестраивают СУБД через два с половиной года после запуска

Привет, меня зовут Гриша, я СТО «Прозорро.Продажі». Уже больше года вместе с командами Raccoon Gang и Triangu мы перестраиваем систему электронных аукционов, недавно пробившую отметку в 33 миллиарда гривен продаж. Про большие цифры и крупные сделки говорят часто и по телевизору. На DOU уже выходила статья о том, как в целом организована работа нашего проекта. Но вот о том, как эта штука работает технически, сказано далеко не всё.

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


Стек (Блиц!)

Языки и библиотеки: Python 3.8, aiohttp, Motor, schematics.

Для хранения данных: MongoDB, Elasticsearch и Swift.

Для работы с метриками: Prometheus, Grafana, Loki.

Для автоматизации развертывания и управления инфраструктурой: GitLab CI, Docker, Kubernetes, Helm.

Для документирования: Swagger, GitLab, Confluence.

Для тех, кто хотел бы контрибьютить: gitlab.prozorro.sale/explore :)

Зачем Цэ-Бэ-Дэ

Стоит начать с того, что такое двухуровневая система. До Революции достоинства одним из больных мест во взаимодействии государства и бизнеса был вопрос доверия: использование информационных систем часто ассоциировалось с ручными вмешательствами и корректировками в пользу «правильных» пользователей.

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

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

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

На самом деле, ЦБД — это не только база, но и API, сервис хранения документов, инфраструктурные сервисы и другие компоненты полноценного приложения.

Путь развития

Двухуровневая система закупок успешно работает с 2015 года. А в 2016-м появилась система аукционов «Прозорро.Продажі». Тоже двухуровневая. И изначально построенная на том же коде, что и система госзакупок. Почти такая же система, но не совсем.

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

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

Одновременно с этим выяснилось, что даже двух инстансов ЦБД («1» и «2», нейминг — наша сильная сторона!) явно мало. Причина крылась в архитектуре: разработчик системы закупок, от которой происходило наследование, не предполагал такого профиля использования, а потому запуск каждого нового направления становился все более длительным, дорогостоящим и болезненным. Помимо этого, проявилось определенное количество болезней роста, не все из которых можно было решить малой кровью.

Собрав все procs and cons вместе, было принято решение о разработке новой версии ЦБД (3!), про которую дальше и пойдет речь.

Обзор ограничений ЦБД-1/2

Срок эксплуатации ЦБД-1/2 на момент принятия решения про полную переработку составлял порядка трех лет. За это время был собран большой объем эксплуатационной информации, а также данных о профиле использования системы. В дополнение к этому уже существовала дорожная карта по развитию бизнес-составляющей: увеличение количества аукционов, новые сервисы для площадок, количество и сроки запуска новых направлений (эта часть роадмапа вам может быть известна из экономического блока новостных сайтов).

Что же выяснили в результате анализа? Поговорим более детально.

Версионирование и деплой

Ввиду специфики финансирования разработки (большая часть функционала разрабатывалась как fixed-price проекты на средства международных доноров) построение цикла непрерывной разработки было невозможным, а каждое новое решение писала хоть немного, но другая команда, со своими правилами и подходами. Как следствие, кодовая база начала страдать от весьма типичных, но от того не менее болезненных факторов:

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

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

Консистентность данных

В качестве основной СУБД-системы использовался CouchDB. Такие базы не поддерживают strong consistency, а для финансовых данных это, вообще говоря, жизненно необходимо. Для синхронизации инстансов понадобился дополнительный сервис Consul, который, с одной стороны, обеспечивал достижение консистентности, а с другой — добавлял точку отказа, которая неоднократно становилась причиной инцидентов.

CAP-теоремаCAP-теорема: невозможно одновременное достижение всех трех составляющих — целостности, распределённости и высокой доступности

Синхронизация площадок

Сложилась практика, в которой каждая отдельная площадка хранила полную копию центральной базы данных и синхронизировала ее с помощью запросов на поиск. С увеличением объема данных даже пассивное существование одной площадки стало создавать заметную нагрузку на инфраструктуру. А площадок к тому моменту было порядка 50. Отсюда — линейный рост потребностей инфраструктуры без создания какой-либо добавочной стоимости.

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

Простота разработки

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

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

Отказоустойчивость

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

Попыток манипулировать аукционами за три года зафиксировано не было. А вот отказы с последующей потерей до 30% проходящих аукционов случались.

Технологические зависимости

Основным языком разработки ЦБД-1/2 является Python версии 2.7, для которой end of life должен был наступить 01.01.2020 (и с этим квестом 2020-й успешно справился). Использование устаревших, но стабильных технологий не является чем-то в принципе невозможным, однако если этого можно избежать — зачастую это стоит делать.

Кроме этого, в архитектуру системы было заложено нативное использование AWS S3 для хранения скан-копий документов. С этим фактом связано два возражения. Во-первых, никто не любит vendor-lock как таковой (во всяком случае, в «Прозорро.Продажі»). А во-вторых, система должна была получить аттестат КСЗИ, что, в свою очередь, предполагает физическое размещение компонент на территории Украины (где, как известно, до сих пор не построены дата-центры AWS). Потому от этой зависимости нужно было избавиться.

Что решаем

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

Цели предстоящего усовершенствования сформулировали так:

  • срок запуска новой процедуры торгов — 1 месяц, включая тестирования;
  • автоматизированный деплоймент длительностью до 1 часа;
  • количество одновременно поддерживаемых аукционов — 10х от текущего количества;
  • все конфигурации — в одном месте;
  • масштабируемость, отказоустойчивость, быстрое восстановление и вот это вот все, что есть в каждом первом техзадании на enterprise-систему.

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

Новое в ЦБД-3: изнутри

Замена СУБД

Для решения вопроса консистентности очевидным ходом была смена СУБД. Переход на реляционную базу не рассматривался ввиду профиля данных (один аукцион — одна структура), который хорошо согласуется с логикой нереляционной базы.

Выбор остановился на MongoDB. Основными аргументами в пользу этой СУБД стала гарантированная консистентность данных, а также нативная система репликации. На тестовом стенде подтвердилась как скорость синхронизации инстансов без дополнительных средств, так и эффективное восстановление при отказе одной из нод.

Результат замены — повышение отказоустойчивости в целом и существенное упрощение процесса синхронизации площадок с ЦБД.

Стейт-машина и хронограф

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

Как и в любой BPM-системе, вся бизнес-логика держится на двух китах: статусах и расписании. Именно на оптимизации работы этих компонент было сконцентрировано низкоуровневое проектирование.

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

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

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

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

Кроме того, одна секунда — это эмпирически определенная константа. Если когда-либо наступит момент, при котором этого времени не хватит для завершения большого количества транзакций, его можно будет увеличить, незначительно пожертвовав временем обработки очереди. Однако нагрузочные тесты говорят, что для этого «Прозорро.Продажі» должны начать продавать больше раз в 50, не добавляя ни гигагерца ресурсов.

Конфигурации

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

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

Отказоустойчивость

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

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

Доставка, среды и площадки

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

Причина именно такого набора сред — в жизненном цикле разработки. В отличие от продуктов, полностью управляемых изнутри компании, здесь свою роль играет двухуровневость системы. От логики ЦБД напрямую зависит успешность работы площадок. А каждая площадка — сама по себе уникальный продукт, который должен как адаптироваться к внешним изменениям (ЦБД), так и к внутренним потребностям бизнеса: UX-оптимизации, собственным технологическим аспектам и другим вызовам, про которые разработчики ЦБД даже не догадываются.

Чтобы обеспечить синхронность движения площадок и ЦБД по версиям, мы обязательно следуем такой логике их движения:

  • после формирования очередного релиза обновляется версия sandbox. Обновление сопровождается подготовкой детального описания предстоящего релиза (высокий уровень культуры коммитов спасает от лишней ручной работы);
  • следом происходит смещение версии staging на версию, ранее доступную на sandbox. Безопасность действия гарантируется прикладыванием персонального токена, без которого не запускается соответствующий пайплайн;
  • после полного тестирования на совместимость версий, прогонки интеграционных тестов и полных бизнес-процессов эта же версия незамедлительно доставляется в продуктив;
  • все версии «замирают» до выполнения следующего цикла. За это время (2–3 недели) разработчики ЦБД успевают подготовить следующую версию, а площадки — адаптироваться к только что выпущенной:

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

Версии техзаданий

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

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

Модели

Ключевой составляющей техзадания и, собственно, ЦБД является модель данных. Прежде чем добиться устойчивой структуры, удовлетворяющей как требования оптимальности, так и пожелания бизнеса, было проведено несколько итераций рефакторинга. Результатом стало выделение типичных структур, неизменных для всех процедур продажи, и последующее их наследование с изменениями, присущими специфическому бизнес-процессу.

Для сериализации/десериализации и управления доступом к непубличным данным было принято решение использовать Python-библиотеку Schematics, которая позиционируется как ORM без слоя DB. После внесения всех необходимых изменений получилась ощутимо кастомизированная версия, которая и ушла в продуктив.

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

Решением обеих задач сразу стало написание Swagger-Schematics генератора, который бы формировал модели непосредственно из Swagger-файла. В итоге процесс получился таким:

  • аналитик собирает требования и формирует из них структуру в формате, поддерживаемом Swagger;
  • файл попадает в пайплайн, где сначала происходит автоматическая валидация структуры, а потом разработчик проводит код-ревью на предмет общей логичности;
  • готовый файл одновременно публикуется в API-документации и отдается генератору, который создает рабочие модели, непосредственно используемые кодом.

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

Еще одним слабым местом можно назвать усложнение восприятия решения новыми разработчиками: никаких моделей в коде нет, фактически был создан дополнительный уровень абстракции, который нужно «представлять» при тестировании и внесении изменений. В свое оправдание можем сказать, что это самый «абстрактный» участок решения. Во всем остальном это эталон the zen of Python!

Новое в ЦБД-3: снаружи

Зеркало

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

Решение основано на встроенном механизме реплицирования MongoDB, который определяет и передает все непосредственные изменения клиенту. Клиенту достаточно открыть и держать поднятым WebSocket, чтобы иметь свою постоянно актуальную копию данных.

По общим правилам разработчики площадок весь свой код пишут самостоятельно. Однако в данном случае в виде исключения специалисты ЦБД создали клиент для зеркала, который может быть использован площадкой «из коробки». Официальная версия написана на Python 3.8 и умеет работать с MongoDB и Elasticsearch, неофициальные есть на Go, Rust и TypeScript (ну, знаете, увлеклись, когда писали). На таком же клиенте был построен и поиск в ЦБД.

Нотификации

Где-то через полгода после начала работ внутри команды прозвучал вопрос: а что с нотификациями? Дело в том, что, согласно бизнес-требованиям, площадки должны оповещать клиентов о событиях в аукционах в виде писем: начало — конец — отмена — всякое. Эта функция исторически находилась на стороне площадок, которые вводили нотификации согласно техзаданию и общему здравому смыслу.

Мы решили воспользоваться здравым смыслом несколько иначе: зачем писать логику формирования нотификаций сорок раз (на каждой площадке), если можно сделать это один раз в ЦБД. Был добавлен довольно простой сервис, который стал генерировать события с темой, адресатом и минимально необходимым текстом в специальном эндпоинте.

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

Торги

В отличие от нотификаций, непосредственно процесс торгов в старых инстансах был монополией ЦБД.

Для ЦБД-3 был разработан новый дизайн с продуманным UX и оптимизацией под современные браузеры. Над дизайном интерфейсов на волонтерских началах работала Юлия Снитко, дизайнер с 10-летним опытом, в прошлом руководитель дизайн-отдела студии Bachoo. Она разработала UI/UX части, готовила макеты и описывала детали реализации верстки для разработчиков.

Однако все равно оставалось ощущение, что сделано не всё.

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

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

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

Эксперимент оказался удачным: десятки разных интерфейсов торгов, от консервативных серых таблиц до динамичных карточек, которые сами сортируются по времени, важности или цене. При этом интерфейс, предоставляемый ЦБД, остался доступен для пользователей. Судя по мониторингу, «родной» интерфейс все так же выбирают покупатели уникальных лотов, а интерфейсы площадок — покупатели древесины, для которых он и разрабатывался.

А что дальше

Мир уже давно ушел от концепции «написал — эксплуатируешь — выбросил». Для того чтобы время, инвестированное в ЦБД-3, не пропало зря, система будет постоянно поддерживаться, развиваться и дополняться. В бэклоге еще сотни задач. Есть очевидные: идти в ногу с технологиями и запускать новые направления (теперь здесь совсем мало кода и много бизнес-анализа). А есть и более амбициозные: превратить ЦБД в коробочное решение, обеспечив тем самым запуск такой же платформы в других странах.

Что бы там не получилось, это только начало пути.

Все про українське ІТ в телеграмі — підписуйтеся на канал DOU

👍ПодобаєтьсяСподобалось10
До обраногоВ обраному4
LinkedIn

Схожі статті




46 коментарів

Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.

судя из описания вы перешли на mongo для решения конфликтов во время записи, в остальном вы полагаетесь в системе на туже eventual consistency при чтении реплик и доставку апдейтов через веб-сокеты. Mongo кластер, о котором вы говорите для скейла не умеет multimaster writes(на шарду всегда одна нода пишет), по сути работает так же как и riak в multimaster = false и имеет похожие механизмы разрешения конфликтов в таком режиме с mongo. кроме того riak тут выглядит богаче по возможностям, что умеет consistent hashing, в отличие от монги. можете детальней рассказать зачем пришлось менять базу в таком случае?

что будете делать если например попросят сделать репортинг с mongo primary storage — будете опять менять базу?

судя по описанию дизайна «хронографа» вы писали много low level инфраструктуры и велосипедили — похожим образом на то что вы описали работает например
www.quartz-scheduler.org/...​BCJobStoreClustering.html
кроме того есть готовые state machine libs, что подерживают кластеризацию.
Почему решили писать свои low-level решения, вместо взять готовые и отлаженные oss либы?

1) на eventual consistency не полагаемся потому что чтение всегда идет из мастера. Т.е. по факту strong consistency. Чтение из слейвов возможно для некоторых ендпойнтов, но на данный момент в этом нет необходимости.
В данный момент нам нет необходимости в multimaster writes. Вопрос производительности записи вообще не является на данный момент проблемой. Практика прошлой системы в которой это было возможно показало что это очень плохое решение с которым очень сложно жить и очень сложно разрешать конфликты бизнес логики в случае конкурентных обновлений одного обьекта в разных мастера нодах.
Меняли основное хранилище как раз для того чтобы гарантировать конситентность записи и отказоусточивость без заметной деградации сервиса. Почему выбрали именно MongoDB — факторов много и надо писать возможно отдельно статью на эту тему. Из простых примеров — очень просто реализовали сохранение снепшотов истории изменений обьектов и возможность посмотреть полное состояние какого то аукциона в любой момент времени в прошлом и например возможность откатить состояние базы на любой момент времени в течении прошедших суток (на случай восстановления при гипотетическом повреждении данных во всем репликасете)

2)

судя по описанию дизайна «хронографа» вы писали много low level инфраструктуры и велосипедили

На самом деле все велосипеды связанные с базой можно посмотреть вот здесь — gitlab.prozorro.sale/...​orro_sale/procedure/db.py
Как видите все велосипеды уложились в 400 строчек питон кода, и то часть из них относится к необязательному сахару который не нужен для работы ядра системы.

Как раз из плюсов текущего решения я бы отметил то что 80% процентов сложности системы связанно с запутанной бизнес логикой (которая запутана по ТЗ), общие вопросы работы с базой как раз очень просты, занимают буквально 10% сложности и в 99% случаев все просто работает. И работа с базой например никак не меняется при разработке нового функционала.

что будете делать если например попросят сделать репортинг с mongo primary storage — будете опять менять базу?

Можно чуть более детализировать вопрос? что именно вы понимаете под репортингом?

На самом деле все велосипеды связанные с базой можно посмотреть вот здесь — gitlab.prozorro.sale/...​orro_sale/procedure/db.py

под low-level инфраструктурой я предполагал сам механизм «хронографа» что должен убрать конкуретный запуск, а не врапер для mongo клиента;

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

я бегло глянул в ваш код — сразу возник вопрос, на цикл прохода итерации же влияет не только нагруженость и количество данных что попадают в обработку — вам надо исключить полностью конкуретный проход это критично для системы, есть другие сложно детерминированные факторы временные, что могут растянуть итерацию на более чем 1 секунду на уровне software, hardware(сетевые/дисковые latency), что тогда произойдет, данные покораптятся с таким решением?

Можно чуть более детализировать вопрос? что именно вы понимаете под репортингом?

например запросят простой дешборд-api со статистикой продаж, возможностью фильтрации по различным периодам, группировок по участникам и какими-то total/sub-total цифрами.

я бегло глянул в ваш код — сразу возник вопрос, на цикл прохода итерации же влияет не только нагруженость и количество данных что попадают в обработку — есть другие сложно детерминированные факторы временные, что могут растянуть итерацию на более чем 1 секунду на уровне software, hardware(сетевые/дисковые latency), что тогда произойдет, данные покораптятся с таким решением?

Хорошее замечание :). Данные внутри системы не будут испорчены т.к. используется механизм CAS при апдейте. 2-й апдейт упадет при попытке записать данные и не будет перезапущен т.к. таймер уже сдвинут первым. При этом возможны нежелательные сайдэффекты если в процессе делались вызовы на внешние сервисы и они уже изменили свое состояние.
Для решения этой проблемы мы внедрили:
1) Мониторинг этого сервиса. Мониторинг показывает что при нормальной работе системы обычный тик хронографа занимает до 50ms времени так что запас по времени на latency там большой.
2) Апи внешних (с точки зрения хронографа) сервисов сделано идемпотентным и поэтому в случае двойной обработки запроса ничего критичного не произойдет

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

например запросят простой дешборд-api со статистикой продаж, возможностью фильтрации по различным периодам, группировок по участникам и какими-то total/sub-total цифрами.

Статистика продаж это все таки больше к конкретно площадке брокера чем к центральному АПИ, но идею я понял. Большую часть подобных отчетов можно построить используя например aggregation фреймворк из MongoDB. Для тех случаев когда его не будет хватать можно сделать зеркало данных в какое то другое хранилище. В частности в статье упоминается механизм зеркалирования который также был написан, и он используется внутри системы в том числе например для того чтобы держать актуальное зеркало публичных данных в Elasticsearch для обеспечения полнотекствого поиска

Хорошее замечание :). Данные внутри системы не будут испорчены т.к. используется механизм CAS при апдейте. 2-й апдейт упадет при попытке записать данные и не будет перезапущен т.к. таймер уже сдвинут первым. При этом возможны нежелательные сайдэффекты если в процессе делались вызовы на внешние сервисы и они уже изменили свое состояние.
Для решения этой проблемы мы внедрили:
1) Мониторинг этого сервиса. Мониторинг показывает что при нормальной работе системы обычный тик хронографа занимает до 50ms времени так что запас по времени на latency там большой.
2) Апи внешних (с точки зрения хронографа) сервисов сделано идемпотентным и поэтому в случае двойной обработки запроса ничего критичного не произойдет

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

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

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

Большую часть подобных отчетов можно построить используя например aggregation фреймворк из MongoDB.

врядли это правильный взгляд на проблему, я вообще первый раз слышу что-то к серьез предлагает aggregations юзать под эти задачи — скорее похоже на то, что вы стараетесь достаточно универсально использовать монгу или elastic там, где они требуют серьезных design trade-offs(вроде иметь одну коллекцию\индекс под конкретную задачу, что бы оно давало вменяемую производительность или вообще работало хоть как-то). я почему и спросил, что выбор пал на очень узкоспециализированные технологии где шаг в право шаг в лево упрется сразу в тонкую настройку и проблемы, с которыми прийдется работать отдельно.

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

Это время обработки одного обьекта. Соответственно один инстанс хронографа может тикать порядка 20-50 обьектов в секунду. На данный момент партицирование не нужно и не используется. Количество инстансов каждого сервиса и базы данных в том числе развернуто исходя из требований отказоустойчивости и работы системы в случае падения одного из железных серверов (там есть свои тонкости, но это не существенно).

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

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

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

Я бы не назвал MongoDB очень узкоспециализированным решением, монга весьма хорошо себя показывает в достаточно широком спектре задач. Особых проблем с ней нет если люди понимают что делают и как пользоваться своим инструментом. Основной недостаток это конечно другой подход к проектированию, и отсутствие транзакций (в свежих версиях они есть, но тут уже мое мнение — что если вам нужны multi document transactions то скорее всего вы неправильно используете монгу). Но вцелом как опять же упоминается в статье отталкивались во многом из опыта эксплуатации старой системы и этот опыт показывает что монга хорошо закрывает те задачи которые необходимо решать.

B догонку добавлю ссылочку на упоминаемый хронограф — gitlab.prozorro.sale/...​/procedure/chronograph.py
Даже 100 строк нет. Сомневаюсь что просто интеграция предложенного вами решения на джаве займет меньше 100 строк кода..

Ваша интерпретация CAP теоремы также вызывает вопросы. Опция «no partition tolerance» в реальности не предоставляется :-), поэтому относить реляционные СУБД в эту категорию не стоит. Любая реляционная СУБД, у которой есть репликация, ведет себя двумя способами, либо репликация асинхронная, и при потере узла/связностми вы можете начать читать не самые свежие данные, потому что свежие не успели доехать. Либо репликация синхронная, и при потере достаточно количества узлов вы потеряете возможность записи. То есть та же consistency vs availability.

Все это в иделе. В реальности, и скорее всего как раз применимо к MongoDB, у вас из CAP скорее всего нет ни одной буквы.

Либо репликация синхронная, и при потере достаточно количества узлов вы потеряете возможность записи. То есть та же consistency vs availability.

репликация синхронная, но и не стоит задача обеспечивать запись при потере большинства узлов. Достаточно гарантировать работоспособность при отказе 1/3 серверов БД. Это закрывает большинство проблем реальной жизни и гораздо лучше чем допустим было в старой системе.

Все это в иделе. В реальности, и скорее всего как раз применимо к MongoDB, у вас из CAP скорее всего нет ни одной буквы.

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

репликация синхронная, но и не стоит задача обеспечивать запись при потере большинства узлов. Достаточно гарантировать работоспособность при отказе 1/3 серверов БД. Это закрывает большинство проблем реальной жизни и гораздо лучше чем допустим было в старой системе.

Я писал про применимость CAP теорему к реляционным СУБД, но вы своим ответом показываете, что синхронная репликация в PostgreSQL для ваших задач подходит. То есть остается разобрать только модель данных.

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

А зачем мне смотреть ваш код для того, чтобы делать выводы о MongoDB, разве вы саму MongoDB модифицировали под ваши нужды? Или вы на клиенте пытаетесь компенсировать возможные проблемы с консистентностью в СУБД? Если нет, то с клиентской частью у вас может быть все ок. Мы же говорим про СУБД. То есть ваш клиент вполне корректно может попросить СУБД записать данные, СУБД скажет — ок, записала, но потом либо при чтении отдать старые данные, либо при отказе окажется, что «записала, но не совсем». Это если очень упрощенно.

Я понимаю, что статьи «как мы еще раз поменяли СУБД после ответа случайного чувака на форуме» скорее всего не будет, но если решите все-таки посмотреть на PostgreSQL, готов на общественных началах по видео поотвечать на вопросы «а как там сделать X?». Заодно и узнаете о моей компетенции :-)

Я писал про применимость CAP теорему к реляционным СУБД, но вы своим ответом показываете, что синхронная репликация в PostgreSQL для ваших задач подходит. То есть остается разобрать только модель данных.

Одна из причин почему была выбрана нереляционная база это как раз сложности с укладыванием модели данных в реляционные таблицы. Тут есть 2 возможных подхода:
1) Иметь одну таблицу и туда пихать 90% полей в одно json поле. Этот подход вполне имеет право на жизнь, но тогда собственно от реляционной базы практически не остается никаких преимуществ.
2) Иметь много таблиц и для них иметь схему в тех местах где это возможно и json в остальных. Но тогда возникает проблема с тем что эти таблицы надо джойнить между собой и временами по очень хитрым правилам. А потенциально эти джойны и транзакции могут стать проблемой если вдруг потребовалось бы масштабировать базу горизонтально и пришлось бы заметно переделывать работу с ДБ слоем при этом.

На данный момент задачу «шардирировать базу и увеличить пропускную способность системы в 2-10 раз» я бы оценил в пару недель работы.. в случае с PostreSQL это было бы гораздо сложнее.

То есть ваш клиент вполне корректно может попросить СУБД записать данные, СУБД скажет — ок, записала, но потом либо при чтении отдать старые данные, либо при отказе окажется, что «записала, но не совсем». Это если очень упрощенно.

Я вам посоветую посмотреть еще раз на диаграмму, там специально для этого указано что в проде (и собственно на всех остальных окружениях тоже) используется опция клиента write_concern == MAJORITY (docs.mongodb.com/...​eplica-set-write-concern). Если вам лень читать, то я могу вкратце подсказать что это гарантирует то что запись будет считаться успешной только если как минимум большинство серверов успешно примут запись и запишут ее в свой журнал, это конечно имеет заметные накладные расходы с точки зрения производительности, но исключает ситуации о которых вы говорите

Замена СУБД
Для решения вопроса консистентности очевидным ходом была смена СУБД. Переход на реляционную базу не рассматривался ввиду профиля данных (один аукцион — одна структура), который хорошо согласуется с логикой нереляционной базы.

Такая логика также хорошо согласуется с реляционными базами данных. В PostgreSQL можно, например, хранить общие поля в типизированных атрибутах, а вариативные — в JSONB.
При этом можно задействовать все можности SQL для работы с данными на стороне СУБД, индексирования (в том числе и отдельных атрибутов JSONB), воспользоваться валидацией данных также на стороне СУБД и многими другими преимуществами этой СУБД.

Выбор остановился на MongoDB. Основными аргументами в пользу этой СУБД стала гарантированная консистентность данных, а также нативная система репликации. На тестовом стенде подтвердилась как скорость синхронизации инстансов без дополнительных средств, так и эффективное восстановление при отказе одной из нод.
Результат замены — повышение отказоустойчивости в целом и существенное упрощение процесса синхронизации площадок с ЦБД.

Каким образом вы проверяли «гарантированную консистентность»? Что вы подразумеваете под консистентностью данных, применимо к MongoDB (у которой, кстати, default isolation level «read uncommitted»)? Как вы проверяли, что нативная система репликации работает и чтение/запись в СУБД будет масштабироваться в будущем? Что вы будете делать, если у вас возникнут проблемы с СУБД?

Мои вопросы неспроста. MongoDB перекомпенсирует огрехи своего дизайна повсеместным маркетингом. В мире СУБД таким вещам как «гарантированная консистеность» нельзя верить на слово

у которой, кстати, default isolation level «read uncommitted»

Да и durability по умолчанию отключена, но нужен был хайп для раскрутки, как она «делает» на записи реляционки.

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

Каким образом вы проверяли «гарантированную консистентность»? Что вы подразумеваете под консистентностью данных, применимо к MongoDB (у которой, кстати, default isolation level «read uncommitted»)?

Транзакции в MongoDB в системе не используются т/к/ обновления всегда прозводятся в рамках одного документа. Именно это гарантирует консистентность данных. Дополнительно это позволит при необходимости легко масштабировать систему т/к/ ее возможно легко разбить на шарды и нет необходимости в обьединении данных из нескольких шардов или распределенных транзакций.

Понятно что подходы к проектированию приложений с реляционными базами и с NoSQL базами отличаются. И очень часто я к сожалению видел и достаточно неправильное использование MongoDB которое убивает все ее преимущества. Но в основном это вызвано тем что люди пытаются свои привычные по реляционным базам схемы наложить на нереляционную базу и конечно это не работает. Конкретно в нашем случае практика показывает что выбранный подход работает достаточно хорошо и не создает проблем именно на уровне хранилища.

Подробные результаты нагрузочного тестирования приводить не буду, но тесты показывают что система в данной конфигурации может держать примерно в 300 раз большую нагрузку чем фактически была в ЦБД-2 и эти числа при желании можно еще увеличить

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

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

А на первом месте (если кому интересно) цитата из книги Brian Gorman «Practical Entity Framework»

Another important concept with the use of code-first database development is that we are making a conscious transition to imperative database programming and saying goodbye to declarative programming around our database.

я другий, відразу за якимось чуваком з форбс. непогано!

Спасибо за ответ. А то я уже подумал, что слишком резко высказался :-)

А по поводу применения реляционной базы для вашего проекта я ответил здесь: dou.ua/...​te-prozorro-sale/#2023007

я другий, відразу за якимось чуваком з форбс. непогано!

Да нет, чувак из Forbes — это другой Brian Gorman (как оказалось, этих Горманов в Америке как собак нерезаных). Я этого из Forbes не читал, но думаю, что он еще большую чушь пишет, чем специалист по Entity Framework.

CAP-теорема: невозможно одновременное достижение всех трех составляющих — целостности, распределённости и высокой доступности

Это не совсем так
«CAP Twelve Years Later: How the „Rules“ Have Changed», chapter «Why „2 of 3“ is missleading»

Все равно потом будете переделывать. Инфа соточка.

точно про це замислимось коли авс/ажур/гк отримають ксзі :/

Нещодавно була стаття щодо можливих інвестицій MS, точніше, про наміри.
ain.ua/...​tiruet-500-mln-v-ukraine
dou.ua/forums/topic/31687

Цікаво, якою у Вас була вартість та обсяги використання AWS S3 і наскільки відрізняються поточні витрати, без S3.

абсолютні витрати зараз вище. але порівнювати насправді некоректо: в AWS ми жили на pay-as-you-go, а в Україні — маємо резервовані ресурси, яких, звісно, з запасом, і які від того дорожчі

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

как раз on-prem стораджи традиционно блочные/файловые, объектные — очень редко, с поддержкой S3-протокола — тем более

min.io, все нужно по протоколу S3

Minio’s estimated annual revenue $7M per year
Hitachi Vantara Revenue: $2 Billion per year
NetApp annual revenue for 2019 was $6.146B

каждый суслик в своем поле агроном, тут кому с чем проще и чего достаточно для решения бизнес задач

протокол S3 — это не бизнес-задача, если ты не сотрудник Amazon
очевидно, что после ванвей миграции на украинского «гиперскалера» (чтобы данные лежали локально, как того требует законодательство) — протокол просто становится ненужным и лишним
Minio, Ceph, etc — нет смысла ставить поверх арендуемых ресурсов, уже расположенных на стораджах NetApp, EMC, ...

з самим S3 все добре. складно з тим, що:
— фізично сховище десь на Огайщині. А має бути, наприклад, на Житомирщині (так хоче закон)
— в старих ЦБД чомусь було вирішено зберігати прив’язку до абсолютних лінків. Колеги із Закупівель декілька місяців писали і тестували мігратор, який дозволив це виправити

Дякую за статтю, дуже корисна для загального розвитку.
Підкажіть, будь ласка, чи не планується створення якогось майданчика на Прозорро для наукових грантів? Prozorro.Grant чи щось подібне.

Прозорро.Продажі починався на чистому ентузиазмі маленької команди. якщо ви вірите в те, що Prozorro.Grant може працювати — не існує жодної причини його не розпочати власноруч! — Prozorro.University, де б готували запускаторів Прозорро, поки що теж не вигадано :)

Для работы с метриками: Loki.

подозреваю, что таки с логами
но зачем, если заявлен Elasticsearch — решительно непонятно

еластік використовується для пошуку бізнес-даних. а локі — для роботи інженерів з логами (так, звісно). прийшли до такого коли почали ресурси рахувати

Очень тяжело читать спокойно, если честно. Практически всё описанное Григорием, в том числе и изменения в архитектуре — это классические примеры того, как делать не надо. Хотя аргументация правильная, но принятые решения вызывают вопросы.
У меня сложилось впечатление, что у разработчиков и архитекторов проекта было просто желание поработать с определёнными технологиями.
Думаю, через несколько лет мы увидим статью о следующее итерации жизни данной системы.

радий що почитали :)
а можна плз предметно: що саме ви б робили інакше, і чому?

Думаю причина в том что Евгений специалист по MSSQL и поэтому любое бесплатное решение для него априори неправильно ;)

Наивно полагать, что MSSQL — это единственная СУБД с которой я работаю и везде продвигаю. Отличное решиние для своих задач, но не универсальное.
Вам же я рекомендовал бы познакомиться с такими терминами как ROI (return on investment) и TCO (total cost of ownership). Неожиданно окажется, что многое, что вы считаете бесплатным, Алексей, не только таким не является, а может быть довольно дорогим.
Что же касается комментариев, то, боюсь, то критики довольно много, тут нужно писать целую статью на эту тему. Если Григорий присоединится к киевскому клубу архитекторов — можно будет обсудить в живую.

Думаю вам стоит для начала ознакомится все таки более подробно с решаемой задачей. Можно ли было ее решить допустим с помощью PosgreSQL — конечно. Было бы такое решение проще или удобнее? не думаю. Как указал в статье Григорий система поддерживает множество видов торгов которые отличаются друг от друга набором полей. Соответственно реляционная база не очень подходит для такого проекта (ну если не пихать все поля в джейсон поле PostgreSQL). Из нереляционных MongoDB обеспечивает наилучшие возможности которые закрывают 100% потребностей. Критику я бы тоже с удовольствием почитал, но для этого критика должна быть все таки обоснована и исходить от человека который все таки разбирается в проблеме, а не просто критикует десяток ключевых слов которые ему почему то не нравятся.

Я не верю, что 100% полей каждого аукциона отличается от другого аукциона, скорее всего достаточно много из них общие и их можно типизировать, получив контроль целосности данных на стороне СУБД. При этом вариантивную часть можно хранить в виде JSONB. Но даже если все поля хранить в JSONB, производительность будет все равно выше, при большей надежности хранения данных.

100% конечно нет, но отличия достаточно значительны чтобы вызывать затруднения. Контроль целостности данных в любом случае надо проводить в коде на этапе валидации входных данных по бизнес логике, т.е. наличие дублирования этого контроля на уровне СУБД особых преимуществ не дает. Производительность на данный момент с большим запасом покрывает реальные потребности поэтому особый упор на это не делали (больше времени отработки запроса занимает сериализация/десериализация данных). Надежность хранения данных вполне достаточна, MongoDB работает в режиме репликасета и с включенным журналированием поэтому тоже не вижу почему могут возникнуть проблемы. Отчасти выбор именно нереляционной базы был обоснован тем что это позволяло использовать наработанный опыт старой системы и обеспечить неплохую «идейную» совместимость со старым кодом и перенести оттуда ряд наработок.

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

Реляционная база хорошо подходит и для такого проекта. Отличающиеся наборы полей можно хранить, используя паттерн EAV (Entity-Attribute-Value). Подробности здесь: dou.ua/...​rums/topic/31052/#1900326

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

Но это простейший вариант, в реальности использовалось около 30 таблиц.

И очень вероятно требует монструозных запросов для доступа к данным, из вашего же примера:
select attr.Name as Attribute_Name,        attr_val.Value as Attribute_Value   from Attribute attr inner join Product_Attribute_Value attr_val     on attr.Attribute_Id = attr_val.Attribute_Id   where attr_val.Product_Id = <...>
Кроме того

высоконагруженных (сотни запросов в секунду).

не кажется мне примером реально нагруженной системы если честно. Я готов считать систему высоконагруженной если она выдает хотя бы 1000 RPS на запись и 10К на чтение.

Подумайте ради интереса в какую структуру данных вы бы сохранили вот примерно вот такой бизнес обьект — procedure.prozorro.sale/...​/5fa271b50127a2f996543106

Что-то гугл не находит, что такое «Swif DB»

я з ним згоден: по-перше, мова не про СУБД, а про об’єктне сховище (S3-лайк), а по-друге — він Swift :) дякую, що звернули увагу

Підписатись на коментарі