Помогите правильно спроектировать микросервисное приложение
От: busk  
Дата: 20.03.25 14:46
Оценка:
Привет всем.
Как-то только в теории читал про микросервисы, но вот новый проект планируется небольшой и хотел как раз попробовать микросервисы тут.

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


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

Приложение: — Система контроля доставок грузов в разных странах (стран 10).
— Юзеры заходят по user/pswd и система определяет страну и подгружает данные этой страны. Новый юзер сам может зарегаться по логину выданному ему и восстановить пароль.
— В системе будет 4 роли: sysadmin, admin, manager, driver. В каждой стране свои пользователи. Один пользователь = 1 страна.
— Сами заказы поступают в эту систему из другой. В этой надо только мониторить и делать разные действия если доставка задержалась или не приехала.
— В каждой стране много складов и поэтому когда заказы поступают в систему то они сразу попадают на нужный склад и спец алгоритм распределяет на водителей.

— sysadmin управляет только техническими настройками разными (кому когда отчеты отсылать, как в системе заводить оптуска, сколько часов в сутки можно работать, заводит праздники), остальное всё только в режиме просмотра видит.
— admin видит всё, не может только технические настройки менять. Следит за водителями, чтобы они всрок доставляли всё — по юаю специальному, вносит отпуска, больничные их, ставит им рабочее время. Если водитель увольняется — удаляет изи системы.
— manager следит за водителями своего склада только, может посмотреть по всем своим водителям информацию и по всем доставкам, но менять не может
— driver видит только по себе все сделанные доставки и предстоящие. Каждый раз когда он начинает и заканчивает доставку то в системе нажимает Start delivery\End delivery. Если доставка не укладывается в
предполагаемые сроки то пишет к доставке комментарий почему не успел


Итого что по функционалу:
Sysadmin — логин в систему, управление настройками, праздниками и просмотр всего.
Admin — логин в систему, cледит за водителями, чтобы они всрок доставляли всё, вносит отпуска, больничные их, ставит им рабочее время. Если водитель увольняется — удаляет изи системы.
Manager логин в систему, следит за водителями своего склада только, может посмотреть по всем своим водителям информацию и по всем доставкам
Driver логин в систему, видит только по себе все сделанные доставки и предстоящие. Каждый раз когда он начинает и заканчивает доставку то в системе нажимает Start delivery\End delivery. Если доставка не укладывается в
предполагаемые сроки то пишет к доставке комментарий почему не успел


Третий вопрос. Если без докеров то как настроить этот набор микросервисов? тут их немного будет и читал что можно настроить без докер контейнеров если что.
Re: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 20.03.25 15:32
Оценка: +5 :)
Здравствуйте, busk, Вы писали:

B>Как-то только в теории читал про микросервисы, но вот новый проект планируется небольшой и хотел как раз попробовать микросервисы тут.

Не надо, оно тебя сожрет
Re[2]: Помогите правильно спроектировать микросервисное приложение
От: busk  
Дата: 20.03.25 15:34
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Здравствуйте, busk, Вы писали:


B>>Как-то только в теории читал про микросервисы, но вот новый проект планируется небольшой и хотел как раз попробовать микросервисы тут.

G>Не надо, оно тебя сожрет

поясни пожалуйста подробней. а то смотришь тот же Яндекс, у них везде микросервисы + ci/cd
Re: Помогите правильно спроектировать микросервисное приложение
От: DiPaolo Россия  
Дата: 20.03.25 15:40
Оценка: 2 (1)
Выглядит так, что тут вкореживать (именно так) микросервисы – дороже выйдет. Ну вообще никак они тут не просматриваются. Очень маленький набор сущностей и функционала. Элементарно нечего пилить

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

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

Тогда может и будет понимание, где надо (и зачем!), а где не надо впихивать микросервисы. Ну и про кубер придет понимание, что этим зоопарком баз и сервисов как-то надо рулить. И как замечательно можно скейлить отдельные куски системы по необходимости
Патриот здравого смысла
Re: Помогите правильно спроектировать микросервисное приложение
От: Miroff Россия  
Дата: 20.03.25 15:42
Оценка: +1
Здравствуйте, busk, Вы писали:

B>Тут я понимаю, что проще было бы сделать монолит, но для обучения вполне годный вариант?


А у вас ресурсы-то на микросервисы есть? На тот же K8S и CI/CD хотя бы.

B>Второе и собственно основное я так понял из книг, что самое сложное это определить границы сервисов. Вот прошу тут помощи как нарезать на сервисы и базы?


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

B>Третий вопрос. Если без докеров то как настроить этот набор микросервисов? тут их немного будет и читал что можно настроить без докер контейнеров если что.


Можно, только это очень дорого и даже Яндекс себе такого не может позволить.
Re[2]: Помогите правильно спроектировать микросервисное приложение
От: busk  
Дата: 21.03.25 04:18
Оценка:
Здравствуйте, Miroff, Вы писали:

M>Здравствуйте, busk, Вы писали:


B>>Тут я понимаю, что проще было бы сделать монолит, но для обучения вполне годный вариант?


M>А у вас ресурсы-то на микросервисы есть? На тот же K8S и CI/CD хотя бы.


я планировал без кубера, читал так можно. Ниже написал модель какую думал. ci/cd у нас есть на другом сервере и думал туда добавить билд нового проекта.
а кубер реально много ресурсов требует дополнительно? я думал он просто как failover service


B>>Второе и собственно основное я так понял из книг, что самое сложное это определить границы сервисов. Вот прошу тут помощи как нарезать на сервисы и базы?


M>Это самое простое. Делить нужно по бизнес-домену (мыши и кактусы отдельно, марксизм и диамат отдельно), по технологиям (ML на питоне, интеграция с внешними сервисами на Scala, интерфейс к БД на сишарпе), требованиям к масштабированию (фронтофисом пользуется вся страна, а бэкофисом 20 человек), надежности (если отчеты упадут об этом узнают через месяц, а если торговый робот упадет, компания обанкротится за 5 минут). Если ничего этого нет, микросервисы не нужны.


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

B>>Третий вопрос. Если без докеров то как настроить этот набор микросервисов? тут их немного будет и читал что можно настроить без докер контейнеров если что.


M>Можно, только это очень дорого и даже Яндекс себе такого не может позволить.


а почему дорого? я так понял просто каждый сервис — отдельный апп в веб сервере и есть шлюз приложение которое регулирует и каждая база на свой сервис.
Re[2]: Помогите правильно спроектировать микросервисное приложение
От: busk  
Дата: 21.03.25 04:37
Оценка:
Здравствуйте, DiPaolo, Вы писали:

DP>Выглядит так, что тут вкореживать (именно так) микросервисы – дороже выйдет. Ну вообще никак они тут не просматриваются. Очень маленький набор сущностей и функционала. Элементарно нечего пилить


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


DP>Потренироваться можно на пет-проекте. Самому придумать и написать что-то несложное. Один микросервис отдает погоду, второй загруженность дорог. Третий все это забирает и что-то отдает наружу. Плюс отдельный микросервис принимают от пользователей какие-то данные. Тут же добавить кэши, обратный прокси. можно потом с лоад балансером поиграться. Каждый сервис пусть по базе имеет. Сделать несколько инстансов каждого микросервиса. Ну и так далее...


DP>Тогда может и будет понимание, где надо (и зачем!), а где не надо впихивать микросервисы. Ну и про кубер придет понимание, что этим зоопарком баз и сервисов как-то надо рулить. И как замечательно можно скейлить отдельные куски системы по необходимости


Понял, спасибо. Монолит тогда сделаю.

А так вот, чисто для развития и понимания в реальных приложениях, подскажи пожалуйста как тут нарезать сервисы?

дополнительно. Еще будет 3 сервиса на питоне: один будет из 1с качать информацию по отпускам, болничным, один будет также из 1с качать новых сотрудников.

я правильно понимаю: сервис аутенфикации, сервис получения заказов со статусами по дням, сервис настроек и сервис сотрудников (где можно им выставлять рабочее время, заводить отгулы, удалять их если они уволились)
Re[3]: Помогите правильно спроектировать микросервисное приложение
От: DiPaolo Россия  
Дата: 21.03.25 05:11
Оценка:
B>А так вот, чисто для развития и понимания в реальных приложениях, подскажи пожалуйста как тут нарезать сервисы?
Да вот именно, что сложно их как-то нарезать. Как коллега правильно заметил, есть несколько вариантов нарезки, и ни один из них сюда не подходит.

Можно нарезать по сущностям, теоретически: сервис юзеров, сервис заказов и так далее. Но это скорее запутает и микросервисы покажутся злом

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

B>дополнительно. Еще будет 3 сервиса на питоне: один будет из 1с качать информацию по отпускам, болничным, один будет также из 1с качать новых сотрудников.


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

B>я правильно понимаю: сервис аутенфикации, сервис получения заказов со статусами по дням, сервис настроек и сервис сотрудников (где можно им выставлять рабочее время, заводить отгулы, удалять их если они уволились)


Я бы сделал так:
— основной бэк (заказы, настройки, сотрудники)
— сервис аутентификации (в вашем случае это тоже лишнее — выносить в отдельный микросервис; но пусть, так часто делают, когда микросервисов много и/или есть SSO)
— сервис для работы с 1С
Патриот здравого смысла
Re: Помогите правильно спроектировать микросервисное приложение
От: DiPaolo Россия  
Дата: 21.03.25 05:17
Оценка:
B>Третий вопрос. Если без докеров то как настроить этот набор микросервисов? тут их немного будет и читал что можно настроить без докер контейнеров если что.

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

Сделать можно так: скрипт, который запускает все сервисы + СУБД должна быть запущена (ее проще всего поднять в докере как раз) и базы созданы.

При этом надо продумать: а как определять, что тот или иной сервис упал/недоступен?

Далее, если понадобится вторая машина, тут встанет тоже вопрос, как с этим управляться.

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

Кстати, кубер сразу не стоит брать. Достаточно будет привнести докеры, а потом Docker compose. Этого хватит. А уже потом – кубер.
Патриот здравого смысла
Re[2]: Помогите правильно спроектировать микросервисное приложение
От: busk  
Дата: 21.03.25 07:32
Оценка:
Здравствуйте, DiPaolo, Вы писали:

B>>Третий вопрос. Если без докеров то как настроить этот набор микросервисов? тут их немного будет и читал что можно настроить без докер контейнеров если что.


DP>Что касается докера и кубера... это хорошая идея – начать без них. Тогда чуть позже придет понимание исходя из реальных потребностей, зачем один и второй нужен.



а я кстати тут вот понял, что без кубера и докера у меня бы точно были проблемы. Читал, что вроде нормально это всё под линухом работает.
а у меня на проде венда + mssql.

За советы спасибо!

видимо, да, домашний проект надо под линукс + postrges
Re[3]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 21.03.25 08:08
Оценка: +1
Здравствуйте, busk, Вы писали:

B>Здравствуйте, gandjustas, Вы писали:


G>>Здравствуйте, busk, Вы писали:


B>>>Как-то только в теории читал про микросервисы, но вот новый проект планируется небольшой и хотел как раз попробовать микросервисы тут.

G>>Не надо, оно тебя сожрет

B>поясни пожалуйста подробней. а то смотришь тот же Яндекс, у них везде микросервисы + ci/cd


1) Ты не яндекс, даже на 1% не яндекс, и никогда яндексом не станешь
2) яндекс на старте не был микросервисным
3) cd\cd без микросервисов работает лучше
Re[4]: Помогите правильно спроектировать микросервисное приложение
От: busk  
Дата: 21.03.25 08:25
Оценка:
Здравствуйте, gandjustas, Вы писали:


G>2) яндекс на старте не был микросервисным


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

G>3) cd\cd без микросервисов работает лучше


звучит что микросервисы удел либо больших компаний либо крупных систем.
Re: Помогите правильно спроектировать микросервисное приложение
От: bnk СССР http://unmanagedvisio.com/
Дата: 21.03.25 09:41
Оценка: +1
Здравствуйте, busk, Вы писали:

B>Второе и собственно основное я так понял из книг, что самое сложное это определить границы сервисов. Вот прошу тут помощи как нарезать на сервисы и базы?


Они проходят по границам команды. Одна команда — один микросервис.
Re[3]: Помогите правильно спроектировать микросервисное приложение
От: Miroff Россия  
Дата: 21.03.25 11:23
Оценка:
Здравствуйте, busk, Вы писали:

B>я планировал без кубера, читал так можно. Ниже написал модель какую думал. ci/cd у нас есть на другом сервере и думал туда добавить билд нового проекта.


Можно, но микросервисы плохо работают без полноценного CD. Релизить руками умаешься.

B>а кубер реально много ресурсов требует дополнительно? я думал он просто как failover service


Ресурсов в смысле админа, который умеет k8s поднять и способен его настроить. Там же не один k8s, а еще сбор логов, CD пайплайны, мониторинг и т.п.

B>Но я так понял что поддержка и настройка микросервисов дело хлопотное и надо делать в кубере, а если без опыта то времени на изучение и настройку уйдет с неделю точно.


Микросервисы позволяют пернести часть сложности из приложения в оркестрацию. Это сильно упрощает разработку, когда у тебя в сервисе меньше 10к строк и один разработчик. Нет ни мердж конфликтов, не нужно ни с кем договариваться, не нужна документация, планирование, архитектура. Выставляешь Restful API и проблема других сервисов как этим API воспользоваться. Если в проекте энфорсится RMM level 3 и стопроцентная обратная совместимость, можно делать проекты на 1000 человеко-лет с минимальным оверхедом. За это приходится расплачиваться необходимостью контроллировать сложность взаимодействия. Вплоть до экономических механизмов, когда каждая команда платит за ресурсы потребленные их микросервисами в облаке.

B>а почему дорого?


Потому что k8s и docker сейчас умеют даже студенты, а для рукопашного CD нужны крайне продвинутые админы, умеющего автоматизировать развертывание каким-нибудь Chef или Puppet. Даже когда Chef был на пике моды, таких были единицы.

B>я так понял просто каждый сервис — отдельный апп в веб сервере и есть шлюз приложение которое регулирует и каждая база на свой

сервис.

Тогда не получится ни масштабирования, ни rolling updates без даунтайма, ни надежности. Непонятно зачем тогда вообще микросервисы?
Re: Помогите правильно спроектировать микросервисное приложение
От: Qulac Россия  
Дата: 21.03.25 12:43
Оценка:
Здравствуйте, busk, Вы писали:

B>Привет всем.

B>Как-то только в теории читал про микросервисы, но вот новый проект планируется небольшой и хотел как раз попробовать микросервисы тут.

Я потренировался бы сначала, прежде чем делать реальный проект на микросервисах или в крайнем случае вывел бы в сервисы какую ни будь не критичную часть функционала, типа хранения разных типов уведомлений для пользователей.
Программа – это мысли спрессованные в код
Re: Помогите правильно спроектировать микросервисное приложение
От: SkyDance Земля  
Дата: 21.03.25 17:09
Оценка:
B>самое сложное это определить границы сервисов. Вот прошу тут помощи как нарезать на сервисы и базы?

Это как раз самое простое. Границы определяются orgchart — "организационной диаграммой". Иными словами, разделением ответственности по принципу "если это делает другая команда, это другой сервис".

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

PS: не видел еще ни одного продукта с микросервисной архитектурой который бы не скатился именно в этот принцип разделения. Во многих случаях разделение идет дальше (вплоть до того, что каждому программисту — по микросервису, а то и два-три), но в этом случае нередок обратный процесс помещения нескольких микросервисов внутрь единого огромного бинарника (привет фейсбуку).
Re: Помогите правильно спроектировать микросервисное приложение
От: L_G Россия  
Дата: 22.03.25 13:17
Оценка:
Даже подразумевая, что всё это делается лишь для тренировки/обучения и не более —
больше, чем на две базы данных описанное разделить крайне трудно. Получается 1) аутентификация и авторизация 2) всё остальное.
Тем более, сервис аутентификации и авторизации вполне логично сделать централизованным (общим на весь корпоративный зоопарк приложений).
А сервисов больше, чем баз делать может иметь смысл только когда их реально делают разные команды (я не про экземпляры одинаковых).
Думаю, и 2 сервисов должно вполне хватить для отработки основных навыков.
Каша в голове — пища для ума (с)
Re: Помогите правильно спроектировать микросервисное приложение
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 22.03.25 23:52
Оценка: 9 (1)
Здравствуйте, busk, Вы писали:

B>Как-то только в теории читал про микросервисы, но вот новый проект планируется небольшой и хотел как раз попробовать микросервисы тут.

Если это реальный проект за деньги, то лучшее делать его на базе известных подходов, например монолит на базе хексогональной архитектуры.
B>Первое что хотел сказать, что везде пишут микросервисы это где много команд часто или много функционала.
Это не основное их назначение. В монолите точно также можно делить всё на разные компоненты между командами.
B>но зато скоп определен четко и подумал прекрасная возможность потренироваться, чтобы потом уверенее пробовать в большом проекте.
Проект, честно скажем, небольшой.
B>Итого что по функционалу:
Ты забыл внешние системы такие как 1С и систему-поставщика заказов.

B>Третий вопрос. Если без докеров то как настроить этот набор микросервисов?

Это будет сложно. Можно на этом проекте поизучать контейнеризацию на базе докера и засунуть свой монолит и базу в отдельные контейнеры, настроить между ними сетевую связанность и подключить хранилища, подумать как будешь делать бэкап этой базы.
Для микросервисной архитектуры нужно ещё продумывать системный дизайн и процесс деплоя.
Sic luceat lux!
Re[3]: Помогите правильно спроектировать микросервисное приложение
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 23.03.25 03:01
Оценка:
Здравствуйте, busk, Вы писали:

G>>Не надо, оно тебя сожрет


B>поясни пожалуйста подробней. а то смотришь тот же Яндекс, у них везде микросервисы + ci/cd


В том же яндексе в каждом микросервисе ад и Израиль, оно тебе надо?
Маньяк Робокряк колесит по городу
Re[5]: Помогите правильно спроектировать микросервисное приложение
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 23.03.25 03:04
Оценка:
Здравствуйте, busk, Вы писали:

B>звучит что микросервисы удел либо больших компаний либо крупных систем.


Монолит в одно жало/одной командой пилить гораздо проще.

Когда дорастёшь до того, что начнут возникать проблемы с монолитом — ты уже будешь нанимать программистов по 500 рублей и на архитектуру (монолит/микросервисы) тебе уже будет начхать
Маньяк Робокряк колесит по городу
Re[3]: Помогите правильно спроектировать микросервисное приложение
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 23.03.25 03:08
Оценка:
Здравствуйте, busk, Вы писали:

B>а я кстати тут вот понял, что без кубера и докера у меня бы точно были проблемы. Читал, что вроде нормально это всё под линухом работает.

B>а у меня на проде венда + mssql.

Докер под виндой десяткой вроде работает без проблем
Маньяк Робокряк колесит по городу
Re[4]: Помогите правильно спроектировать микросервисное приложение
От: m2user  
Дата: 23.03.25 07:31
Оценка:
M>Это сильно упрощает разработку, когда у тебя в сервисе меньше 10к строк и один разработчик.
M>Нет ни мердж конфликтов,

+1

M>не нужно ни с кем договариваться, не нужна документация,


Как минимум нужно предоставить документацию на API и получить feedback

M> планирование, архитектура.


слабо верится, и над архитектурой нужно тоже думать, и планировать (хотя бы по срокам имплементации этого самого API)

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


Так это вроде и без микросервисов всегда так было. Есть проблемы с производительностью — отвечает автор кода, являющегося узким местом.
Re[5]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.03.25 17:14
Оценка: +5
Здравствуйте, busk, Вы писали:

B>Здравствуйте, gandjustas, Вы писали:



G>>2) яндекс на старте не был микросервисным


B>ну вот да, книжку читал и пишут, что большие проекты часто вначале делают монолитом, когда еще не всё ясно и четко по логике и потом типа уже распиливают на микросервисы.

И вообще не факт что это надо делать.


G>>3) cd\cd без микросервисов работает лучше

B>звучит что микросервисы удел либо больших компаний либо крупных систем.

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

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

В этом случае может быть выгодно иметь микросервисы как раз по границам команд. Других адекватных причин использовать MSA я не видел.
Re[5]: Каких программ вам не хватает?
От: Miroff Россия  
Дата: 24.03.25 14:35
Оценка:
Здравствуйте, m2user, Вы писали:

M>Как минимум нужно предоставить документацию на API и получить feedback


Прелесть RMM L3 в том, что он самодокументируемый и на 99% фидбека можно сразу отвечать: "это не RESTful, мы этого делать не будем"

M>слабо верится, и над архитектурой нужно тоже думать, и планировать (хотя бы по срокам имплементации этого самого API)


Так RMM L3 однозначный, там не над чем думать. Размышлений требует дефиниция ресурсов, но она в 90% случаев типовая и там опять же думать не над чем.

M>Так это вроде и без микросервисов всегда так было. Есть проблемы с производительностью — отвечает автор кода, являющегося узким местом.


Это вопрос не производительности, а потребляемых ресурсов. Микросервис суперпросто поднять, но суперсложно удалить. Например, компания, которая ввела внутренний биллинг, обнаружила себя тратящей несколько десятков миллионов в месяц на порядка тысячи микросервисов, которые непонятно кто и с какой целью поднял. Причем административными средствами проблема не решалась, потому что всегда оказывалось что микросервис поднял Вася пять лет назад для проекта X и за 5 лет Вася уволился, проект X влили в проект Y, который заменили на проект Z, в процессе два раза сменив команду и три раза сделав пивот на 180 градусов. И теперь концов уже не найти, нужен ли этот сервис СЕЙЧАС и если нужен, то ДЛЯ ЧЕГО.
Re[6]: Каких программ вам не хватает?
От: RushDevion Россия  
Дата: 24.03.25 18:32
Оценка:
M>Прелесть RMM L3 в том, что он самодокументируемый и на 99% фидбека можно сразу отвечать: "это не RESTful, мы этого делать не будем"

Чисто для общего развития.
RMM L3 — это когда, получив первый ресурс, мы по link/rel определяем, что с ним дальше можно сделать (и на какие url слать соответствующие запросы), так ведь?

Можно пример какого-нибудь публичного REST API, которое именно классический RMM L3 (т.е. не Graph QL и т.п.)?

Просто в моей микросервисной практике все попытки внедрить hypermedia в конечном итоге скатывались либо в полноценную Open API/Swagger спеку, либо в Graph QL, либо в текстовую документацию.
Отредактировано 24.03.2025 21:02 RushDevion . Предыдущая версия . Еще …
Отредактировано 24.03.2025 18:49 RushDevion . Предыдущая версия .
Re[7]: Каких программ вам не хватает?
От: Miroff Россия  
Дата: 25.03.25 01:30
Оценка:
Здравствуйте, RushDevion, Вы писали:

RD>RMM L3 — это когда, получив первый ресурс, мы по link/rel определяем, что с ним дальше можно сделать (и на какие url слать соответствующие запросы), так ведь?


Да, но не только. Это как с третьей нормальной формой, RMM L3, это RMM L2 + HATEOAS. В первую очередь хорошее API обеспечивается правильным разделением на ресурсы, с которыми можно оперировать с помощью HTTP глаголов. HATEOAS на это просто очень удачно ложится и позволяет не размывать логику между клиентом и сервером.

RD>Можно пример какого-нибудь публичного REST API, которое именно классический RMM L3 (т.е. не Graph QL и т.п.)?


PayPal API

RD>Просто в моей микросервисной практике все попытки внедрить hypermedia в конечном итоге скатывались либо в полноценную Open API/Swagger спеку, либо в Graph QL, либо в текстовую документацию.


HATEOAS не отменяет сваггера. Все равно нужно описывать семантику полей и действий.
Re[6]: Помогите правильно спроектировать микросервисное приложение
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.03.25 05:50
Оценка: +2
Здравствуйте, gandjustas, Вы писали:

G>В этом случае может быть выгодно иметь микросервисы как раз по границам команд. Других адекватных причин использовать MSA я не видел.


Ну ещё ограничить failure domain. Чтобы бага в одном углу приложения клала его не целиком, а только блокировала отдельные сценарии.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[8]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.03.25 06:07
Оценка: +2
Здравствуйте, Miroff, Вы писали:

M>Здравствуйте, RushDevion, Вы писали:


RD>>RMM L3 — это когда, получив первый ресурс, мы по link/rel определяем, что с ним дальше можно сделать (и на какие url слать соответствующие запросы), так ведь?


M>Да, но не только. Это как с третьей нормальной формой, RMM L3, это RMM L2 + HATEOAS. В первую очередь хорошее API обеспечивается правильным разделением на ресурсы, с которыми можно оперировать с помощью HTTP глаголов. HATEOAS на это просто очень удачно ложится и позволяет не размывать логику между клиентом и сервером.

Я не вижу каких-то прямо практически значимых преимуществ HATEOAS.
1. Discoverability немножко улучшается, но не очень значительно. Вот мы дёрнули какой-то ресурс GET-ом. Он прислал нам
— набор ссылок на объекты внутри своих свойств (типа "owner": "https://our-api-enpdoint/users/23412312-ws-44"}
— набор ссылок на "глаголы" в коллекции links (типа {"href": "https://api-m.paypal.com/v1/payments/sale/36C38912MN9658832/refund","rel": "refund","method": "POST"})
Дальше что? Всё равно, без чтения документации невозможно понять,
— можно ли в качестве owner использовать URL юзер-аккаунта в другом скоупе
— что делает глагол refund, и какие у него будут параметры, и где их брать
2. Evolvability улучшается крайне незначительно. У нас нет никакого способа научить клиентов всегда ходить только по указанным нами ссылкам. Если мы решили перенести endpoint для рефандов в другое место, то недостаточно просто отдавать новую ссылку в links. Примерно 99% разработчиков клиента зашьют логику генерации ссылки в своё приложение, вместо того, чтобы прикапывать URL для каждого платежа в своей системе (и тратить место; и опять же рисковать пойти по устаревшей ссылке) или там бегать всякий раз делать GET и искать этот ./links[@rel=refund] (и увеличивать нагрузку на сеть и латентность своих сервисов). Нам так или иначе придётся до каждого из них дойти и убедиться, что они переписали своё приложение
3. Накладные расходы значительно увеличиваются. Там, где в RMM L2 мы обходились GUID-ом, а то и вообще int-ом, теперь мы тащим длиннюшие урлы с совершенно бесполезными обрамлениями. Можно немножко починить это путём компрессии реквестов и респонсов, но всё равно — растёт allocation size для каждого запроса, учащаются сборки мусора, снижается общая эффективность, повышается расход электроэнергии.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Помогите правильно спроектировать микросервисное приложение
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.03.25 06:34
Оценка: 32 (3) +2
Здравствуйте, busk, Вы писали:
B>Первое что хотел сказать, что везде пишут микросервисы это где много команд часто или много функционала. В этом проекте ни того ни другого не будет,
B>но зато скоп определен четко и подумал прекрасная возможность потренироваться, чтобы потом уверенее пробовать в большом проекте.
Это — хорошая идея.
B>Тут я понимаю, что проще было бы сделать монолит, но для обучения вполне годный вариант?
Да, проще. Да, годный — невозможно сходу научиться резать монолит на микросервисы так, чтобы не стало хуже, чем было.

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

Да, совершенно точно.

B>Вот прошу тут помощи как нарезать на сервисы и базы?


B>Приложение: — Система контроля доставок грузов в разных странах (стран 10).

B> — Юзеры заходят по user/pswd и система определяет страну и подгружает данные этой страны. Новый юзер сам может зарегаться по логину выданному ему и восстановить пароль.
B> — В системе будет 4 роли: sysadmin, admin, manager, driver. В каждой стране свои пользователи. Один пользователь = 1 страна.
B> — Сами заказы поступают в эту систему из другой. В этой надо только мониторить и делать разные действия если доставка задержалась или не приехала.
B> — В каждой стране много складов и поэтому когда заказы поступают в систему то они сразу попадают на нужный склад и спец алгоритм распределяет на водителей.

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

B> — admin видит всё, не может только технические настройки менять. Следит за водителями, чтобы они всрок доставляли всё — по юаю специальному, вносит отпуска, больничные их, ставит им рабочее время. Если водитель увольняется — удаляет изи системы.
B> — manager следит за водителями своего склада только, может посмотреть по всем своим водителям информацию и по всем доставкам, но менять не может
B> — driver видит только по себе все сделанные доставки и предстоящие. Каждый раз когда он начинает и заканчивает доставку то в системе нажимает Start delivery\End delivery. Если доставка не укладывается в
B>предполагаемые сроки то пишет к доставке комментарий почему не успел

Важно, чтобы микросервисы как можно меньше взаимодействовали между собой. Если не получается — значит, границы выбраны неверно.
По вашему описанию не вполне понятно, что собственно должна делать система. Распределять заказы между водителями?
Смотрите:
0. Сервис аутентификации. Его хочется делать крайне надёжной, т.к. если никто не может зайти, то не работает вообще всё. Очень опасно делать монолит, в котором выкат какой-нибудь мелкой фичи типа "а давайте поздравим всех водителей-женщин с 8 марта" способен сломать логин. То есть эту штуку мы как можно реже релизим, и при каждом выкате покрываем тестами в шесть слоёв. Отдельные пацаны аудируют код на предмет потенциальных уязвимостей, т.к. кража базы паролей — почти самое плохое, что с нами вообще может случиться. Сам сервис скорее всего раскатан в нескольких экземплярах с геодистрибуцией и офигенной избыточностью — чтобы отключение света в нижегородском ДЦ не валило всю федеральную сеть водителей.
(Кстати, очень часто мы захотим это вообще делегировать наружу, чтобы не заниматься дорогостоящими инвестициями в эти пять девяток и прочие особенности. Привинчиваем OAuth и полагаемся на Google/Facebook/etc)
1. Сервис авторизации. После того, как пользователь залогинился, нам нужно ещё и получить набор его прав. В большинстве случаев это довольно-таки типовая структура, вроде ролей. Опять же, требования к ней довольно-таки жёсткие, т.к. она нужна в каждом первом сценарии. Ещё можно заметить, что она в значительной мере дублирует информацию из 0-й системы — как минимум, у нас должен быть тот же самый список пользователей. Поэтому в современном мире зачастую 0 и 1 объединяют в одну.
2. Сервис профилей. Всякие личные данные, предпочтения, и прочее. Набор этих данных меняется относительно часто, поэтому несмотря на структурную похожесть с предыдущим сервисом, его имеет смысл держать отдельно. Если даже кто-то очень криво выкатит обнову, которая даёт пользователю вместо фоточки на аватару выкладывать короткий ролик, логины и права продолжат работать.
Опять же, данные этого сервиса подпадают под всякие регуляторные нормы типа GDPR и его аналогов, поэтому имеет смысл их держать в отдельной базе с отдельными полисями бэкапа, контроля доступа, и функций своевременной очистки.
3. Сервис флагов/пользовательских настроек. Иногда его объединяют с предыдущим, но в большой системе лучше разделять. Потому что требования разные — флаги и настройки не являются personal information, зато постоянно нужны другим сервисам для принятия решений типа "а можно ли этого водителя назначить на рейс в Европу".

4. Вы упомянули про отпуска/больничные/рабочее время. Это выглядит как достаточно изолированный кусок, который может развиваться независимо от других. Опять же, он может попадать под какие-нибудь регуляторные требования в юрисдикции эксплуатации — всякие там нормы от минтруда и прочих проверяющих.

Обратите внимание, во всех сервисах, которые пока что были перечислены, есть понятие "пользователя". Типичной ошибкой начинающих проектировщиков является идея "а давайте мы всех пятерых подключим к одной базе данных — в конце-концов, список пользователей-то у нас общий! Зачем его дублировать?".
Вот это — плохая идея: получите все недостатки МСА без её достоинств. Да, список пользователей будет дублироваться. Да, на это придётся потратить немножко усилий. Но на самом деле всё не так уж и плохо — в том смысле, что, к примеру, настоящее имя пользователя и его дата рождения хранится только в одной системе, а какие-нибудь данные о его трудовой биографии — в другой. Дублирования не так уж и много; основное затруднение — невозможность контролировать ссылочную целостность. У вас всегда будет риск получить в четырёх базах "подвисшие" записи для пользователей, логин которых был забанен в 0м сервисе навсегда. Уборка мусора становится отдельной maintenance задачей.

Едем дальше — вот у нас что-то там про доставки. Собственно, тут пояснений недостаточно, придётся фантазировать. Очевидно, нужен какой-то сервис, который показывает водителю его доставки и позволяет с ними взаимодействовать.
Раздача доставок по водителям может быть частью этого сервиса, а может быть отдельной подсистемой. Чтобы опять же нововведения в алгоритме раздачи заявок не могли ничего отломать в учёте статуса доставок — у водителя в пути не всегда есть возможность "подождать до четверга", чтобы отметить статус заявки. Этому сервису будут нужны данные о том, кто когда планируется на смене — из того сервиса, где учитывались больничные/отпуска и т.п. Обратите внимание, что ему не надо никаких подробностей — можно придумать относительно стабильный, узкий API, по которому этот сервис запрашивает доступность того или иного водителя.

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

Вот примерно так это и должно работать.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Каких программ вам не хватает?
От: Miroff Россия  
Дата: 25.03.25 08:10
Оценка: -1
Здравствуйте, Sinclair, Вы писали:

S>Я не вижу каких-то прямо практически значимых преимуществ HATEOAS.


HATEOAS позволяет не размазывать логику между сервером и клиентом. Клиенту не нужно думать, можно ему дергать какой-то ресурс или нет. Прислали ссылку, значит можно. Не прислали -- нельзя. Вместе с OPTIONS это такой подход позволяет полностью перенести авторизацию с клиента на сервер. Или, например, пейджинг: клиенту не нужно вычислять сколько страниц всего, какая предыдущая, какая следующая и т.п. Вместо этого ему сразу приходит список страниц, которые можно дернуть.

S>- можно ли в качестве owner использовать URL юзер-аккаунта в другом скоупе


HATEOAS это про чтение, что ты собрался использовать при чтении. Можно ли поменять owner? Надо пробовать, может да, может нет, может в каких-то случаях да, а в остальных нет. Даже OpenAPI не позволяет описать настолько сложные контракты.

S>- что делает глагол refund, и какие у него будут параметры, и где их брать


Это же ресурс, делаешь GET, смотришь, меняешь, делаешь PUT.

S>2. Evolvability улучшается крайне незначительно. У нас нет никакого способа научить клиентов всегда ходить только по указанным нами ссылкам. Если мы решили перенести endpoint для рефандов в другое место, то недостаточно просто отдавать новую ссылку в links.


Правильно делать GET перед PUT, потому что ссылки могут измениться, и вообще конкурентные обновления. Если разработчики не умеют пользоваться REST API это их проблема. Можно тупо менять ссылки при каждом запросе, чтобы даже желания сохранять их куда либо не возникало.

S>3. Накладные расходы значительно увеличиваются.


Я тебя умоляю, мы видео в 4К по сети гоняем и GUID вместо id используем. Несколько ссылок погоды не сделают.
Re[10]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.03.25 08:38
Оценка: +1
Здравствуйте, Miroff, Вы писали:

M>HATEOAS позволяет не размазывать логику между сервером и клиентом. Клиенту не нужно думать, можно ему дергать какой-то ресурс или нет. Прислали ссылку, значит можно. Не прислали -- нельзя.

Это заблуждение. Авторизация проверяется совершенно отдельно. Ещё не хватало в представлении ресурса показывать разный набор ссылок в зависимости от transient штуки вроде Bearer-токена.

M>Вместе с OPTIONS это такой подход позволяет полностью перенести авторизацию с клиента на сервер.

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

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

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

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

M>HATEOAS это про чтение, что ты собрался использовать при чтении.

Нет конечно. HATEOAS — это про идентификацию ресурсов. REST сам по себе (даже без HATEOAS) — как раз про то, что все действия выполняются через управление представлением состояния.
В частности, приделывание к платёжке ссылки refund с методом POST — это RMM не L3, а L1. Потому, что нормальный способ — это PUT либо PATCH, которые прямым либо косвенным образом меняют состояние платежа на refunded.

M>Можно ли поменять owner? Надо пробовать, может да, может нет, может в каких-то случаях да, а в остальных нет. Даже OpenAPI не позволяет описать настолько сложные контракты.

Конечно позволяет. Вот у вас есть объект, у него есть представление. Всё, можно делать PUT этого представления. Если что-то запрещено бизнес-правилами — приедет 409. Если правами доступа — 403.

S>>- что делает глагол refund, и какие у него будут параметры, и где их брать

M>Это же ресурс, делаешь GET, смотришь, меняешь, делаешь PUT.
Нет конечно. Это не ресурс, это адрес для POST запроса.

M>Правильно делать GET перед PUT, потому что ссылки могут измениться, и вообще конкурентные обновления. Если разработчики не умеют пользоваться REST API это их проблема. Можно тупо менять ссылки при каждом запросе, чтобы даже желания сохранять их куда либо не возникало.

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

M>Я тебя умоляю, мы видео в 4К по сети гоняем и GUID вместо id используем. Несколько ссылок погоды не сделают.

Сделают. Стоимость эксплуатации складывается из мелочей. Два-три десятка таких идиотских решений — и вот уже ваш стейкхолдер вместо одного виртуального сервера оплачивает ферму из восьми выделенных.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re: Помогите правильно спроектировать микросервисное приложение
От: cppguard  
Дата: 25.03.25 08:58
Оценка:
Здравствуйте, busk, Вы писали:

B>Привет всем.

B>Как-то только в теории читал про микросервисы, но вот новый проект планируется небольшой и хотел как раз попробовать микросервисы тут.

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

B>Второе и собственно основное я так понял из книг, что самое сложное это определить границы сервисов. Вот прошу тут помощи как нарезать на сервисы и базы?


Я бы начал с ответа на вопрос: зачем вообще тебе микросервисы? Просто попробовать или действительно есть нужда? Просто пробовать на боевом проекте это почти всегда или фиаско, или бессонные ночи. Иногда сразу оба варианта В моём случае я оба раза выбирал микросервисы по прочине нехватки ресурсов для реализации всего функционала. Но не надо путать с нехваткой ресурсов для написания микросервисов как таковых — что монолит, что микросервисы потребовали бы целой команды, а я был один. Поэтому я разбил проект на непересекающиеся модули и для каждого использовал фреймворк или библиотеку, которая на старте давала 80% функционала из коробки. Например, нужен обыкновыенний CRUD? — Берём сразу Rail и не думаем. Илитных икспертов, которые расскажут, что Ruby медленный, посылаем в /dev/null — современный Ruby можно горизонтально масштабировать, а выигрышь от ускорения за счёт использования другого языка никогда не окупит время разработки эквивалентного функционала на этом языке. Теперь помимо CRUD нужно добавить возможность запускать процессы (например, мы пишем свой godbolt), изолировать их и контролировать. Отлично! Используем systemd для запуска процессов, а контроль осуществим через DBus. И как круто, что есть возможность генерировать интерфейсы классов на Python для DBus — не нужно руками прописывать вызовы API! Теперь у нас есть Ruby и Python. Просто связываем их через БД (вообще любая подойдёт). И вот у нас уже микросервисы. А если пойти дальше, то выяснится, что пресловутые микросервисы это REST гипертрофированный до размера операционной системы — состояние хранится где-то на диске (или в памяти в особо тяжёлых случаях), вызовы происходят через сервисы, а корректность состояния осуществляется через блокировки хранилища. Другого пока не придумали. Сложности и ошибки возникают, когда какой-то smart ass, решает, что он очень уж smart и пытается вынести контроль целостности данных с уровня хранилища на уровень приложения. Не надо так делать, и всё будет хорошо. И не надо бояться использовать разные языки. Лично я не понимаю, какой вообще смысл микросервисов, если мы всё пишем на одном языке? Теряется вся гибкость, всё преимущество возможности стоять на плечах гигантов и использовать готовые решения.

B>Третий вопрос. Если без докеров то как настроить этот набор микросервисов? тут их немного будет и читал что можно настроить без докер контейнеров если что.


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

Резюме: 80% написанного в личных блогах про микросервисы это каргокульт, можно не читать. Микросервисы это вообще обыкновенная архитектура — 100% современных автомобилей используют микросервисную архитектуру ещё с нулевых, не нужно её бояться. Купи книгу с кабанчиком (Клепман) и прочти её три раза.
Re[2]: Помогите правильно спроектировать микросервисное приложение
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 25.03.25 09:54
Оценка:
Здравствуйте, cppguard, Вы писали:

C>Так, во-первых не слушай старых пердунов (ну, или молодых смузихлёбов , утверждающих, что для микросервисной архитектуры нужны большие ресурсы. Я целых два(!!!) раза успешно реализовывал микросервисы в одиночку.

Мы все видели твои решения в некоторых разделах. Что ты там успешно напроектировал и реализовал — большой вопрос .
Sic luceat lux!
Re[6]: Помогите правильно спроектировать микросервисное приложение
От: · Великобритания  
Дата: 25.03.25 11:22
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>В этом случае может быть выгодно иметь микросервисы как раз по границам команд. Других адекватных причин использовать MSA я не видел.

В мною виденных трейдинговых системах — везде сабж. Одна команда (~10 девов) пилит пол-сотни приложух, которые разворачивачивется в ~тысячу сервисов на десятках хостов в пятёрке датацентров.
И да, сервисы не через REST взаимодйествуют, а через pub-sub сообщения (kafka/29west/mq/fix/аналоги).
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[2]: Помогите правильно спроектировать микросервисное приложение
От: TG  
Дата: 25.03.25 12:58
Оценка:
Здравствуйте, cppguard, Вы писали:

C>Я бы начал с ответа на вопрос: зачем вообще тебе микросервисы?

Дайте своё определение микросервисов.

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

Это Вы Кафку и прочие MQ REST-ом обозвали? Зачем?

C>Купи книгу с кабанчиком (Клепман) и прочти её три раза.

"Кабанчик" хорош, но его мало. Например, он никак не поможет определить границы сервисов.
Re[6]: Каких программ вам не хватает?
От: TG  
Дата: 25.03.25 13:01
Оценка:
Здравствуйте, Miroff, Вы писали:

M>Это вопрос не производительности, а потребляемых ресурсов. Микросервис суперпросто поднять, но суперсложно удалить. Например, компания, которая ввела внутренний биллинг, обнаружила себя тратящей несколько десятков миллионов в месяц на порядка тысячи микросервисов, которые непонятно кто и с какой целью поднял. Причем административными средствами проблема не решалась, потому что всегда оказывалось что микросервис поднял Вася пять лет назад для проекта X и за 5 лет Вася уволился, проект X влили в проект Y, который заменили на проект Z, в процессе два раза сменив команду и три раза сделав пивот на 180 градусов. И теперь концов уже не найти, нужен ли этот сервис СЕЙЧАС и если нужен, то ДЛЯ ЧЕГО.


Как-то не очень в такое верится.
"Тысячи микросервисов" — это при инстансы или типы сервисов?
Re[2]: Помогите правильно спроектировать микросервисное приложение
От: TG  
Дата: 25.03.25 13:06
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Обратите внимание, во всех сервисах, которые пока что были перечислены, есть понятие "пользователя". Типичной ошибкой начинающих проектировщиков является идея "а давайте мы всех пятерых подключим к одной базе данных — в конце-концов, список пользователей-то у нас общий! Зачем его дублировать?".

S>Вот это — плохая идея: получите все недостатки МСА без её достоинств. Да, список пользователей будет дублироваться. Да, на это придётся потратить немножко усилий. Но на самом деле всё не так уж и плохо — в том смысле, что, к примеру, настоящее имя пользователя и его дата рождения хранится только в одной системе, а какие-нибудь данные о его трудовой биографии — в другой. Дублирования не так уж и много; основное затруднение — невозможность контролировать ссылочную целостность. У вас всегда будет риск получить в четырёх базах "подвисшие" записи для пользователей, логин которых был забанен в 0м сервисе навсегда. Уборка мусора становится отдельной maintenance задачей.

Для таких вещей надо делать сервис реконсиляции.
С последующим ручным (скорее всего) разбором и разрешением коллизий.
Re[3]: Помогите правильно спроектировать микросервисное приложение
От: cppguard  
Дата: 25.03.25 13:16
Оценка:
Здравствуйте, Kernan, Вы писали:

K>Мы все видели твои решения в некоторых разделах. Что ты там успешно напроектировал и реализовал — большой вопрос .


Очень большой вопрос, но за это платили деньги и просили продолжать работать. Для меня это критерий успешности
Re[4]: Помогите правильно спроектировать микросервисное приложение
От: TG  
Дата: 25.03.25 13:24
Оценка: +1
Здравствуйте, Miroff, Вы писали:

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


API приложения полностью зависит от нужд потребителя. Договариваться придётся.
И документация на API нужна (даже если у вас RMM L3). Людям нужно знать не столько функционал отдельного эндпойнта, сколько возможность осуществить некоторый сценарий. Свагер такого не описывает.

M>Выставляешь Restful API и проблема других сервисов как этим API воспользоваться.

Даже условный Яндекс с его, например, Картами не может себе такого позволить и думает о потребителях, иначе конкуренты сожрут.
Re[3]: Помогите правильно спроектировать микросервисное приложение
От: cppguard  
Дата: 25.03.25 13:51
Оценка:
Здравствуйте, TG, Вы писали:

C>>Я бы начал с ответа на вопрос: зачем вообще тебе микросервисы?

TG>Дайте своё определение микросервисов.

Зачем?

TG>Это Вы Кафку и прочие MQ REST-ом обозвали? Зачем?


Ну, это было очень грубое сравнение. Просто мало людей понимают, что есть данные и есть код, и код изменяет данные. И можно придумывать велосипед снова и снова и говорить про принципиально новые подходы к разработке, но суть меняется мало. Например, возьмём понолит, реализуем ключевые функции через динамические библиотеки, реализуем механизм горячего обновления функций при измении соответствующего файла библиотеки — опа! У нас получилась микросервисная архитектура. Или не микросервисная — как посмотреть. Я работал в разных проектах — от социальных сетей до роботов, и везде было одно и то же: код, данные, шины обмена данными, интерфейсы взаимодейсвтия. И когда начинают сраться по поводу микросервисов и монолитов, я в упор не понимаю, в чём реально принципиальная разница? Если открыть произвольную статью про проблемы микросервисов, то 99% нытья сведётся к тому, что архитектура получилась слабосвязная, валидации никакой нет, поэтому команды каждый день ломают друг другу совместимость. Но это норма. Когда-то срались по поводу REST vs. XML RPC (хотя это ложная дихотомия, одно не отменяет другого), потом конечно же RPC вернулся в виде gRCP и Thrift, только об этом стыдливо умолчали, ведь сколько людей было унижено на собеседовании вопросами об абсолютном доминировании REST.

TG>"Кабанчик" хорош, но его мало. Например, он никак не поможет определить границы сервисов.


Что посоветуете почитать? Выбор зоны ответственности сервиса это вопрос, выходящий далеко за рамки темы микросервисов. Детское автомобильное кресло это категория "товары для детей" или "автоаксессуары"? Вот и с сервисами так же. У меня недостаточно опыта работы в кровавом энтерпрайзе, чтобы дискутировать на эту тему, хотя даже за его пределами приходится решать эту проблему. Вот прямо сейчас нужно понять, данные с лидара нужно слать в сыром виде по шине, а другой сервис будет заниматься обработкой, или же всё делать в рамках одного сервиса? В первом случае возникает нагрузка на шину, во втором случае — нагрузка на вычислительный узел. Пока что решение — вынести вообще всю систему восприятия робота в отдельную подсистему, но там свои нюансы.
Re[7]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 25.03.25 15:02
Оценка: -1
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>В этом случае может быть выгодно иметь микросервисы как раз по границам команд. Других адекватных причин использовать MSA я не видел.


S>Ну ещё ограничить failure domain. Чтобы бага в одном углу приложения клала его не целиком, а только блокировала отдельные сценарии.


В managed среда это можно и без микросервисов сделать. А даже в случае unmanaged можно разделить по процессам и использовать unix-sockets\named-pipes для общения между ними в рамках одного сервера, обеспечивая все взаимодействие и синхронизацию разных инстансов (серверов\контейнеров) через базу.

Если говорить failure domain с точки зрения логики приложения, то разделение на микросервисы делает ситуацию только хуже. Так как разработчики одного микросервиса могут внести изменения, ломающие работу других и даже не узнают об этом. Поэтому с точки зрения бизнес-логики идеально чтобы бизнес-процесс начинался и заканчивался в одном микросервисе.

В итоге подходящая модель для MSA — где команды разделены по бизнес-доменам, где все транзакционные бизнес-процессы полностью лежат в ответственности домена. Например закупки, склад, производство, продажи, планирование. Причем это будут дольно большие домены и, соответственно, большие сервисы. Почему их называют "микро-сервисами"&
Re[7]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 25.03.25 15:32
Оценка: :)
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>В этом случае может быть выгодно иметь микросервисы как раз по границам команд. Других адекватных причин использовать MSA я не видел.

·>В мною виденных трейдинговых системах — везде сабж. Одна команда (~10 девов) пилит пол-сотни приложух, которые разворачивачивется в ~тысячу сервисов на десятках хостов в пятёрке датацентров.
·>И да, сервисы не через REST взаимодйествуют, а через pub-sub сообщения (kafka/29west/mq/fix/аналоги).

Я конечно тоже видел системы, где микросервисов больше чем программистов и пользователей вместе взятых, но при проверке оказывалось что все то же самое можно было бы запились в монолит с меньшими затратами, не потеряв вообще никаких потребительских качеств системы.
Re[8]: Помогите правильно спроектировать микросервисное приложение
От: · Великобритания  
Дата: 25.03.25 16:55
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>·>В мною виденных трейдинговых системах — везде сабж. Одна команда (~10 девов) пилит пол-сотни приложух, которые разворачивачивется в ~тысячу сервисов на десятках хостов в пятёрке датацентров.

G>·>И да, сервисы не через REST взаимодйествуют, а через pub-sub сообщения (kafka/29west/mq/fix/аналоги).
G>Я конечно тоже видел системы, где микросервисов больше чем программистов и пользователей вместе взятых, но при проверке оказывалось что все то же самое можно было бы запились в монолит с меньшими затратами, не потеряв вообще никаких потребительских качеств системы.
Охотно верю. Я лишь рассказал о типах систем, которые ты не видел. Если интересно и есть вопросы, спрашивай.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: Помогите правильно спроектировать микросервисное приложение
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.04.25 05:17
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>В managed среда это можно и без микросервисов сделать.

Не очень понятно, что вы имеете в виду. Поддержку AppDomain выпилили (за ненадобностью) из нового дотнета, а в жаве её никогда и не было.
Управляемая среда всего лишь гарантирует отсутствие ошибок типизации и "повисших" ссылок. Всё. Никакой изоляции она из коробки не предоставляет.
Понятно, что она лучше любого анменеджеда хотя бы тем, что в ней нет undefined behavior (если, опять же, придерживаться гигиены и не пользоваться всякими ансейф и около-ансейф вещами).
Но defined behavior недостаточно для ограничения failure domain. Потому что если есть разделяемое состояние, то невозможно гарантировать его целостность при наступлении неожиданных результатов.
Вот у вас есть разделяемая в памяти коллекция, и один поток начал её модификацию, пока второй читает. Ок, мы обеспечили отсутствие гонок, обложив доступ примитивами синхронизации.
Менеджед-среда (более-менее) гарантирует нам то, что при выбросе исключения будут обработаны все блоки finally и, в частности, все удерживаемые блокировки будут отпущены. Но она вовсе не гарантирует нам то, что после отпускания блокировок коллекция придёт в консистентное (с точки зрения прикладных инвариантов) состояние.

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

G>А даже в случае unmanaged можно разделить по процессам и использовать unix-sockets\named-pipes для общения между ними в рамках одного сервера, обеспечивая все взаимодействие и синхронизацию разных инстансов (серверов\контейнеров) через базу.

Всё верно. Если у нас shared state вынесен в "базу", которая предоставляет ACID-гарантии, то у нас масштабы проблемы ограничены.
В предположении, что все инварианты инкапсулированы в этой базе. На практике, зачастую собственно перемещение логики из базы в "сервисы" и делается ради того, чтобы обеспечить те инварианты, которые в базе поддерживать оказывается тяжело. То есть у нас опять корректность разделяемого состояния определяется тем, насколько корректно себя ведёт каждый сервис. И наличие бесконтрольного доступа в базу приводит к тому, что ошибка в сервисе А приводит к записи в базу мусора, который нарушает предположения, на основе которых реализованы другие сервисы.

Поэтому сервисная архитектура и появляется как развитие идеи "давайте разведём сервисы по процессам и заставим их взаимодействовать по ограниченным каналам". Выбор JSON over HTTP вместо "proprietary binary protocol over named pipes" обусловлен уже не вопросами надёжности, а вопросами совместимости и готовности к эволюции.
Если мы попытаемся спроектировать свой бинарный протокол со встроенной поддержкой всех нужных negotiations, а также с умением восстанавливаться после сбоев и кэшировать результаты, то у нас получится примерно такой же REST, чутка отличающийся диалектом.

G>Если говорить failure domain с точки зрения логики приложения, то разделение на микросервисы делает ситуацию только хуже. Так как разработчики одного микросервиса могут внести изменения, ломающие работу других и даже не узнают об этом. Поэтому с точки зрения бизнес-логики идеально чтобы бизнес-процесс начинался и заканчивался в одном микросервисе.

Это уже административные вопросы. Вы неявно предполагаете, что единицей тестирования является отдельный микросервис, из-за чего бизнес-процесс, пересекающий границы микросервисов, останется непротестированным.
Но с таким подходом мы налетим на те же проблемы и в монолите — если мы ограничимся юнит-тестами компонентов без интеграционного тестирования получится ровно такой же результат.
А если у нас процесс тестирования построен (хотя бы частично) вокруг бизнес-сценариев, то нам всё равно, сколько там компонентов, сервисов, или микросервисов в нём участвует.
Микросервисы несколько выигрывают по сравнению с другими архитектурами как раз потому, что сокращают возможности по внесению нежелательных зависимостей.
То есть пока у нас всё лежит в монолите, сотрудник команды А имеет возможность подсмотреть в код команды Б, и напрямую завязаться на то, что интерфейс IFoo реализован некоторым классом FooImpl. И вместо того, чтобы обратиться по стандартным каналам к архитекторам команды Б "добавьте мне в IFoo метод по расчёту предварительной цены без заключения сделки", делает даункаст и вызывает потрошки напрямую.
Когда вы распиливаете монолит на процессы и пайпы, такой возможности уже нет — у Foo своё адресное пространство, и мимо протокола к нему обратиться не получится.
Зато можно полезть напрямую в базу "я же знаю, в каких таблицах у них что лежит". В итоге команда Б, полагая себя единоличными владельцами схемы, что-то перекраивает в очередной версии своего сервиса, и код команды А начинает падать, хотя такой же код неделю назад прекрасно работал.

G>В итоге подходящая модель для MSA — где команды разделены по бизнес-доменам, где все транзакционные бизнес-процессы полностью лежат в ответственности домена. Например закупки, склад, производство, продажи, планирование. Причем это будут дольно большие домены и, соответственно, большие сервисы. Почему их называют "микро-сервисами"


Не, это вы как раз рассказываете про SOA, где сервисы всё ещё представляют собой очень крупные блоки. А MSA — это как раз экстремальное развитие SOA, когда мы дробим задачу на ещё более мелкие единицы, которые не реализуют никакой бизнес-процесс, а, скорее, отвечают за некоторые кирпичики этого процесса. Например, у вас есть "ядро" — микросервис, который отвечает за финансовые транзации. Только переводы между счетами, и всё. У него очень жёсткие инварианты, и всё покрыто требованиями регулятора. Поверх этого вы реализуете функциональность типа "Выдача кредитов". Наличие отдельного ядра гарантирует вам, что криворукий джун, разрабатывая код бизнес-процессов "выдача кредита" и "внесение очередного платежа" не напорет с транзакциями и не сломает базовые гарантии типа сохранения суммы всех балансов.
Опять же сбоку этого делается отдельный сервис по оценке рисков, который никак не может сломать поведение бизнес-процесса "рассмотрение кредитных заявок", и в худшем случае у вас какие-то из видов кредитов перестанут выдаваться. Но никаких шансов запороть механику начисления процентов и списания задолженности у этого микросервиса нет.
И так далее — всё строится из параноидально изолированных микрокомпонентов. Один умеет рассчитывать стоимость залогового имущества, другой — умеет управлять статусами залогового имущества. Третий из этих двух собирает бизнес-процесс "переоценка рисков и досрочное истребование кредита в связи с изменением существенных обстоятельств".
Дополнительными бонусами являтся возможность один писать на питоне, другой — на тайпскрипте.

Как-то так.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Помогите правильно спроектировать микросервисное приложение
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 01.04.25 14:04
Оценка: -1 :)
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>В managed среда это можно и без микросервисов сделать.

S>Не очень понятно, что вы имеете в виду. Поддержку AppDomain выпилили (за ненадобностью) из нового дотнета, а в жаве её никогда и не было.

Ну есть экспериментальные DotNetIsolator [EXPERIMENTAL]

Правда 2 года не изменялась

// Set up an isolated runtime
using var host = new IsolatedRuntimeHost().WithBinDirectoryAssemblyLoader();
using var runtime = new IsolatedRuntime(host);

// Output: I'm running on X64
Console.WriteLine($"I'm running on {RuntimeInformation.OSArchitecture}");

runtime.Invoke(() =>
{
    // Output: I'm running on Wasm
    Console.WriteLine($"I'm running on {RuntimeInformation.OSArchitecture}");
});
и солнце б утром не вставало, когда бы не было меня
Re[9]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 02.04.25 07:45
Оценка: 80 (1)
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>В managed среда это можно и без микросервисов сделать.

S>Не очень понятно, что вы имеете в виду. Поддержку AppDomain выпилили (за ненадобностью) из нового дотнета, а в жаве её никогда и не было.
S>Управляемая среда всего лишь гарантирует отсутствие ошибок типизации и "повисших" ссылок. Всё. Никакой изоляции она из коробки не предоставляет.
S>Понятно, что она лучше любого анменеджеда хотя бы тем, что в ней нет undefined behavior (если, опять же, придерживаться гигиены и не пользоваться всякими ансейф и около-ансейф вещами).
Я говорю о том, что вручную выстроенная модульность внутри приложения, на уровне отсутствия ссылок из группы объектов, поддерживается самой managed средой. Без использования ансейфа и разделяемых данных без должной осмотрительности крайне сложно в managed среде во время обработки одного запроса нарушить работу кода, обрабатывающего другие запросы.


S>Но defined behavior недостаточно для ограничения failure domain. Потому что если есть разделяемое состояние, то невозможно гарантировать его целостность при наступлении неожиданных результатов.

S>Вот у вас есть разделяемая в памяти коллекция, и один поток начал её модификацию, пока второй читает. Ок, мы обеспечили отсутствие гонок, обложив доступ примитивами синхронизации.
S>Менеджед-среда (более-менее) гарантирует нам то, что при выбросе исключения будут обработаны все блоки finally и, в частности, все удерживаемые блокировки будут отпущены. Но она вовсе не гарантирует нам то, что после отпускания блокировок коллекция придёт в консистентное (с точки зрения прикладных инвариантов) состояние.
S>Всё, у вас ошибка в одном из "микросервисов" привела к тому, что рантайм стейт всего приложения пришёл в развал. Все остальные будут делать фигню или сыпать ошибками.

Очень хороший кейс.

А давай прикинем как это будет в MSA. там будет точно такая же коллекция в одном из сервисов, и менять её будет не поток, порожденный программистом, а поток порожденный хостом для обработки запроса. А все остальное будет то же самое. И, как в случае монолитного приложения, так и в случае MSA падения как такового не будет. Восстановление после такого falure возможно только сбросом состояния путем насильного перезапуска, например по хелсчеку. Разница в случае MSA будет только в том, что будет перезапущен один маленький сервис, а не толстое приложение. Но при этом весь сценарий работать не будет, пока микросервис с разделяемой коллекцией в памяти не оживет.

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




G>>А даже в случае unmanaged можно разделить по процессам и использовать unix-sockets\named-pipes для общения между ними в рамках одного сервера, обеспечивая все взаимодействие и синхронизацию разных инстансов (серверов\контейнеров) через базу.

S>Всё верно. Если у нас shared state вынесен в "базу", которая предоставляет ACID-гарантии, то у нас масштабы проблемы ограничены.
При горизонтальном масштабировании к этому все и придут обязательно. В принципе грамотный архитектор должен сразу на такое натолкнуть.

S>В предположении, что все инварианты инкапсулированы в этой базе. На практике, зачастую собственно перемещение логики из базы в "сервисы" и делается ради того, чтобы обеспечить те инварианты, которые в базе поддерживать оказывается тяжело. То есть у нас опять корректность разделяемого состояния определяется тем, насколько корректно себя ведёт каждый сервис. И наличие бесконтрольного доступа в базу приводит к тому, что ошибка в сервисе А приводит к записи в базу мусора, который нарушает предположения, на основе которых реализованы другие сервисы.

Опять-таки MSA ничем не помогает в этом случае. Без разницы будет мусор писать в базу микросервис или монолит.

G>>Если говорить failure domain с точки зрения логики приложения, то разделение на микросервисы делает ситуацию только хуже. Так как разработчики одного микросервиса могут внести изменения, ломающие работу других и даже не узнают об этом. Поэтому с точки зрения бизнес-логики идеально чтобы бизнес-процесс начинался и заканчивался в одном микросервисе.

S>Это уже административные вопросы. Вы неявно предполагаете, что единицей тестирования является отдельный микросервис, из-за чего бизнес-процесс, пересекающий границы микросервисов, останется непротестированным.
Так это самые важные вопрос. Я еще раз напомню тезис, который в этой теме и другие коллеги подтверждают: MSA это больше про оргструктуру, а не про архитектуру. В интернетах предлагают "правильную" MSA с разделением микросервисов по разным репозитариям исходного кода. Как в этом случае обеспечить синхронность изменений разных микросервисах, если изменяемый бизнес-процесс затрагивает несколько таких микросервисов?

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

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

S>А если у нас процесс тестирования построен (хотя бы частично) вокруг бизнес-сценариев, то нам всё равно, сколько там компонентов, сервисов, или микросервисов в нём участвует.

В msa весьма вероятна ситуация, что в принципе невозможно собрать работающее сочетание микросервисов. Я такое на практике видел. Было три микросервиса (А,Б,В) и два процесса(1,2), затрагивающие три сервиса. В один момент было так, что: А/HEAD, Б/HEAD, В/HEAD~1 работал сценарий 1, но не работал 2. А/HEAD, Б/HEAD~1, В/HEAD работал 2, но не работал 1 и при этом же А/HEAD~1, Б/HEAD, В/HEAD также работал 2, но не работал 1.

И каждый разработчик миросервиса доказывал что "work on my microservice" (современный аналог work on my machine)


S>Микросервисы несколько выигрывают по сравнению с другими архитектурами как раз потому, что сокращают возможности по внесению нежелательных зависимостей.

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

S>То есть пока у нас всё лежит в монолите, сотрудник команды А имеет возможность подсмотреть в код команды Б, и напрямую завязаться на то, что интерфейс IFoo реализован некоторым классом FooImpl. И вместо того, чтобы обратиться по стандартным каналам к архитекторам команды Б "добавьте мне в IFoo метод по расчёту предварительной цены без заключения сделки", делает даункаст и вызывает потрошки напрямую.

Доункаст к чему, если у него нет паблик класса, к которому даункастить?

S>Когда вы распиливаете монолит на процессы и пайпы, такой возможности уже нет — у Foo своё адресное пространство, и мимо протокола к нему обратиться не получится.

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

S>Зато можно полезть напрямую в базу "я же знаю, в каких таблицах у них что лежит". В итоге команда Б, полагая себя единоличными владельцами схемы, что-то перекраивает в очередной версии своего сервиса, и код команды А начинает падать, хотя такой же код неделю назад прекрасно работал.

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

G>>В итоге подходящая модель для MSA — где команды разделены по бизнес-доменам, где все транзакционные бизнес-процессы полностью лежат в ответственности домена. Например закупки, склад, производство, продажи, планирование. Причем это будут дольно большие домены и, соответственно, большие сервисы. Почему их называют "микро-сервисами"


S>Не, это вы как раз рассказываете про SOA, где сервисы всё ещё представляют собой очень крупные блоки. А MSA — это как раз экстремальное развитие SOA, когда мы дробим задачу на ещё более мелкие единицы, которые не реализуют никакой бизнес-процесс, а, скорее, отвечают за некоторые кирпичики этого процесса. Например, у вас есть "ядро" — микросервис, который отвечает за финансовые транзации. Только переводы между счетами, и всё. У него очень жёсткие инварианты, и всё покрыто требованиями регулятора. Поверх этого вы реализуете функциональность типа "Выдача кредитов". Наличие отдельного ядра гарантирует вам, что криворукий джун, разрабатывая код бизнес-процессов "выдача кредита" и "внесение очередного платежа" не напорет с транзакциями и не сломает базовые гарантии типа сохранения суммы всех балансов.

Я именно об этом и говорю. Знаю один банк где над таким ядром-"микросервисом" трудится команда из 40+ только программистов. То есть ни разу он не "микро", о чем я и говорю. А выдача кредитов это вообще с десяток связанных процессов, на каждом из которых команда в 10+ человек. То есть их тоже "микро" назвать сложно.

Напомню что апологеты микросервисов говорят что микросервис это когда команду можно накормить двумя пиццами, грубо 4-5 человек.

S>Дополнительными бонусами являтся возможность один писать на питоне, другой — на тайпскрипте.

Это какбы единственное неоспоримое преимущество MSA, с которым никто никогда и не спорил.
Re[10]: Помогите правильно спроектировать микросервисное приложение
От: Sinclair Россия https://github.com/evilguest/
Дата: 02.04.25 09:51
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>Я говорю о том, что вручную выстроенная модульность внутри приложения, на уровне отсутствия ссылок из группы объектов, поддерживается самой managed средой. Без использования ансейфа и разделяемых данных без должной осмотрительности крайне сложно в managed среде во время обработки одного запроса нарушить работу кода, обрабатывающего другие запросы.

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

G>А давай прикинем как это будет в MSA. там будет точно такая же коллекция в одном из сервисов, и менять её будет не поток, порожденный программистом, а поток порожденный хостом для обработки запроса. А все остальное будет то же самое. И, как в случае монолитного приложения, так и в случае MSA падения как такового не будет. Восстановление после такого falure возможно только сбросом состояния путем насильного перезапуска, например по хелсчеку. Разница в случае MSA будет только в том, что будет перезапущен один маленький сервис, а не толстое приложение. Но при этом весь сценарий работать не будет, пока микросервис с разделяемой коллекцией в памяти не оживет.

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

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

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

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

Вопрос открытый. Чего я только не видел в энтерпрайзе, при самых грамотных архитекторах.

G>Опять-таки MSA ничем не помогает в этом случае. Без разницы будет мусор писать в базу микросервис или монолит.

Нет, разница принципиальная. Микросервис физически не может записать в чужую базу кривую запись о переводе денег.

G>Так это самые важные вопрос. Я еще раз напомню тезис, который в этой теме и другие коллеги подтверждают: MSA это больше про оргструктуру, а не про архитектуру. В интернетах предлагают "правильную" MSA с разделением микросервисов по разным репозитариям исходного кода. Как в этом случае обеспечить синхронность изменений разных микросервисах, если изменяемый бизнес-процесс затрагивает несколько таких микросервисов?

Никак — MSA, собственно, и обеспечивает отсутствие синхронности. Это преимущество, а не недостаток.
Изменения в бизнес-процессе накатываются постепенно: сначала мы обучаем новостям самый "нижний" микросервис в топологически отсортированном списке микросервисов, затем — чуть более "верхние".
Изменения в БП становятся видны в тот момент, когда мы выкатываем изменения "корневого" микросервиса, который оркестрирует весь БП.

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

G>В msa весьма вероятна ситуация, что в принципе невозможно собрать работающее сочетание микросервисов. Я такое на практике видел. Было три микросервиса (А,Б,В) и два процесса(1,2), затрагивающие три сервиса. В один момент было так, что: А/HEAD, Б/HEAD, В/HEAD~1 работал сценарий 1, но не работал 2. А/HEAD, Б/HEAD~1, В/HEAD работал 2, но не работал 1 и при этом же А/HEAD~1, Б/HEAD, В/HEAD также работал 2, но не работал 1.

G>И каждый разработчик миросервиса доказывал что "work on my microservice" (современный аналог work on my machine)

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

G>Дисциплина, современные среды, языки и библиотеки (я не про Go) позволяют создавать достаточную изоляцию и внутри монолитного приложения, не получая огромных затрат на внесение зависимостей когда они нужны.

Может быть. Я не говорю, что MSA — единственный верный способ деятельности

G>Доункаст к чему, если у него нет паблик класса, к которому даункастить?

Почему вы думаете, что нету?
А если нету — всегда можно запилить reflection. Пытливому уму все средства хороши.

G>Тут даже комментировать не буду. В дотнете и жабе полно средств изоляции. При желании даже в JS можно инкапсуляцию сделать, чтобы до потрохов никто не добрался.

Как раз в JS-то можно. Но, опять же, можно!=делают.

G>Так можно и в базу другого микросервиса полезть.

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

G>В случае единой репы и нормального архитектора — на ревью бить по рукам (или по лицу) за такой код.

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

G>Я именно об этом и говорю. Знаю один банк где над таким ядром-"микросервисом" трудится команда из 40+ только программистов. То есть ни разу он не "микро", о чем я и говорю. А выдача кредитов это вообще с десяток связанных процессов, на каждом из которых команда в 10+ человек. То есть их тоже "микро" назвать сложно.

Так "micro" это же не про размер команды. А про "площадь поверхности".

G>Напомню что апологеты микросервисов говорят что микросервис это когда команду можно накормить двумя пиццами, грубо 4-5 человек.

Хм. Я не очень тесно знаком с апологетами микросервисов, могу чего-то не знать.

G>Это какбы единственное неоспоримое преимущество MSA, с которым никто никогда и не спорил.

Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Помогите правильно спроектировать микросервисное приложение
От: · Великобритания  
Дата: 02.04.25 10:08
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>Я говорю о том, что вручную выстроенная модульность внутри приложения, на уровне отсутствия ссылок из группы объектов, поддерживается самой managed средой. Без использования ансейфа и разделяемых данных без должной осмотрительности крайне сложно в managed среде во время обработки одного запроса нарушить работу кода, обрабатывающего другие запросы.

Гы. Как минимум, память и gc — разделяемые. Так что когда запрос какого-нибудь отчёта для аналитики вдруг роняет по OOM всё сразу, в т.ч. обработку заказов — ничего хорошего.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[11]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 02.04.25 11:03
Оценка: +1
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>Я говорю о том, что вручную выстроенная модульность внутри приложения, на уровне отсутствия ссылок из группы объектов, поддерживается самой managed средой. Без использования ансейфа и разделяемых данных без должной осмотрительности крайне сложно в managed среде во время обработки одного запроса нарушить работу кода, обрабатывающего другие запросы.

·>Гы. Как минимум, память и gc — разделяемые. Так что когда запрос какого-нибудь отчёта для аналитики вдруг роняет по OOM всё сразу, в т.ч. обработку заказов — ничего хорошего.
Ничего не мешает в рамках монолитного решения вынести отдельный инстанс для обработки тяжелых запросов и на уровне балансера распределять.

Еще раз повторю тезис: MSA это больше про оргструктуру и следующие за ней технические решения. А все инструменты: докеры, кубернетесы, балансеры, кэши, готовые сервисы хранения и расчетов, вполне можно применять даже если у вас весь ваш код в одной репе, копилируется в один бинарник и запускается в одном контейнере.
Re[12]: Помогите правильно спроектировать микросервисное приложение
От: · Великобритания  
Дата: 02.04.25 11:21
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>>>Я говорю о том, что вручную выстроенная модульность внутри приложения, на уровне отсутствия ссылок из группы объектов, поддерживается самой managed средой. Без использования ансейфа и разделяемых данных без должной осмотрительности крайне сложно в managed среде во время обработки одного запроса нарушить работу кода, обрабатывающего другие запросы.

G>·>Гы. Как минимум, память и gc — разделяемые. Так что когда запрос какого-нибудь отчёта для аналитики вдруг роняет по OOM всё сразу, в т.ч. обработку заказов — ничего хорошего.
G>Ничего не мешает в рамках монолитного решения вынести отдельный инстанс для обработки тяжелых запросов и на уровне балансера распределять.
Видимо ты тут намекаешь на какую-то принципиальную разницу между SOA и MSA?
Ещё почему-то ты предполагаешь что есть некий один универсальный балансер. А в приложухе может быть REST для аналитики, FIX для заказов и т.п.

G>Еще раз повторю тезис: MSA это больше про оргструктуру и следующие за ней технические решения.

По-моему, ты причину со следствием путаешь.

G>А все инструменты: докеры, кубернетесы, балансеры, кэши, готовые сервисы хранения и расчетов, вполне можно применять даже если у вас весь ваш код в одной репе, копилируется в один бинарник и запускается в одном контейнере.

А зачем обязательно один бинарник? Если деплоймент разный всё равно. Чтобы были многочасовые билды?
Сборка, деплой и перезапуск мелкого сервиса занимает минуты от момента мержа PR. Тогда как типичная выкатка монолита — приятно проведённые выходные.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[13]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 02.04.25 14:32
Оценка: +1
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>>>Я говорю о том, что вручную выстроенная модульность внутри приложения, на уровне отсутствия ссылок из группы объектов, поддерживается самой managed средой. Без использования ансейфа и разделяемых данных без должной осмотрительности крайне сложно в managed среде во время обработки одного запроса нарушить работу кода, обрабатывающего другие запросы.

G>>·>Гы. Как минимум, память и gc — разделяемые. Так что когда запрос какого-нибудь отчёта для аналитики вдруг роняет по OOM всё сразу, в т.ч. обработку заказов — ничего хорошего.
G>>Ничего не мешает в рамках монолитного решения вынести отдельный инстанс для обработки тяжелых запросов и на уровне балансера распределять.
·>Видимо ты тут намекаешь на какую-то принципиальную разницу между SOA и MSA?

«Они очень маленькие. Даже сто строк кода, вероятно, считаются сегодня большим сервисом», — Фред Джордж, Barcelona Ruby Conference


«Команда на две пиццы» https://aws.amazon.com/executive-insights/content/amazon-two-pizza-team/


«Если сервис проектирует и поддерживает несколько программистов, то это не микросервис», — Фред Джордж, GOTO 2016


«Каждый сервис автономен, самостоятелен и выполняет уникальный процесс». https://www.paloaltonetworks.co.uk/cyberpedia/what-are-microservices#:~:text=Each%20service%20is%20autonomous%20and%20self%2Dcontained%20and%20runs%20a%20unique%20process


«Каждый микросервис упакован как контейнер Docker, чтобы обеспечить возможность развёртывания в кластер Kubernetes для оркестрации приложения». https://thinkmicroservices.com/


Тут и паттерны, и технологии, и оргструктуры и много чего еще.


·>Ещё почему-то ты предполагаешь что есть некий один универсальный балансер. А в приложухе может быть REST для аналитики, FIX для заказов и т.п.

Я не понял к чему эта фраза

G>>Еще раз повторю тезис: MSA это больше про оргструктуру и следующие за ней технические решения.

·>По-моему, ты причину со следствием путаешь.
А может и наоборот, не задумывался об этом?
Я вижу что микросервисы цветут там, где слишком много команд и разработчиков чтобы пилить монолиты. Поэтому техническая архитектура диктуется оргструктурой. Если у тебя команда небольшая или ты вообще один что-то делаешь — тебе в принципе не нужны микросервисы.


G>>А все инструменты: докеры, кубернетесы, балансеры, кэши, готовые сервисы хранения и расчетов, вполне можно применять даже если у вас весь ваш код в одной репе, копилируется в один бинарник и запускается в одном контейнере.

·>А зачем обязательно один бинарник? Если деплоймент разный всё равно. Чтобы были многочасовые билды?
А ты думаешь сборка одного толстого бинарника медленнее, чем сборка трех бинарников потоньше? Или ты думаешь что обязательно пересобирать все проекты каждый раз?
Да и в целом время сборки для деплоя мало интересует, интересует время от команды run до запуска приложения, если ты это делаешь 25 раз в день.

·>Сборка, деплой и перезапуск мелкого сервиса занимает минуты от момента мержа PR. Тогда как типичная выкатка монолита — приятно проведённые выходные.

В теории да. Но внезапно:
1) в микросервисах надо тупо больше кода править
2) если ты затрагиваешь несколько сервисов своим изменением, то несколько сервисов собираются дольше чем один
3) даже докер умеет кэшировать образы, поэтому вполне можно сделать сбору с кэшированием и не пересобирать то, что не изменилось
Re[11]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 02.04.25 22:01
Оценка:
Здравствуйте, Sinclair, Вы писали:

G>>А давай прикинем как это будет в MSA. там будет точно такая же коллекция в одном из сервисов, и менять её будет не поток, порожденный программистом, а поток порожденный хостом для обработки запроса. А все остальное будет то же самое. И, как в случае монолитного приложения, так и в случае MSA падения как такового не будет. Восстановление после такого falure возможно только сбросом состояния путем насильного перезапуска, например по хелсчеку. Разница в случае MSA будет только в том, что будет перезапущен один маленький сервис, а не толстое приложение. Но при этом весь сценарий работать не будет, пока микросервис с разделяемой коллекцией в памяти не оживет.

S>Всё верно. MSA не гарантирует безошибочности. MSA гарантирует, что падение не затронет сценарии, в которых микросервис не задействован.
Смотря что значит "не затронет". Если сервис А зависит от срвиса Б, а сервис Б содержит ошибку, даже неважно приводящую к падению или нет, то и А не будет корректно работать.
Падение — не самая страшная часть, даже в проде. Плохо когда: после падения не может подняться, или вместо падения не работает корректно — висит в ожидании или отдает некорректные данные. Причем второе при несинхронной разработке весьма вероятно.

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

S>Либо отказываться от разделяемого состояния. Потому что людей, умеющих изготовить полноценное клиент-серверное приложение внутри ACID-хранилища, на рынке не осталось (даже если предположить, что они когда-то были)
Его же не надо "внутри", надо чтобы разделяемое состояние польностью сохранялось и обрабатывалось ACID хранилищем, желательно внешним по отношению к самому приложению. Чтобы приложение можно было безопасно масштабировать и перезапускать.

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

S>Вопрос открытый. Чего я только не видел в энтерпрайзе, при самых грамотных архитекторах.
Бывает конечно всякое, и архитекторы, которые не понимают как и почему базы работают. Но в целом это подход по умолчанию: "давайте состояние приложения положим в базу". Раньше по умолчанию выбирали РСУБД, но сейчас уговаривать приходится.

G>>Опять-таки MSA ничем не помогает в этом случае. Без разницы будет мусор писать в базу микросервис или монолит.

S>Нет, разница принципиальная. Микросервис физически не может записать в чужую базу кривую запись о переводе денег.
Ну физически то может. Но сделать это гораздо сложнее чем в монолите.
Но у этой медали есть и обратная сторона: если у вас разные базы, то сделать джоин в них крайне сложно. Задачи которые в монолите бы решались одной строкой в SQL в MSA начинают требовать тонны кода.
Пример реальный: профили пользователей лежат в одном сервисе, данные о туристических маршрутах в другом. Важная часть логики: для несовреннолетних доступна одна часть маршрутов, для соврешеннолетных — другая. Есть еще другие признаки для фильтров: по регионам, интересам итд.
Теперь нам надо сделать рассылку, подобрав подходящие маршруты для клиентов.
В монолите — один джоин. В MSA — пожалуйста напишите тонну кода.
В реальности ситуация была еще хуже. Данные о бронях групп и совершенных путешествиях лежали в третьем сервисе. И конечно же надо было из предложений исключить тех кто уже ездил.


G>>Так это самые важные вопрос. Я еще раз напомню тезис, который в этой теме и другие коллеги подтверждают: MSA это больше про оргструктуру, а не про архитектуру. В интернетах предлагают "правильную" MSA с разделением микросервисов по разным репозитариям исходного кода. Как в этом случае обеспечить синхронность изменений разных микросервисах, если изменяемый бизнес-процесс затрагивает несколько таких микросервисов?

S>Никак — MSA, собственно, и обеспечивает отсутствие синхронности. Это преимущество, а не недостаток.
А в чем собственно преимущество? Ну если в деньгах посчитать.

S>Изменения в бизнес-процессе накатываются постепенно: сначала мы обучаем новостям самый "нижний" микросервис в топологически отсортированном списке микросервисов, затем — чуть более "верхние".

S>Изменения в БП становятся видны в тот момент, когда мы выкатываем изменения "корневого" микросервиса, который оркестрирует весь БП.
Если сервисы имеют строгую иерархию и представляют из себя "слои" приложения, то возможно это и так.
Но сразу три замечания:
1) это цикл внедрения фичи удлиняет. Так как скорости выгоднее делать "вертикальное" деление проекта.
2) Это противоречит мысли самодостаточности сервиса. "Верхний" не работает без "нижних", а "нижние" не нужны без "верних".
3) При падении\ошибке в "нижних" "верхние" тоже ломаются.



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

G>>В msa весьма вероятна ситуация, что в принципе невозможно собрать работающее сочетание микросервисов. Я такое на практике видел. Было три микросервиса (А,Б,В) и два процесса(1,2), затрагивающие три сервиса. В один момент было так, что: А/HEAD, Б/HEAD, В/HEAD~1 работал сценарий 1, но не работал 2. А/HEAD, Б/HEAD~1, В/HEAD работал 2, но не работал 1 и при этом же А/HEAD~1, Б/HEAD, В/HEAD также работал 2, но не работал 1.

G>>И каждый разработчик миросервиса доказывал что "work on my microservice" (современный аналог work on my machine)

S>Это организационная проблема. В монолите все эти команды делали бы ровно то же самое ровно с тем же результатом.
Это в монорепе гораздо сложнее, до уровня "почти невозможно", так как версия всех компонент в монорепе всегда одна. Если вы не балуетесь выносом бизнес-логики во внешние пакеты.

S>А, ну и, возможно, неудачно были проведены границы между микросервисами. Это — отдельное искусство, настолько редкое, что я, ЕМНИП, видел только одно правильное решение (и с десяток заведомо неверных).

Может быть мне так везло, но я участвовал в 6 проектах с MSA и у десятка "стоял рядом" и везде были такие проблемы.
Более того я два проекта с MSA запилил назад в монолит и стало всем лучше. Но там четко одна команда работала над проектом.

G>>Дисциплина, современные среды, языки и библиотеки (я не про Go) позволяют создавать достаточную изоляцию и внутри монолитного приложения, не получая огромных затрат на внесение зависимостей когда они нужны.

S>Может быть. Я не говорю, что MSA — единственный верный способ деятельности
Я делаю более сильное утверждение: MSA пригодна только там, где есть соответствующая оргструктура: много несвязанных команд, каждая из которых работает на процессами, слабо связанными друг с другом. Такое в банках и маркетплейсах такое очень часто встречается, а они составляют основу русского бигтеха.
А например в корпоративных системах оно не надо, как при кастомной разработке, так и при разработке тиражируемого продукта.

G>>Доункаст к чему, если у него нет паблик класса, к которому даункастить?

S>Почему вы думаете, что нету?
S>А если нету — всегда можно запилить reflection. Пытливому уму все средства хороши.
Ну я точно также могу сказать про запись в чужую базу. Оно ведь все равно деплоится все в одно приложение докера\кубера и контейнеры видят друг друга. И все равно разраб сталкивается с тестовой средой, где может "в дикой природе" наблюдать какие еще сервисы работают и с какими хранилищами.

G>>Тут даже комментировать не буду. В дотнете и жабе полно средств изоляции. При желании даже в JS можно инкапсуляцию сделать, чтобы до потрохов никто не добрался.

S>Как раз в JS-то можно. Но, опять же, можно!=делают.
Так и в MSA изоляция зачастую условная. Да, у каждого сервиса база своя, но сервер БД один. Дорого поднимать несколько экзепляров даже постгреса. И учетные данные одни.
Если разработчикам не прививать дисциплину, то никакая изоляция не поможет.

G>>Так можно и в базу другого микросервиса полезть.

S>Невозможно. Базу чужого микросервиса даже не видно. И даже если вдруг чужая база лежит на той же машине, то доступ к ней выдан другому сервис-аккаунту, чем тот, под которым бегает этот микросервис.
Ну-ну. Это на практике может быть в бигтехе, где базы нарезаются отдельной командой и не входят в профиль деплоя. Но пока все приложение это одни набор yaml файлов для docker\kuber — пролезть можно. Опять-таки если недостаточная дисциплина.

Один раз премии лишить за рефлекшн без необходимости и сразу желание пропадет так писать.

G>>В случае единой репы и нормального архитектора — на ревью бить по рукам (или по лицу) за такой код.

S>Это всё хорошо работает до определённых размеров кодовой базы, команды, и уровня ответственности за промахи. Там, где за ошибки принято тюремное заключение или суровые штрафы, делают именно так, не полагаясь на бдительность peer review и личность архитектора.
Ну давай честно: есть предел размера команды или предел структуры управления для монолита. С этим никто не спорит.
Далее возможно развитие или по пути MSA или по пути "ядро\платформа и прикладная часть".
Но до этого предела монолит превосходит MSA по любым параметрам.

G>>Я именно об этом и говорю. Знаю один банк где над таким ядром-"микросервисом" трудится команда из 40+ только программистов. То есть ни разу он не "микро", о чем я и говорю. А выдача кредитов это вообще с десяток связанных процессов, на каждом из которых команда в 10+ человек. То есть их тоже "микро" назвать сложно.

S>Так "micro" это же не про размер команды. А про "площадь поверхности".
Не очень понятно в чем она измеряется.
Re[2]: Помогите правильно спроектировать микросервисное приложение
От: busk  
Дата: 03.04.25 09:07
Оценка:
Здравствуйте, Sinclair, Вы писали:


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

S>По вашему описанию не вполне понятно, что собственно должна делать система. Распределять заказы между водителями?
S>Смотрите:
S>0. Сервис аутентификации. Его хочется делать крайне надёжной, т.к. если никто не может зайти, то не работает вообще всё. Очень опасно делать монолит, в котором выкат какой-нибудь мелкой фичи типа "а давайте поздравим всех водителей-женщин с 8 марта" способен сломать логин. То есть эту штуку мы как можно реже релизим, и при каждом выкате покрываем тестами в шесть слоёв. Отдельные пацаны аудируют код на предмет потенциальных уязвимостей, т.к. кража базы паролей — почти самое плохое, что с нами вообще может случиться. Сам сервис скорее всего раскатан в нескольких экземплярах с геодистрибуцией и офигенной избыточностью — чтобы отключение света в нижегородском ДЦ не валило всю федеральную сеть водителей.
S>(Кстати, очень часто мы захотим это вообще делегировать наружу, чтобы не заниматься дорогостоящими инвестициями в эти пять девяток и прочие особенности. Привинчиваем OAuth и полагаемся на Google/Facebook/etc)
S>1. Сервис авторизации. После того, как пользователь залогинился, нам нужно ещё и получить набор его прав. В большинстве случаев это довольно-таки типовая структура, вроде ролей. Опять же, требования к ней довольно-таки жёсткие, т.к. она нужна в каждом первом сценарии. Ещё можно заметить, что она в значительной мере дублирует информацию из 0-й системы — как минимум, у нас должен быть тот же самый список пользователей. Поэтому в современном мире зачастую 0 и 1 объединяют в одну.



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

а вопрос такой: если это будет винда и один сервер то как проще организовать архитектуру двух сервисов (аутенфикация + авторизация) и всего остального?
докер или один общий шлюз (я так понял приложение) и дальше запрос идет либо на сервис 1 либо на сервис 2?
Re[14]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 03.04.25 10:41
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>>>·>Гы. Как минимум, память и gc — разделяемые. Так что когда запрос какого-нибудь отчёта для аналитики вдруг роняет по OOM всё сразу, в т.ч. обработку заказов — ничего хорошего.

G>>>Ничего не мешает в рамках монолитного решения вынести отдельный инстанс для обработки тяжелых запросов и на уровне балансера распределять.
G>·>Видимо ты тут намекаешь на какую-то принципиальную разницу между SOA и MSA?
...
G>Тут и паттерны, и технологии, и оргструктуры и много чего еще.
Ок, можно согласится что в разных контекстах/организациях под SOA/MSA понимаются разные вещи. Нет никакого официального математически строгого определения.
Я рассуждаю с т.з. "собираем много разных бинарей, деплоим в разные места с разными опциями".

G>>>Еще раз повторю тезис: MSA это больше про оргструктуру и следующие за ней технические решения.

G>·>По-моему, ты причину со следствием путаешь.
G>А может и наоборот, не задумывался об этом?
Задумывался. Вполне возможно где-то может быть и наоборот в соответствии с карго-культом.

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

Иногда нужны — если есть соответствующие технические требования.

G>·>А зачем обязательно один бинарник? Если деплоймент разный всё равно. Чтобы были многочасовые билды?

G>А ты думаешь сборка одного толстого бинарника медленнее, чем сборка трех бинарников потоньше? Или ты думаешь что обязательно пересобирать все проекты каждый раз?
Гы. Трёх? Ты тут выше написал о сотне строк на сервис. За "монолит" объёмом на 3 сотни — я всеми конечностями.

G>Да и в целом время сборки для деплоя мало интересует, интересует время от команды run до запуска приложения, если ты это делаешь 25 раз в день.

В мире soa/msa часто бывает так, что запустить приложение ты просто не можешь командой run. Ресурсов на рабочей машине не хватит. Так что run запускаются только некоторые части, чаще просто тесты.

G>·>Сборка, деплой и перезапуск мелкого сервиса занимает минуты от момента мержа PR. Тогда как типичная выкатка монолита — приятно проведённые выходные.

G>В теории да. Но внезапно:
G>1) в микросервисах надо тупо больше кода править
зато общий impact правок ниже.

G>2) если ты затрагиваешь несколько сервисов своим изменением, то несколько сервисов собираются дольше чем один

В msa у тебя будет не три бинарника, а хотя бы тридцать. И работая над очередной фичей типично трогаешь 1-2, в плохих случаях 4-5 бинарников. А вот сборка 2 бинарников от 30 отличается уже на порядок.

G>3) даже докер умеет кэшировать образы, поэтому вполне можно сделать сбору с кэшированием и не пересобирать то, что не изменилось

А дальше? Как потом бинарник будет решать кем он сейчас работает — rest-сервером или fix-коннектором или распределённым кешем? Потребуется нетривиальная система конфигов.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 03.04.2025 11:02 · . Предыдущая версия .
Re[15]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 03.04.25 13:04
Оценка:
Здравствуйте, ·, Вы писали:

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

·>Иногда нужны — если есть соответствующие технические требования.
Предположим:
Команда из 10 человек, 7 разрабы, 3 аналитики и тестеры. Используется всеми один стек: условно дотнет и TS\react на клиенте.
Какие могут быть технические требования, чтобы с микросервисами они реализовывались проще, чем без них?


G>>·>А зачем обязательно один бинарник? Если деплоймент разный всё равно. Чтобы были многочасовые билды?

G>>А ты думаешь сборка одного толстого бинарника медленнее, чем сборка трех бинарников потоньше? Или ты думаешь что обязательно пересобирать все проекты каждый раз?
·>Гы. Трёх? Ты тут выше написал о сотне строк на сервис. За "монолит" объёмом на 3 сотни — я всеми конечностями.
уже на трех одновременно пересобираемых микросервисах время сборки монолита оказывается меньше.

G>>Да и в целом время сборки для деплоя мало интересует, интересует время от команды run до запуска приложения, если ты это делаешь 25 раз в день.

·>В мире soa/msa часто бывает так, что запустить приложение ты просто не можешь командой run. Ресурсов на рабочей машине не хватит. Так что run запускаются только некоторые части, чаще просто тесты.
Довольно странное заявление. Почему может не хватить ресурсов запустить проекта, который ничего не делает? Только кривостью рук разработчиков может быть такое решение обосновано.

G>>·>Сборка, деплой и перезапуск мелкого сервиса занимает минуты от момента мержа PR. Тогда как типичная выкатка монолита — приятно проведённые выходные.

G>>В теории да. Но внезапно:
G>>1) в микросервисах надо тупо больше кода править
·>зато общий impact правок ниже.
Я не понимаю что это. Что значит "общий импакт"? Я считаю количество строк\методов\объектов.

G>>2) если ты затрагиваешь несколько сервисов своим изменением, то несколько сервисов собираются дольше чем один

·>В msa у тебя будет не три бинарника, а хотя бы тридцать. И работая над очередной фичей типично трогаешь 1-2, в плохих случаях 4-5 бинарников. А вот сборка 2 бинарников от 30 отличается уже на порядок.
Тогда непонятно с чем ты споришь. Всегда найдется N, такое, что пересборка N микросервисов будет дольше чем пересборка монолита с аналогичным функционалом. Причем это N оказывается довольно небольшим. По моей практике N бывает от 3 до 5.

G>>3) даже докер умеет кэшировать образы, поэтому вполне можно сделать сбору с кэшированием и не пересобирать то, что не изменилось

·>А дальше? Как потом бинарник будет решать кем он сейчас работает — rest-сервером или fix-коннектором или распределённым кешем? Потребуется нетривиальная система конфигов.
Она все равно O(1), то пишется один раз и потом вносятся правки, независимые от объема изменений кода.
А кот конфиг для микросервисов зачастую O(N), так как развитие систем на базе MSA приводит к росту числа микросервисов, что в свою очередь увеличивает объем конфигов.
Re[3]: Помогите правильно спроектировать микросервисное приложение
От: Sinclair Россия https://github.com/evilguest/
Дата: 03.04.25 13:56
Оценка: 3 (1)
Здравствуйте, busk, Вы писали:

B>а вопрос такой: если это будет винда и один сервер то как проще организовать архитектуру двух сервисов (аутенфикация + авторизация) и всего остального?

B>докер или один общий шлюз (я так понял приложение) и дальше запрос идет либо на сервис 1 либо на сервис 2?
Обычно никаких шлюзов нет, и пользователь явно идёт за токеном в сервис номер 1, а уже потом с этим токеном идёт в сервис номер 2.

Можем посмотреть на пример:

Сервис номер 2 расположен по адресу https://graph.microsoft.com/{version}/{resource}?{query-parameters}.
Токены для него получаем из сервиса номер 1 по адресу https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize

Наличие в сервисе 1 авторизации означает, что у вас сервис номер один возвращает в токене не просто идентити пользователя, а и некий набор привилегий, которые сервис номер 2 считывает для принятия решения о том, чего пользователю делать можно, а чего — нельзя. В частности, для Microsoft Graph мы прямо в запросе авторизационного токена сервису логина указываем список scopes, к которым хотим получить доступ. И в случае успеха, токен будет содержать информацию об этом.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[7]: Помогите правильно спроектировать микросервисное приложение
От: Sharov Россия  
Дата: 03.04.25 15:39
Оценка:
Здравствуйте, ·, Вы писали:

·>И да, сервисы не через REST взаимодйествуют, а через pub-sub сообщения (kafka/29west/mq/fix/аналоги).


А в чем проблема взаимодействовать по REST'у?
Кодом людям нужно помогать!
Re[8]: Помогите правильно спроектировать микросервисное приложение
От: · Великобритания  
Дата: 03.04.25 20:49
Оценка:
Здравствуйте, Sharov, Вы писали:

S>·>И да, сервисы не через REST взаимодйествуют, а через pub-sub сообщения (kafka/29west/mq/fix/аналоги).

S>А в чем проблема взаимодействовать по REST'у?
Отсутствие гибкости. Rest — это синхронный p2p request-response только. Притом с большим оверхедом в виде http обёртки.
Сообщения — это кто угодно с кем угодно, streaming, множество отправителей/получателей и т.п. В MSA без такого трудно обойтись.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[16]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 03.04.25 21:02
Оценка:
Здравствуйте, gandjustas, Вы писали:

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

G>·>Иногда нужны — если есть соответствующие технические требования.
G>Предположим:
G>Команда из 10 человек, 7 разрабы, 3 аналитики и тестеры. Используется всеми один стек: условно дотнет и TS\react на клиенте.
G>Какие могут быть технические требования, чтобы с микросервисами они реализовывались проще, чем без них?
Ну тут Sinclair вроде рассказывал варианты. Могу рассказать что я видал, но там много чего, а не просто ts/react. Сразу скажу, это торговые системы, где процент веба около нуля.

G>>>А ты думаешь сборка одного толстого бинарника медленнее, чем сборка трех бинарников потоньше? Или ты думаешь что обязательно пересобирать все проекты каждый раз?

G>·>Гы. Трёх? Ты тут выше написал о сотне строк на сервис. За "монолит" объёмом на 3 сотни — я всеми конечностями.
G>уже на трех одновременно пересобираемых микросервисах время сборки монолита оказывается меньше.
Очень сомневаюсь, когда бинарников десятки.

G>>>Да и в целом время сборки для деплоя мало интересует, интересует время от команды run до запуска приложения, если ты это делаешь 25 раз в день.

G>·>В мире soa/msa часто бывает так, что запустить приложение ты просто не можешь командой run. Ресурсов на рабочей машине не хватит. Так что run запускаются только некоторые части, чаще просто тесты.
G>Довольно странное заявление. Почему может не хватить ресурсов запустить проекта, который ничего не делает? Только кривостью рук разработчиков может быть такое решение обосновано.
А зачем вообще запускать что-то, что ничего не делает? Ты уж определись.

G>·>зато общий impact правок ниже.

G>Я не понимаю что это. Что значит "общий импакт"? Я считаю количество строк\методов\объектов.
Что обновив какой-нибудь условный коннектор с платёжной системой можно сломать только платежи, а не всю систему.

G>>>2) если ты затрагиваешь несколько сервисов своим изменением, то несколько сервисов собираются дольше чем один

G>·>В msa у тебя будет не три бинарника, а хотя бы тридцать. И работая над очередной фичей типично трогаешь 1-2, в плохих случаях 4-5 бинарников. А вот сборка 2 бинарников от 30 отличается уже на порядок.
G>Тогда непонятно с чем ты споришь. Всегда найдется N, такое, что пересборка N микросервисов будет дольше чем пересборка монолита с аналогичным функционалом. Причем это N оказывается довольно небольшим. По моей практике N бывает от 3 до 5.
Ну видимо какие-то простые маленькие системы в твоей практике были.

G>·>А дальше? Как потом бинарник будет решать кем он сейчас работает — rest-сервером или fix-коннектором или распределённым кешем? Потребуется нетривиальная система конфигов.

G>Она все равно O(1), то пишется один раз и потом вносятся правки, независимые от объема изменений кода.
G>А кот конфиг для микросервисов зачастую O(N), так как развитие систем на базе MSA приводит к росту числа микросервисов, что в свою очередь увеличивает объем конфигов.
Так растёт число не само по себе, а потому что добавляется новая функциональность, которую надо конфигурить — адреса-пароли-явки.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[9]: Помогите правильно спроектировать микросервисное приложение
От: Sharov Россия  
Дата: 03.04.25 22:33
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Sharov, Вы писали:


S>>·>И да, сервисы не через REST взаимодйествуют, а через pub-sub сообщения (kafka/29west/mq/fix/аналоги).

S>>А в чем проблема взаимодействовать по REST'у?
·>Отсутствие гибкости. Rest — это синхронный p2p request-response только. Притом с большим оверхедом в виде http обёртки.
·>Сообщения — это кто угодно с кем угодно, streaming, множество отправителей/получателей и т.п. В MSA без такого трудно обойтись.

А что мешает его сделать асинхронным?
Кодом людям нужно помогать!
Re[9]: Помогите правильно спроектировать микросервисное приложение
От: Sinclair Россия https://github.com/evilguest/
Дата: 04.04.25 01:14
Оценка:
Здравствуйте, ·, Вы писали:

·>Отсутствие гибкости. Rest — это синхронный p2p request-response только. Притом с большим оверхедом в виде http обёртки.

·>Сообщения — это кто угодно с кем угодно, streaming, множество отправителей/получателей и т.п. В MSA без такого трудно обойтись.
Вот с чем никогда в жизни не работал — так это с описываемой вами архитектурой.
Как в ней решаются вопросы
1. Согласованности: чтобы вся эта слабосвязанная мешанина реально делала то, что нужно?
2. Зависимостей — как мне понять, сколько сервисов нужно запустить, чтобы реализовался сценарий X? Чтобы не получилось, как в анекдоте: "один копает ямы, а другой закапывает; ещё один должен был туда деревья вставлять, но он не пришёл".
3. Предотвращения лайв-локов: один сервис ждёт с шины сообщение А, потом отправит сообщение Б. Другой сервис как раз ждёт сообщение Б, чтобы отправить А

Если есть какая-то книжка на эту тему — отправьте в неё, пожалуйста
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Помогите правильно спроектировать микросервисное при
От: Miroff Россия  
Дата: 04.04.25 03:24
Оценка: 100 (2) +2
Здравствуйте, Sinclair, Вы писали:

S>Вот с чем никогда в жизни не работал — так это с описываемой вами архитектурой.


В такой архитектуре, думают не через сценарии, а через доменные сущности, их состояния и переходы между ними. Сквозные сценарии либо реализуются сами собой, в том числе и те, о которых бизнес аналитики даже и не подозревали, либо путем создания дополнительных доменных сущностей по общему правилу:
1. Сервис отвечает за свои, и только свои, доменные объекты и является для них единственным источником правды
2. Сервис принимает команды на изменение состояния своих объектов
3. Сервис отправляет нотификации о ВСЕХ изменениях ВСЕХ своих объектов независимо от того, нужна кому-то эта информация или не нужна
4. Сервис может подписываться только на нотификации об изменениях в других сервисах и только на команды, адресованные непосредственно этому сервису. Нельзя подписаться на команды другого сервиса.

S>1. Согласованности: чтобы вся эта слабосвязанная мешанина реально делала то, что нужно?


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

S>2. Зависимостей — как мне понять, сколько сервисов нужно запустить, чтобы реализовался сценарий X? Чтобы не получилось, как в анекдоте: "один копает ямы, а другой закапывает; ещё один должен был туда деревья вставлять, но он не пришёл".


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

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

S>3. Предотвращения лайв-локов: один сервис ждёт с шины сообщение А, потом отправит сообщение Б. Другой сервис как раз ждёт сообщение Б, чтобы отправить А


Это не RPC, сервис ничего не ждёт. Сервис отправляет запрос на изменение и забывает о нем. Если сервису важно, чтобы запрос отработал в за какой-то тайм-аут, он проверяет изменения спустя какое-то время и если изменений не произошло, шлет команду на отмену.

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

Рассылки это новый домен относительно пользователй и туров, значит нужен новый сервис. Этому сервису необходимы пользователи, туры и поездки, значит подписываемся на изменения в трёх доменах. От пользователей храним е-мейл и возраст, от тура храним описание, от поездки храним сам факт связи пользователя и тура. Все это складываем в контекст и из контекста извлекаем что кому рекомендовать. При рассылке отправляем нотификации: создана рассылка для пользователя X приглашающая в туры {Y}. И такая система получается очень гибкой и расширяемой. Если нам нужно поменять правила рассылки, мы меняем один сервис. Если нужны новые данные, добавляем подписки. Если нужно действие после рассылки, добавляем нового подписчика. И т.п.

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

S>Если есть какая-то книжка на эту тему — отправьте в неё, пожалуйста


Можно начать со знакомства с моделью акторов на практике
https://www.oreilly.com/library/view/reactive-messaging-patterns/9780133846904/
https://www.oreilly.com/library/view/applied-akka-patterns/9781491934876/ch01.html
Либо поискать что-то более фундаментальное.
Отредактировано 08.04.2025 7:53 Miroff . Предыдущая версия .
Re[10]: Помогите правильно спроектировать микросервисное приложение
От: TG  
Дата: 04.04.25 05:55
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вот с чем никогда в жизни не работал — так это с описываемой вами архитектурой.

S>Как в ней решаются вопросы
S>1. Согласованности: чтобы вся эта слабосвязанная мешанина реально делала то, что нужно?
S>2. Зависимостей — как мне понять, сколько сервисов нужно запустить, чтобы реализовался сценарий X? Чтобы не получилось, как в анекдоте: "один копает ямы, а другой закапывает; ещё один должен был туда деревья вставлять, но он не пришёл".
S>3. Предотвращения лайв-локов: один сервис ждёт с шины сообщение А, потом отправит сообщение Б. Другой сервис как раз ждёт сообщение Б, чтобы отправить А

S>Если есть какая-то книжка на эту тему — отправьте в неё, пожалуйста


Только оркестрацией сервисов. Например, https://github.com/temporalio/temporal.
Re[11]: Помогите правильно спроектировать микросервисное приложение
От: TG  
Дата: 04.04.25 06:01
Оценка: +1
Здравствуйте, Miroff, Вы писали:

Как у Вас это:
M>Разумеется, у такой архитектуры полно недостатков: нужен глубокий анализ домена, нужно бить по рукам тем, кто пытается домен ломать, полная схема взаимодействий становится очень сложной и превращается в комбинаторную стейт машину через которую практически невозможно протащить единичный сценарий, авторизация становится нетривиальным занятием.
сочетается с этим:
M> Но зато можно поддерживать небольшой командой в 10-15 человек несколько сотен микросервисов почти не напрягаясь.
?

Что понимается под поддержкой?
Re[11]: Каких программ вам не хватает?
От: Miroff Россия  
Дата: 04.04.25 06:11
Оценка: -1
Здравствуйте, Sinclair, Вы писали:

S>Это заблуждение. Авторизация проверяется совершенно отдельно. Ещё не хватало в представлении ресурса показывать разный набор ссылок в зависимости от transient штуки вроде Bearer-токена.


Можно это как-то обосновать? Мне кажется, мы о разных вещах говорим. Есть ресурс, есть набор связанных ресурсов с которыми можно проделывать какие-то операции. HATEOS позволяет НЕ ВЫЗЫВАЯ операций предсказать, будут они доступны клиенту или нет. Например, в списке сотрудников рядом с именем Sinclair либо показывать кнопку "объявить выговор" или не показывать. И чтобы это реализовать без HATEOAS часть логики авторизации, кому можно налагать взыскания и на кого, вынужденно скопируется из сервиса в его клиента. А поскольку мы сейчас обсуждаем микросервисную архитектуру, где акторами являются не только пользователи, но и другие сервисы, эта логика начинает неконтролируемо расползаться по системе. Как в примере из соседнего поста, когда сервис туров следит чтобы определенные туры были доступны только совершеннолетним пользователям и эта же логика дублируется в сервисе рекомендаций чтобы не показывать несоврешеннолетним то, что им и так недоступно. HATEOAS бы эту проблему решил докинув ресурс user список доступных для него туров.

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


Опять же, можешь раскрыть тему? Пейджинг это простая штука если перестать думать о нем, как о RPC и начать думать как об отдельном ресурсе. Ты СОЗДАЕШЬ отдельный объект "поисковый запрос" и в ответ получаешь список страниц с результатами этого запроса. И, внезапно, у тебя уже нет проблем со стабильностью, сортировки, изменению состава страниц, кэшированию и т.п. Результат фиксируется в момент создания запроса и далее до нового поиска уже не меняется.

S>В частности, приделывание к платёжке ссылки refund с методом POST — это RMM не L3, а L1. Потому, что нормальный способ — это PUT либо PATCH, которые прямым либо косвенным образом меняют состояние платежа на refunded.


Нет. POST refund это создание ДРУГОГО ресурса. Другого потому что у него другой URI. Нужно думать о рефанде не как об изменении платежа, а как ос создании нового объекта — сторнирующей операции, потому что сам платеж изменять не позволяет принцип двойной бухгалтерии известный с 17 века: сделанная проводка не может быть удалена.

S>Конечно позволяет. Вот у вас есть объект, у него есть представление. Всё, можно делать PUT этого представления. Если что-то запрещено бизнес-правилами — приедет 409. Если правами доступа — 403.


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

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


Людям вообще нравится RPC, даже по этой теме видно. Но RPC не дает необходимой для микросервисов изоляции и довольно быстро превращает приложение в распределенный монолит.
Re[5]: Помогите правильно спроектировать микросервисное приложение
От: Miroff Россия  
Дата: 04.04.25 06:19
Оценка:
Здравствуйте, TG, Вы писали:

TG>API приложения полностью зависит от нужд потребителя. Договариваться придётся.


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

Альтернатива — дизайн от доменной модели, когда ты определяешь доменные сущности, их состояния и переходы между ними и выставляешь полное API для манипулирования этими сущностями не беспокоясь о том, нужно кому-то это прямо сейчас или нет. Это позволяет клиентам твоего сервиса реализовать любой сценарий не дергая тебя и не требуя изменений твоего API. Это и есть полноценная микросервисная архитектура.
Re[6]: Помогите правильно спроектировать микросервисное приложение
От: TG  
Дата: 04.04.25 07:14
Оценка:
Здравствуйте, Miroff, Вы писали:

TG>>API приложения полностью зависит от нужд потребителя. Договариваться придётся.


M>Дизайн от сценариев лишь один из возможных подходов. Если ты дизайнишь API в расчете на пользователя, пользователь, конечно рад, но при каждом изменении сценариев потребителя тебе приходится подстраивать под них свое API, а если потребителей много то еще и увязывать их сценарии между собой. В результате ты делаешь кучу ненужной работы, переусложняешь свой сервис и в конце концов обнаруживаешь себя внутри распределенного монолита.


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

M>Альтернатива — дизайн от доменной модели, когда ты определяешь доменные сущности, их состояния и переходы между ними и выставляешь полное API для манипулирования этими сущностями не беспокоясь о том, нужно кому-то это прямо сейчас или нет. Это позволяет клиентам твоего сервиса реализовать любой сценарий не дергая тебя и не требуя изменений твоего API. Это и есть полноценная микросервисная архитектура.


Что Вы понимаете под доменной моделью?
И что значит "полное API"?
Re[7]: Помогите правильно спроектировать микросервисное приложение
От: Miroff Россия  
Дата: 04.04.25 07:25
Оценка:
Здравствуйте, TG, Вы писали:

TG>И если клиент готов платить, почему не делать?


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

TG>Что Вы понимаете под доменной моделью?


https://martinfowler.com/bliki/DomainDrivenDesign.html

TG>И что значит "полное API"?


Возможность достигнуть любого состояния системы посредством API. CRUD на все доменныек сущности плюс переходы по всем возможным состояниям.
Re[10]: Помогите правильно спроектировать микросервисное приложение
От: · Великобритания  
Дата: 04.04.25 07:32
Оценка:
Здравствуйте, Sharov, Вы писали:

S>·>Отсутствие гибкости. Rest — это синхронный p2p request-response только. Притом с большим оверхедом в виде http обёртки.

S>·>Сообщения — это кто угодно с кем угодно, streaming, множество отправителей/получателей и т.п. В MSA без такого трудно обойтись.
S>А что мешает его сделать асинхронным?
Спека http. Я наверное неточно выразился, я имел в виду, что на каждый запрос приходит ответ, который надо прочитать, даже если это "204 No Content". Притом ответ ровно один.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[8]: Помогите правильно спроектировать микросервисное приложение
От: TG  
Дата: 04.04.25 10:09
Оценка:
Здравствуйте, Miroff, Вы писали:

TG>>И если клиент готов платить, почему не делать?

M>Если цель доить заказчиков, это отличный вариант. Именно так и получаются команды в 400 разработчиков для поддержки небольшой региональной логистической системы.

Пусть об этом голова у менеджмента болит.

TG>>И что значит "полное API"?

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

Так сервис может не ограничиваться CRUD-ом и состояниями.
Построение маршрута там же Яндекс.Карты, например.
Какое там "полное API" для всех клиентов можно выкатить?
Re[9]: Помогите правильно спроектировать микросервисное приложение
От: Miroff Россия  
Дата: 04.04.25 10:16
Оценка: +1
Здравствуйте, TG, Вы писали:

TG>Так сервис может не ограничиваться CRUD-ом и состояниями.

TG>Построение маршрута там же Яндекс.Карты, например.
TG>Какое там "полное API" для всех клиентов можно выкатить?

Посмотри на API OpenStreetMap, версия 0.7 вышла 15 лет назад когда, еще JSON не изобрели, и с тех пор ее не апдейтили потому что и так хорошо вышло. Так бывает, когда API проектируют инженеры, а не менеджеры.
Re[10]: Помогите правильно спроектировать микросервисное приложение
От: TG  
Дата: 04.04.25 10:37
Оценка:
Здравствуйте, Miroff, Вы писали:

TG>>Так сервис может не ограничиваться CRUD-ом и состояниями.

TG>>Построение маршрута там же Яндекс.Карты, например.
TG>>Какое там "полное API" для всех клиентов можно выкатить?

M>Посмотри на API OpenStreetMap, версия 0.7 вышла 15 лет назад когда, еще JSON не изобрели, и с тех пор ее не апдейтили потому что и так хорошо вышло. Так бывает, когда API проектируют инженеры, а не менеджеры.

Это не означает, что все довольны.

Вот, допустим, я заказчик/клиент Яндекс.Карт, OSM и т.д.
Задача: построить маршрут с условиями:
— маршрут не должен проходить по дорогам общего пользования (в том числе грунтовым) и вообще не должен приближаться к ним ближе, чем на 5 км.
— если без пересечения дорог общего пользования не обойтись, то пересечение маршрута с ними должно идти под углом, близким к прямому
— нельзя прокладывать маршрут по: болотам, лесам, возвышенностям
— нельзя прокладывать маршрут по определенным зонам, задаваемым пользователем в запросе.

Какое готовое API известных сервисов картографии такое уже умеет?
Re[13]: Помогите правильно спроектировать микросервисное приложение
От: SkyDance Земля  
Дата: 04.04.25 17:26
Оценка:
·>Сборка, деплой и перезапуск мелкого сервиса занимает минуты от момента мержа PR. Тогда как типичная выкатка монолита — приятно проведённые выходные.

Беда приходит тогда, когда требуется поменять код, общий для многих/всех микросервисов. Например, добавить (m)TLS, или еще что-то такое. Любой инфраструктурный проект в МСА вызывает многолетнюю попаболь, и приводит к жутчайшим штуковинам типа Istio и прочим service-mesh'ам.
Re[8]: Помогите правильно спроектировать микросервисное приложение
От: Kernan Ниоткуда https://rsdn.ru/forum/flame.politics/
Дата: 05.04.25 15:51
Оценка: +1
Здравствуйте, Sharov, Вы писали:

S>А в чем проблема взаимодействовать по REST'у?

В том, что его применяют там, где он не нужен. У меня в последнее время есть сомнения что народ в массе понимает что такое REST и зачем он нужен.
Sic luceat lux!
Re[12]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 06.04.25 06:09
Оценка: 11 (2) +1
Здравствуйте, Miroff, Вы писали:

M>Можно это как-то обосновать?

Можно. Когда я делаю GET на ресурс, я получаю его представление. Крайне желательно, чтобы это представление зависело от состояния ресурса, а не от состояния пользователя, который его получает.
Например, можно воткнуть reverse proxy перед сервером приложения, и тогда он сможет снять значительную часть нагрузки с сервера.
Как только мы начинаем менять представление объекта в зависимости от пользователя, о кэшировании можно забыть.

M>Мне кажется, мы о разных вещах говорим. Есть ресурс, есть набор связанных ресурсов с которыми можно проделывать какие-то операции.

Тут всё верно. Ресурсы обычно связаны друг с другом не искусственными линками, а естественным образом, спрятанным в представлении.
Возьмём, к примеру, ресурс Payment.
Вот как выглядит представление этого ресурса через PayPal API:
{
  "id": "0VF52814937998046",
  "status": "CREATED",
  "amount": {
    "value": "10.99",
    "currency_code": "USD"
  },
  "invoice_id": "INVOICE-123",
  "seller_protection": {
    "status": "ELIGIBLE",
    "dispute_categories": [
      "ITEM_NOT_RECEIVED",
      "UNAUTHORIZED_TRANSACTION"
    ]
  },
  "payee": {
    "email_address": "merchant@example.com",
    "merchant_id": "7KNGBPH2U58GQ"
  },
  "expiration_time": "2017-10-10T23:23:45Z",
  "create_time": "2017-09-11T23:23:45Z",
  "update_time": "2017-09-11T23:23:45Z",
  "links": [
    {
      "rel": "self",
      "method": "GET",
      "href": "https://api-m.paypal.com/v2/payments/authorizations/0VF52814937998046"
    },
    {
      "rel": "capture",
      "method": "POST",
      "href": "https://api-m.paypal.com/v2/payments/authorizations/0VF52814937998046/capture"
    },
    {
      "rel": "void",
      "method": "POST",
      "href": "https://api-m.paypal.com/v2/payments/authorizations/0VF52814937998046/void"
    },
    {
      "rel": "reauthorize",
      "method": "POST",
      "href": "https://api-m.paypal.com/v2/payments/authorizations/0VF52814937998046/reauthorize"
    }
  ]
}

Этот ресурс, судя по всему, связан с другим ресурсом — invoice. Но никаких намёков на то, как получить информацию об этом ресурсе, внутри ресурса нет. И линков тоже нет. Зато есть линки про то, как изменить состояние этого же ресурса. Так что это никакой не REST.
В настоящем хардкорном RMM3 REST вместо атрибута invoice_id был бы атрибут invoice со значением "https://www.paypal.com/invoice/details/INVOICE-123".

M>HATEOS позволяет НЕ ВЫЗЫВАЯ операций предсказать, будут они доступны клиенту или нет.

Нет, ничего подобного HATEOAS не предполагает. Нет никакой гарантии, что операции будут доступны. Нет никакой гарантии, что никаких других операций нет.

M>Например, в списке сотрудников рядом с именем Sinclair либо показывать кнопку "объявить выговор" или не показывать. И чтобы это реализовать без HATEOAS часть логики авторизации, кому можно налагать взыскания и на кого, вынужденно скопируется из сервиса в его клиента.

Нет конечно. Зачем? Это крайне контрпродуктивная идея.
Во-первых, "объявление выговора" в REST-системе — это не операция с "сотрудником с именем Синклер". Это создание ресурса "выговор". А точнее — создание ресурса "приказ об объявлении выговора".
В котором могут фигурировать, к примеру, несколько сотрудников. И права на создание этого выговора весьма косвенно связаны с перечисленными там сотрудниками.
Во-вторых, построение безопасности путём скрытия действий в UI — это популярная, но плохая идея. В основном — потому, что она неимоверно бесит пользователей. Вот я сижу перед списком пользователей, хочу объявить выговор. Поискал в меню — нет такой команды. Поискал в свойствах сотрудника — нет такой кнопки. Полез в инструкцию — сказано "для объявления выговора нажмите на кнопку "Объявить выговор". И вот я уже полчаса думаю, то ли я дурак, то ли инструкция устарела, то ли программа глючит. Внезапно, "некрасивый" способ — дать мне нажать на кнопку, а потом вывести "403 у вас недостаточно прав для объявления выговора этому сотруднику" — в разы гуманнее по отношению к пользователю, чем предлагаемая вами идея.

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

Ничего никуда не расползается. Зачем? Микросервис просто делает свою работу. Поймите, нельзя строить микросервис на том, что "я прочитал ресурс — если нет ссылки, значит нельзя рефандить и надо идти по ветке А, а если ссылка есть — то надо идти по ветке Б и выполнять рефанд". Возможно, настройки прав или ешё что-то поменялось между чтением ресурса и переходом по ссылке; всегда можно получить в ответ "403". Так что вместо вот этой вот двухшаговой наркомании "если ссылка, то идём по ветке Б, и если получили 403, то переключаемся обратно на ветку А". Просто идём и пытаемся сделать рефанд, не делая никаких предположений о правах там и всём остальном.

M>Как в примере из соседнего поста, когда сервис туров следит чтобы определенные туры были доступны только совершеннолетним пользователям и эта же логика дублируется в сервисе рекомендаций чтобы не показывать несоврешеннолетним то, что им и так недоступно. HATEOAS бы эту проблему решил докинув ресурс user список доступных для него туров. ,


Это очень, очень плохая архитектура. В первую очередь потому, что вы инвертируете зависимости. Теперь у вас ресурс user (и его микросервис) обязан знать всё о турах (которые вообще-то обрабатываются другим микросервисом).
А ресурс "тур", ясное дело, должен знать всё о пользователях. Поздравляю, вы получили не просто монолит, а трудноразвиваемый монолит.

M>Опять же, можешь раскрыть тему? Пейджинг это простая штука если перестать думать о нем, как о RPC и начать думать как об отдельном ресурсе. Ты СОЗДАЕШЬ отдельный объект "поисковый запрос" и в ответ получаешь список страниц с результатами этого запроса. И, внезапно, у тебя уже нет проблем со стабильностью, сортировки, изменению состава страниц, кэшированию и т.п. Результат фиксируется в момент создания запроса и далее до нового поиска уже не меняется.

И это — тоже очень, очень плохая архитектура. Смотрите как это работает: "поисковый запрос" — это эфемерный ресурс. Последнее, что мы хотим — это хранить его. В первую очередь по соображениям масштабирования. Сколько пользователей вы ожидаете ежедневно? Двух? Как только у вас появляется хотя бы несколько сотен тысяч запросов в сутки, хранение (даже в течение ограниченного времени) их эфемерных запросов становится неподъёмным.
Как раз наоборот — пользователь, который получил в ответ на поиск информацию о том, что нашлось 100000 страниц, скорее всего выполнит новый запрос, потому что этот оказался недостаточно точным.
Нет, бывают такие ситуации, когда поиск сам по себе очень дорогой, и мы как отдельную фичу предлагаем "сохранение запросов", да ещё и иногда с оповещениями об изменениях. Но в простых гражданских случаях это не работает, и поиск делается поверх реалтаймовых данных, а не поверх какого-то снапшота, полученного неведомо когда.
И вот в такой обстановке "традиционный" пейджинг в стиле "дай мне позиции с 120 по 139" — тупиковая ветвь проектировщицкой мысли. Единственное его достоинство — простота реализации.
Зато недостатки у него принципиальные — в частности, невозможность восстановить целостную картину.

Есть несколько способов решить эту проблему. Один из них — примерно то, что вы предлагаете, "думать о запросе как об отдельном ресурсе", только правильно реализованный.
Правильно — значит иметь один ресурс "поисковый запрос". Несмотря на его эфемерность, он вполне реален. То есть не два разных ресурса "пользователи на букву А с 0 по 99" и "пользователи на букву А с 100 по 199", а один ресурс "пользователи на букву А". Если этот ресурс слишком велик для прожёвывания его одним запросом, то мы добавляем к запросу range header и тащим комфортными для нас частями.
А непротиворечивость гарантируется тем, что последующие запросы мы дооборудуем хидером if-match/if-none-match, что позволяет нам заметить момент, когда данные на серверной стороне поменялись, и закешированную версию нужно выкинуть.

M>Нет. POST refund это создание ДРУГОГО ресурса. Другого потому что у него другой URI. Нужно думать о рефанде не как об изменении платежа, а как ос создании нового объекта — сторнирующей операции, потому что сам платеж изменять не позволяет принцип двойной бухгалтерии известный с 17 века: сделанная проводка не может быть удалена.

Отлично, давайте зайдём с этого конца. Хотя там, если покопать в API, будет пачка методов по изменению именно того же объекта (https://developer.paypal.com/docs/api/payments/v2/#authorizations_reauthorize).
В нормальном REST избегают POST методов, поскольку в случае потери результата POST крайне сложно понять, чем там кончилось дело, и что делать теперь.
К сожалению, авторы PayPal API книжек по REST-у не читали. Поэтому у них нет ничего идемпотентного.

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

Простите, но вы рассказываете какие-то небылицы. Что такое "пречеки"? Мы пробежались по всем нужным ресурсам, и убедились, что все нужные нам "действия" торчат в виде линков?
Ну так это ничего не значит. Посмотрите в тот же PayPal API — там очень редко бывает так, что достаточно POST с пустым телом. То есть — результат будет зависеть от параметров запроса. А их вы никаким пречеком не проверите (если в API нет специального метода precheck). Поэтому — да, либо rollback (если вдруг у нас есть детерминированный способ сделать rollback), либо rollforward. Чудес не бывает.
Если вы собрались изображать в любой архитектуре, хоть микросервисной, хоть монолитной, композитный сценарий вроде "запросили кредит — перевели деньги на расчётный счёт — расплатились за покупку товара", то он всегда может сфейлиться на каждом этапе. В прошлом веке для этого применяли протокол двухфазного коммита. Сейчас у нас два варианта:
— реализовать двухфазный коммит (каждый из этапов на самом деле делается в виде двух отдельных методов типа "подготовить ресурс" и "использовать ресурс"; при этом подготовленный ресурс после некоторого таймаута саморассасывается.
— понимать, что мы можем застрять посреди процесса — например, с выданным кредитом, но без дефицитной игровой приставки, которую успели купить.
Никакие "пречеки" тут не помогут, если нет реального резервирования ресурсов.

M>Людям вообще нравится RPC, даже по этой теме видно. Но RPC не дает необходимой для микросервисов изоляции и довольно быстро превращает приложение в распределенный монолит.

RPC людям нравится не за то, что у него есть преимущества, а за то, что они не сразу видят его недостатки.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[13]: Каких программ вам не хватает?
От: TG  
Дата: 07.04.25 06:58
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Во-вторых, построение безопасности путём скрытия действий в UI — это популярная, но плохая идея. В основном — потому, что она неимоверно бесит пользователей. Вот я сижу перед списком пользователей, хочу объявить выговор. Поискал в меню — нет такой команды. Поискал в свойствах сотрудника — нет такой кнопки. Полез в инструкцию — сказано "для объявления выговора нажмите на кнопку "Объявить выговор". И вот я уже полчаса думаю, то ли я дурак, то ли инструкция устарела, то ли программа глючит. Внезапно, "некрасивый" способ — дать мне нажать на кнопку, а потом вывести "403 у вас недостаточно прав для объявления выговора этому сотруднику" — в разы гуманнее по отношению к пользователю, чем предлагаемая вами идея.


Ну, идея как-то промаркировать элементы в списке, с которыми я могу что-то делать и с которыми не могу, вполне нормальная.
Жмякнуть на кнопку, подождать стандартный таймаут в 60 сек. и получить отлуп, может выбешивать не меньше.
Re[14]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.04.25 07:29
Оценка:
Здравствуйте, TG, Вы писали:

TG>Ну, идея как-то промаркировать элементы в списке, с которыми я могу что-то делать и с которыми не могу, вполне нормальная.

Нет.
TG>Жмякнуть на кнопку, подождать стандартный таймаут в 60 сек. и получить отлуп, может выбешивать не меньше.
Не очень понятно, откуда взялся "стандартный таймаут". Большинство сервисов отвечают в течение 1-2 секунд. Это я говорю про обращения с другого континента.
А уж если им и делать ничего не надо, а только вернуть ошибку — то ещё быстрее.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Каких программ вам не хватает?
От: TG  
Дата: 07.04.25 08:46
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, TG, Вы писали:


TG>>Ну, идея как-то промаркировать элементы в списке, с которыми я могу что-то делать и с которыми не могу, вполне нормальная.

S>Нет.

Сайты с бесплатными объявлениями бывают вываливают в поиске архивные объявления и какие-то кнопки там неактивны.
Мы такой подход не одобряем (с), но тамошние маркетологи, видимо, имеют другое мнение.
Я не призываю делать бан кнопок через HATEOAS. Но чем сама идея такого UI плоха?

TG>>Жмякнуть на кнопку, подождать стандартный таймаут в 60 сек. и получить отлуп, может выбешивать не меньше.

S>Не очень понятно, откуда взялся "стандартный таймаут".

"Стандартный таймаут" — который часто выставляет фреймворк, например, WCF. Кто-то вообще не заморачивается на эти таймауты и оставляет всё по умолчанию.

S> Большинство сервисов отвечают в течение 1-2 секунд. Это я говорю про обращения с другого континента.

S>А уж если им и делать ничего не надо, а только вернуть ошибку — то ещё быстрее.

Это, скажем так, в норме. За городом на мобильном интернете лаг уже составляет секунды.
Да и в "городском" энтерпрайзе пользователи могут столкнуться с тормозами, если они сидят через VPN и админы как-то криво "раскрасили" трафик.
Re[16]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.04.25 11:22
Оценка: 2 (1)
Здравствуйте, TG, Вы писали:

TG>Я не призываю делать бан кнопок через HATEOAS. Но чем сама идея такого UI плоха?

Сама идея такого UI плоха тем, что
1. не даёт пользователю никакой информации о причинах того, что кнопка неактивна. То ли у пользователя нет прав; то ли у объявления истёк срок доступности этой кнопки; то ли просто баг на сайте.
2. даёт пользователю неверную информацию — состояние кнопки вычисляется в момент t0, а нажатие на эту кнопку происходит в произвольный момент t1, где разница между t1 и t0 может составлять дни, а не секунды.


TG>"Стандартный таймаут" — который часто выставляет фреймворк, например, WCF. Кто-то вообще не заморачивается на эти таймауты и оставляет всё по умолчанию.

Я не понимаю, какое отношение стандартный таймаут имеет к вопросу о проектировании UI.

TG>Да и в "городском" энтерпрайзе пользователи могут столкнуться с тормозами, если они сидят через VPN и админы как-то криво "раскрасили" трафик.


И это всё ещё лучше, чем прятать от пользователя возможность без объяснения причин.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[13]: Каких программ вам не хватает?
От: Miroff Россия  
Дата: 08.04.25 07:14
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Можно. Когда я делаю GET на ресурс, я получаю его представление. Крайне желательно, чтобы это представление зависело от состояния ресурса, а не от состояния пользователя, который его получает.

S>Например, можно воткнуть reverse proxy перед сервером приложения, и тогда он сможет снять значительную часть нагрузки с сервера.
S>Как только мы начинаем менять представление объекта в зависимости от пользователя, о кэшировании можно забыть.

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

S>Во-вторых, построение безопасности путём скрытия действий в UI — это популярная, но плохая идея. В основном — потому, что она неимоверно бесит пользователей. Вот я сижу перед списком пользователей, хочу объявить выговор. Поискал в меню — нет такой команды. Поискал в свойствах сотрудника — нет такой кнопки. Полез в инструкцию — сказано "для объявления выговора нажмите на кнопку "Объявить выговор". И вот я уже полчаса думаю, то ли я дурак, то ли инструкция устарела, то ли программа глючит. Внезапно, "некрасивый" способ — дать мне нажать на кнопку, а потом вывести "403 у вас недостаточно прав для объявления выговора этому сотруднику" — в разы гуманнее по отношению к пользователю, чем предлагаемая вами идея.


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

M>>Как в примере из соседнего поста, когда сервис туров следит чтобы определенные туры были доступны только совершеннолетним пользователям и эта же логика дублируется в сервисе рекомендаций чтобы не показывать несоврешеннолетним то, что им и так недоступно. HATEOAS бы эту проблему решил докинув ресурс user список доступных для него туров. ,


S>Это очень, очень плохая архитектура. В первую очередь потому, что вы инвертируете зависимости. Теперь у вас ресурс user (и его микросервис) обязан знать всё о турах (которые вообще-то обрабатываются другим микросервисом).

S>А ресурс "тур", ясное дело, должен знать всё о пользователях. Поздравляю, вы получили не просто монолит, а трудноразвиваемый монолит.

У тебя в любом случае есть связь между двумя ресурсами. Ее можно показать с одного конца, с другого конца или с обоих концов. Последний вариант самый универсальный. Если связь пересекает границы микросервисов, существуют способы это реализовать не увеличивая лишнюю связность: обогащение на уровне gateway, backend for frontend, обогащения на уровне middleware, наконец, просто засунуть в HATEOAS вместо списка туров, ссылку по которой этот список можно получить. Обычно, за HATEOS как раз и отвечает не сам сервис, а middleware. Можно, конечно, замести связь под ковер, но как раз усложняет поддержку сервисов.

S>И это — тоже очень, очень плохая архитектура. Смотрите как это работает: "поисковый запрос" — это эфемерный ресурс. Последнее, что мы хотим — это хранить его. В первую очередь по соображениям масштабирования. Сколько пользователей вы ожидаете ежедневно? Двух? Как только у вас появляется хотя бы несколько сотен тысяч запросов в сутки, хранение (даже в течение ограниченного времени) их эфемерных запросов становится

неподъёмным.

А давай посчитаем Один указатель (кэшировать нужно не весь результат, а только порядок записей), пусть long -- 8 байт * 300k запросов в сутки * 1000 записей (ты же не забываешь про max_search_results)= всего 2.2Gb При этом 300к уникальных запросов это где-то 20M DAU т.е. федеральная система уровня всего СНГ по поиску с запасом влазит на одну машину. У нас, слава богу, не 2005 год и уже завезли кэши в разделяемой памяти, типа hazelcast и ehcache.

S>А непротиворечивость гарантируется тем, что последующие запросы мы дооборудуем хидером if-match/if-none-match, что позволяет нам заметить момент, когда данные на серверной стороне поменялись, и закешированную версию нужно выкинуть.


Во-первых, проверка изменились ли результаты поиска эквивалентна выполнению самого запроса, так что кэширование результатов поиска не имеет смысла и никто им не пользуется. Во-вторых, допустим клиент узнает что результаты изменились, делать-то ему что? Повторять запрос? Тогда никакой консистентности при переходе между страницами не будет и получится ровно то же самое что и с параметрами offset + limit в запросе. Настроить промежуточный кэширующий сервер, который примет полные результаты поиска, а отдавать будет с учетом range. Ну так то же хранение и получится.

S>Простите, но вы рассказываете какие-то небылицы. Что такое "пречеки"? Мы пробежались по всем нужным ресурсам, и убедились, что все нужные нам "действия" торчат в виде линков?


Мы подняли все ресурсы, входящие в сценарий, убедились, что их состояние позволяет реализовать этот сценарий и только после этого начинаем претворять сценарий в жизнь. Понимаешь, когда речь идет про микро сервисы, когда каждый сервис отвечает за свой домен и управляет одним-двумя ресурсами, почти все сценарии захватывают несколько сервисов. При этом подавляющее большинство сценариев не требует строгой консистентности, вполне достаточно eventual consistency. Поэтому нет необходимости любой сценарий превращать в сагу или двухфазный коммит. Более того, сценарии, требующие строгой консистентности встречаются настолько редко, что большинство разработчиков такого не реализовывали ни разу в жизни. В то же время, eventual consistency это все еще consistency, а если у нас сценарии разваливаются через раз, никакой консистентности не будет. Поэтому, дизайнить систему так, чтобы сценарии не разваливались, это хорошая идея. В том числе поддерживать между сервисами контракт, что если мы получили от строннего сервиса ресурс и список связанных ресурсов, то попытка обратиться к этим ресурсам не вызовет ошибки.
Re[14]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.04.25 09:19
Оценка:
Здравствуйте, Miroff, Вы писали:

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

Я бы разводил такие вещи по разным "ресурсам", так, чтобы границы безопасности совпадали с границами ресурсов. Потому что это и надёжнее, и эффективнее.
Например: есть у нас, допустим, судебное постановление. Имена и реквизиты упомянутых там лиц — дело чувствительное. Анонимной публике их видеть не положено; а вот авторизованной — да, нужно.
Вариантов три:
1. При отдаче текста постановления смотрим в токен пользователя, и либо заменяем все имена плейсхолдерами, либо вставляем как надо.
2. Делаем два ресурса: "анонимизированное постановление", "полноценное постановление". Анонимный доступ ко второму получает 401, аутентифицированный доступ без прав на данное дело получает 403.
3. Делаем два ресурса: постановление, в "тексте" которого есть ссылки на фигурантов, каждый из которых — самостоятельный ресурс. Постановления отдаём всем желающим, детали фигурантов — только авторизованным пользователям. Остальные получают 403.
Как по мне, так первый вариант, очевидно, самый-самый плохой. Он плохо масштабируется, и крайне плохо проверяется на корректность.

M>Кому-то можно показывать одно, кому-то другое, третьему вообще ничего нельзя показывать. В результате, во всех без исключения проектах, которые я видел, кэширование за пределами контроллируемого контура отключено по-умолчанию и включается только для определенным образом проверенных ресурсов.

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

M>Современная ИБ говорит, что если какой-то функционал пользователю недоступен он и не должен знать о существовании этого функционала.

Можно ссылку на источник, который вы цитируете? Я не эксперт по ИБ, с удовольствием ознакомлюсь.
M>Иначе мотивированный пользователь начнет искать способ для эскалации привилегий и, чем черт не шутит, вдруг найдет.
В той литературе по ИБ, которую я читал в детстве, излагаемая вами концепция называлась "security by obscurity" и считалась однозначным злом. По сравнению, естественно, с нормальной безопасностью, которая не построена на невежестве пользователей.

REST зарубает вашу идею на корню, т.к. весь "функционал" в нём известен заранее: GET/PUT/DELETE.


M>К тому же, с точки зрения UX скрытие кнопки эквивалентно вызову с ошибкой потому что а) причин ошибки бесконечно много и в этом случае все их нужно обрабатывать и б) ИБ не велит раскрывать внутреннее устройство приложения через сообщения об ошибках, так что пользователь максимум что увидит это "тебе нельзя".

Я не понимаю, что вы имеете под пунктом а). Да, совершенно верно, причин ошибки много, и их надо обрабатывать. В любом случае. Как минимум вы должны уметь отличать 4хх от 5хх. Полагаться на то, что ошибок не существует — верный способ заслужить ненависть пользователей. Я знаю, что сейчас модно либо вообще не обрабатывать ошибки (тыкаешь кнопку — и ничего не происходит), либо обрабатывать их в стиле "произошла неизвестная ошибка". Но нужно отдавать себе отчёт в том, что это — не лучший UX. Его можно чем-то оправдывать, но стремиться надо к хорошему — к UX, который пользователя уважает. В частности, не требует от него навыков трассировки веб-сайтов по F12 для того, чтобы отличить ситуацию "в этом детском саду нет мест" от "сессия прокисла, перелогиньтесь".

M>У тебя в любом случае есть связь между двумя ресурсами. Ее можно показать с одного конца, с другого конца или с обоих концов. Последний вариант самый универсальный. Если связь пересекает границы микросервисов, существуют способы это реализовать не увеличивая лишнюю связность: обогащение на уровне gateway, backend for frontend, обогащения на уровне middleware, наконец, просто засунуть в HATEOAS вместо списка туров, ссылку по которой этот список можно получить. Обычно, за HATEOS как раз и отвечает не сам сервис, а middleware.

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

M>А давай посчитаем Один указатель (кэшировать нужно не весь результат, а только порядок записей), пусть long -- 8 байт * 300k запросов в сутки * 1000 записей (ты же не забываешь про max_search_results)= всего 2.2Gb При этом 300к уникальных запросов это где-то 20M DAU т.е. федеральная система уровня всего СНГ по поиску с запасом влазит на одну машину. У нас, слава богу, не 2005 год и уже завезли кэши в разделяемой памяти, типа hazelcast и ehcache.

Омг, омг. Во-первых, кэшировать нужно, конечно же, весь результат. Потому что иначе совершенно непонятно, как в поиск Ивановых затесался Петров. Или в список билетов до 12000р затесался перелёт за 58000.
Во-вторых, что такое "указатель"? Я так понял, вы положились на то, что все исходные данные загружены в память вашей единственной машины?

M>Во-первых, проверка изменились ли результаты поиска эквивалентна выполнению самого запроса, так что кэширование результатов поиска не имеет смысла и никто им не пользуется.

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

M>Во-вторых, допустим клиент узнает что результаты изменились, делать-то ему что? Повторять запрос?

Зачем? REST как раз позволяет "обновить кэш" одним запросом безо всяких повторений.

M>Тогда никакой консистентности при переходе между страницами не будет и получится ровно то же самое что и с параметрами offset + limit в запросе. Настроить промежуточный кэширующий сервер, который примет полные результаты поиска, а отдавать будет с учетом range. Ну так то же хранение и получится.

Всё верно. Только это хранение вы реализуете не за счёт своих средств, дорогостоящих и ограниченных, а за счёт средств клиентов, которые масштабируются автоматически и бесплатно.
Наличие "промежуточного" кэшируюшего сервера — не обязательная часть решения. Можно считать, что у вас есть "кэширующий клиент", который способен построить иллюзию согласованной бесшовной картины.
Вы застали до-гуглмэпные времена? Когда основным картографическим сервисом был MapQuest, который как раз реализовывал концепцию "постраничной навигации". Пользователь мог двигать "окно просмотра" по фиксированной сетке, перемещаясь по ней в четырёх направлениях. Гугл сумел придумать и реализовать клиента, который создаёт иллюзию "бесконечного канваса", несмотря на наличие под капотом точно такого же "постраничного" механизма.
Теперь всем очевидно, что именно такой способ навигации по "огромной плоскости" и является нормальной реализацией концепции просмотра большой картинки через маленькое окно. Карты гугла, яндекса, 2гиса, и примерно кого угодно теперь устроены именно так, и никак иначе. Та же концепция — у Miro, Figma, и бесчисленного множества диаграммных сервисов.
И только "табличные данные", которые являются частным случаем ровно той же задачи, до сих пор реализуются методиками девяностых годов прошлого века. Нет никакой причины так делать, кроме "здесь так принято" и "мне лень думать, как сделать нормально".

M>Мы подняли все ресурсы, входящие в сценарий, убедились, что их состояние позволяет реализовать этот сценарий и только после этого начинаем претворять сценарий в жизнь. Понимаешь, когда речь идет про микро сервисы, когда каждый сервис отвечает за свой домен и управляет одним-двумя ресурсами, почти все сценарии захватывают несколько сервисов. При этом подавляющее большинство сценариев не требует строгой консистентности, вполне достаточно eventual consistency. Поэтому нет необходимости любой сценарий превращать в сагу или двухфазный коммит. Более того, сценарии, требующие строгой консистентности встречаются настолько редко, что большинство разработчиков такого не реализовывали ни разу в жизни. В то же время, eventual consistency это все еще consistency, а если у нас сценарии разваливаются через раз, никакой консистентности не будет. Поэтому, дизайнить систему так, чтобы сценарии не разваливались, это хорошая идея. В том числе поддерживать между сервисами контракт, что если мы получили от строннего сервиса ресурс и список связанных ресурсов, то попытка обратиться к этим ресурсам не вызовет ошибки.

Этот "контракт" невозможно надёжно обеспечить в реальной среде. Всё, что можно делать — это притворяться, что он выполняется. Поэтому существенной разницы между hateoas и "компактной" формой ресурсов с точки зрения достигнутого результата я не вижу.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Каких программ вам не хватает?
От: Miroff Россия  
Дата: 08.04.25 12:26
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>2. Делаем два ресурса: "анонимизированное постановление", "полноценное постановление". Анонимный доступ ко второму получает 401, аутентифицированный доступ без прав на данное дело получает 403.


А если у тебя десять независимых полей в ресурсе? Будешь делать 100 ресурсов? Сильно сомневаюсь что это хорошее решение, их же придется потом тестировать.

S>3. Делаем два ресурса: постановление, в "тексте" которого есть ссылки на фигурантов, каждый из которых — самостоятельный ресурс. Постановления отдаём всем желающим, детали фигурантов — только авторизованным пользователям. Остальные получают 403.


Опять же, ИБ не велит) Как только у атакующего появляется возможность идентифицировать анонимизированые ресурсы, деанонимизация становится делом техники.

S>Можно ссылку на источник, который вы цитируете? Я не эксперт по ИБ, с удовольствием ознакомлюсь.


Можно, например, начать с википедии и заполировать OWASP Developer Guide. Современный подход к ИБ построен на модели швейцарского сыра: вместо того чтобы обеспечивать безопасность только в одном слое, мы реализуем достаточный уровень безопасности на каждом слое. Один из аспектов такого подхода это принцип минимальных привилегий (пользователю или сервису выдаются как можно меньшие права на как можно более короткий срок достаточный для выполнения операции) и принцип разделения (пользователю выдается только та информация, которая необходима для выполнения операции). И нет, это не security by obscutiry, потому что безопасность на слое ресурсов никак не отменяет проверки прав на уровне сервисов, а лишь дополняет их. Короче, если ты все еще занимаешься разработкой и тем более принятием архитектурных решений, рекомендую пройти какой-нибудь хороший AppSec тренинг, а то с твоего детства многое изменилось )

S>Я не понимаю, что вы имеете под пунктом а). Да, совершенно верно, причин ошибки много, и их надо обрабатывать. В любом случае.


В времена Нового Царства, каждый египтянин после смерти подвергался суду Осириса, где он должен был долго и нужно перечислять чего в жизни он НЕ делал. Вот и с ошибками та же история ) Позитивный сценарий может завершиться только одним способом, негативный -- бесконечным множеством способов из которых восстановимых считанные единицы: 502, да 401 и то не всегда.

S>Во-вторых, что такое "указатель"? Я так понял, вы положились на то, что все исходные данные загружены в память вашей единственной машины?


Не обязательно, любой идентификатор объекта. Хотя, конечно, кэши на разделяемой памяти выгладят красиво.

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


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

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


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

S>Этот "контракт" невозможно надёжно обеспечить в реальной среде. Всё, что можно делать — это притворяться, что он выполняется. Поэтому существенной разницы между hateoas и "компактной" формой ресурсов с точки зрения достигнутого результата я не вижу.


Так практически вся микросервисная архитектура это история не про гарантии, а про достаточно хорошие допущения.
Re[16]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.04.25 12:48
Оценка:
Здравствуйте, Miroff, Вы писали:

M>А если у тебя десять независимых полей в ресурсе? Будешь делать 100 ресурсов?

Это очень странное предположение. Что значит "независимых"? Независимость и означает, что это не один ресурс, а несколько. А их искусственное склеивание — прямой путь создать себе проблемы.
M>Сильно сомневаюсь что это хорошее решение, их же придется потом тестировать.
А в других вариантах их типа можно не тестировать? Как раз нет: вам нужно протестировать не просто корректность раздачи прав доступа на 10 разных ресурсов, а 210 комбинаций вида "есть права на F1 и F3, нет прав на F2 и прочие".

S>>3. Делаем два ресурса: постановление, в "тексте" которого есть ссылки на фигурантов, каждый из которых — самостоятельный ресурс. Постановления отдаём всем желающим, детали фигурантов — только авторизованным пользователям. Остальные получают 403.


M>Опять же, ИБ не велит) Как только у атакующего появляется возможность идентифицировать анонимизированые ресурсы, деанонимизация становится делом техники.

Необязательно. Если делать криво, то да. А если делать по уму, то никакой возможности не появится.

S>>Можно ссылку на источник, который вы цитируете? Я не эксперт по ИБ, с удовольствием ознакомлюсь.


M>Можно, например, начать с википедии и заполировать OWASP Developer Guide. 4

Спасибо, интересно.

M>Не обязательно, любой идентификатор объекта. Хотя, конечно, кэши на разделяемой памяти выгладят красиво.

Ну, вот "любой идентификатор" — это уже не 8 байт, а как минимум 16 (если мы пользуемся гуидами). А то и больше, если у нас ресурсы размазаны по нескольким неймспейсам.

M>Погоди, ты же только что предлагал в заголовках передавать range. Как у тебя будет работать согласованное кэширование, если часть диапазона клиентом даже не запрашивалась? Не говоря уже о том, что браузеры не поддерживают кэширование совместно с range.

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

M>Вообще говоря, в 25 году мало кто работает с табличными данными вообще. Сейчас в моде разделяемое состояние между сервером и клиентом и реактивное программирование для двусторонней синхронизации этой модели в реальном времени. Если непонятно сформулировал, посмотри на фейсбук.

Ок, тем лучше. У меня отвалился антизапрет, а включать VPN чтобы почитать фейсбуковый API guide мне лень. Выражу осторожное сомнение в том, что у фейсбука под капотом — пейджинг на limit offset. Скорее всего, там предикатный метки (примерно так же, как сделаны continuation token в многостраничных респонсах MS Graph API) или там какие-нибудь timestamp ranges.

M>Так практически вся микросервисная архитектура это история не про гарантии, а про достаточно хорошие допущения.

Кмк, это не потому, что такова была цель, а потому что "так получилось". Я же говорю — удачных примеров MSA я видел в единичных количествах, а вот неудачных — как говна за баней.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Каких программ вам не хватает?
От: · Великобритания  
Дата: 08.04.25 13:09
Оценка: 8 (1)
Здравствуйте, Sinclair, Вы писали:

S>2. Делаем два ресурса: "анонимизированное постановление", "полноценное постановление". Анонимный доступ ко второму получает 401, аутентифицированный доступ без прав на данное дело получает 403.

Такое экспоненциально рванёт. У тебя 2 ресурса для одного permission "смотреть инфу о фигурантах". Представь у тебя 10 таких permissions, в зависимости от роли пользователя. Потребуется 1024 разных ресурса.
А ещё ты можешь запрашивать список документов. К половине у тебя роль полного доступа, а к половине частичного.

S>3. Делаем два ресурса: постановление, в "тексте" которого есть ссылки на фигурантов, каждый из которых — самостоятельный ресурс. Постановления отдаём всем желающим, детали фигурантов — только авторизованным пользователям. Остальные получают 403.

Получаем список из n документов, и в каждом ещё 10 ссылок. Тут же шлём ещё n*10 запросов, некоторые из которых что-то возвращают, а некоторые дают 403 отлуп... Тучи запросов и тормоза на пустом месте.

S>Как по мне, так первый вариант, очевидно, самый-самый плохой. Он плохо масштабируется, и крайне плохо проверяется на корректность.

Зато работает.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[16]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 08.04.25 15:24
Оценка:
Здравствуйте, ·, Вы писали:

S>>2. Делаем два ресурса: "анонимизированное постановление", "полноценное постановление". Анонимный доступ ко второму получает 401, аутентифицированный доступ без прав на данное дело получает 403.

·>Такое экспоненциально рванёт. У тебя 2 ресурса для одного permission "смотреть инфу о фигурантах". Представь у тебя 10 таких permissions, в зависимости от роли пользователя. Потребуется 1024 разных ресурса.
Это ж не настоящие ресурсы, а виртуальные. В параллельном ответе пояснил, что экспоненциальный взрыв наступает даже тогда, когда ресурс "один", но может быть отдан в 1024 разных конфигурациях.

S>>3. Делаем два ресурса: постановление, в "тексте" которого есть ссылки на фигурантов, каждый из которых — самостоятельный ресурс. Постановления отдаём всем желающим, детали фигурантов — только авторизованным пользователям. Остальные получают 403.

·>Получаем список из n документов, и в каждом ещё 10 ссылок. Тут же шлём ещё n*10 запросов, некоторые из которых что-то возвращают, а некоторые дают 403 отлуп... Тучи запросов и тормоза на пустом месте.
Зато всё безопасно. Причин для тормозов я не вижу: ваш "рабочий" вариант делает всё то же самое "под капотом", т.к. пермишны проверить всё равно надо, и сходить за данными фигуранта тоже надо. Единственная радость — некоторая экономия на хидерах.

S>>Как по мне, так первый вариант, очевидно, самый-самый плохой. Он плохо масштабируется, и крайне плохо проверяется на корректность.

·>Зато работает.
А вы тестировали? Все 1024 комбинации прав и привилегий?

На практике, конечно же, таких мелкогранулярных систем безопасности не бывает. Как правило, всё квадратно-гнездовое, и набор ролей фиксирован в бизнес-модели (и зачастую ещё и продиктован регулятором).
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: Каких программ вам не хватает?
От: · Великобритания  
Дата: 08.04.25 16:15
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>>2. Делаем два ресурса: "анонимизированное постановление", "полноценное постановление". Анонимный доступ ко второму получает 401, аутентифицированный доступ без прав на данное дело получает 403.

S>·>Такое экспоненциально рванёт. У тебя 2 ресурса для одного permission "смотреть инфу о фигурантах". Представь у тебя 10 таких permissions, в зависимости от роли пользователя. Потребуется 1024 разных ресурса.
S>Это ж не настоящие ресурсы, а виртуальные. В параллельном ответе пояснил, что экспоненциальный взрыв наступает даже тогда, когда ресурс "один", но может быть отдан в 1024 разных конфигурациях.
Гы. Практически с таким же успехом можно засунуть bearer-токен в url, вот у тебя по ресурсу для каждой конфигурации.

S>·>Получаем список из n документов, и в каждом ещё 10 ссылок. Тут же шлём ещё n*10 запросов, некоторые из которых что-то возвращают, а некоторые дают 403 отлуп... Тучи запросов и тормоза на пустом месте.

S>Зато всё безопасно. Причин для тормозов я не вижу: ваш "рабочий" вариант делает всё то же самое "под капотом", т.к. пермишны проверить всё равно надо, и сходить за данными фигуранта тоже надо. Единственная радость — некоторая экономия на хидерах.
Поход внутри сервиса может осуществляться в памяти того же процесса сервиса, в крайнем случае добавлением соответствующих JOIN в sql. А так у тебя будет куча сетевых вызовов... и представь это всё с мобилы через инет! Надо считать roundtrips.

S>·>Зато работает.

S>А вы тестировали? Все 1024 комбинации прав и привилегий?
Каждую комбинацию тестировать не нужно. Нужно тестировать каждую привелегию. Два теста: "разрешено всё -> возвращается инфа о фигурантах", "разрешено всё, кроме инфы о фигурантах -> не возвращается инфа о фигурантах". Ну и отдельно матрица роли <-> привилегии.

Анонимный доступ это такая же роль, как и авторизированный внутренний юзер, внешний юзер, юзер из другого подразделения, админ, аудитор и т.п. И у каждой роли своё подмножество привилегий.

S>На практике, конечно же, таких мелкогранулярных систем безопасности не бывает. Как правило, всё квадратно-гнездовое, и набор ролей фиксирован в бизнес-модели (и зачастую ещё и продиктован регулятором).

Ролей может быть, да. А привилегии — их может быть очень много.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[18]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.04.25 06:55
Оценка:
Здравствуйте, ·, Вы писали:

S>>Это ж не настоящие ресурсы, а виртуальные. В параллельном ответе пояснил, что экспоненциальный взрыв наступает даже тогда, когда ресурс "один", но может быть отдан в 1024 разных конфигурациях.

·>Гы. Практически с таким же успехом можно засунуть bearer-токен в url, вот у тебя по ресурсу для каждой конфигурации.
Ну, да. Поэтому я и не назвал этот способ самым лучшим. Он приемлем тогда, когда у нас нет большого разнообразия пермиссий. Скажем, есть инвойс "с точки зрения плательщика", а есть тот же инвойс "с точки зрения получателя".
Вместо того, чтобы искусственно совмещать их в одном ресурсе и отдавать разный контент в зависимости от содержимого bearer token, делается два разных ресурса с похожей структурой.

·>Поход внутри сервиса может осуществляться в памяти того же процесса сервиса, в крайнем случае добавлением соответствующих JOIN в sql. А так у тебя будет куча сетевых вызовов... и представь это всё с мобилы через инет! Надо считать roundtrips.

Можно и считать. В разных задачах — разные приоритеты требований. Безопасность очень часто входит в конфликт с производительностью.

·>Каждую комбинацию тестировать не нужно. Нужно тестировать каждую привелегию. Два теста: "разрешено всё -> возвращается инфа о фигурантах", "разрешено всё, кроме инфы о фигурантах -> не возвращается инфа о фигурантах". Ну и отдельно матрица роли <-> привилегии.

Перед тем, как прийти к таким выводам, придётся как-то доказать независимость code path, которые приводят к появлению тех или иных результатов. Ну, или надеяться на эту независимость, и рисковать пролезанием false negative в каких-то особенных комбинациях привилегий.
·>Анонимный доступ это такая же роль, как и авторизированный внутренний юзер, внешний юзер, юзер из другого подразделения, админ, аудитор и т.п. И у каждой роли своё подмножество привилегий.
Это всё понятно.
S>>На практике, конечно же, таких мелкогранулярных систем безопасности не бывает. Как правило, всё квадратно-гнездовое, и набор ролей фиксирован в бизнес-модели (и зачастую ещё и продиктован регулятором).
·>Ролей может быть, да. А привилегии — их может быть очень много.
Повторюсь: это зависит от задачи. Модель привилегий и ролей — это всего лишь оптимизация процесса "пересмотр ролей". В зарегулированном бизнесе набор привилегий жёстко связан с ролью.
И это не просто так: бездумная раздача привилегий слишком легко позволяет создавать дырки там, где их не ожидаешь. Типа, напрямую посмотреть реквизиты произвольного контрагента я не могу (прав нет), но могу отправить ему 1 рубль через платёж по номеру телефона. А код формирования ресурса "квитанция о платеже" ничего о привилегиях на контрагентов не знает — у него свой набор привилегий, и вот я уже вижу все реквизиты встроенными прямо в тело квитанции.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[19]: Каких программ вам не хватает?
От: · Великобритания  
Дата: 09.04.25 09:27
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>>>Это ж не настоящие ресурсы, а виртуальные. В параллельном ответе пояснил, что экспоненциальный взрыв наступает даже тогда, когда ресурс "один", но может быть отдан в 1024 разных конфигурациях.

S>·>Гы. Практически с таким же успехом можно засунуть bearer-токен в url, вот у тебя по ресурсу для каждой конфигурации.
S>Ну, да. Поэтому я и не назвал этот способ самым лучшим. Он приемлем тогда, когда у нас нет большого разнообразия пермиссий. Скажем, есть инвойс "с точки зрения плательщика", а есть тот же инвойс "с точки зрения получателя".
Или нет смешанных пермиссий. Скажем, получить список инвойсов за период, и получится что некоторые инвойсы где ты плательщик, а некоторые получатель. В общем, способ может и красивый, но почти бесполезный на практике.

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

Угу. А потом получится, что сегодня два, завтра внезапно понадобился третий, а через год у нас их уже 1024. Так что ещё и немножечко вредный.

S>·>Поход внутри сервиса может осуществляться в памяти того же процесса сервиса, в крайнем случае добавлением соответствующих JOIN в sql. А так у тебя будет куча сетевых вызовов... и представь это всё с мобилы через инет! Надо считать roundtrips.

S>Можно и считать. В разных задачах — разные приоритеты требований. Безопасность очень часто входит в конфликт с производительностью.
Интересно узнать какой такой магией фрагмент в урле повышает безопасность? Как ты будешь доказывать или тестировать безопасность 1024 конфигураций ресурса, пусть и виртуальных?
По сути ровно те же данные в теле запроса. Просто кусочек инфы переползает из ~третей строчки http запроса (там где Authorization: Bearer хедер) в первую строчку (там где урл). Злоумышленникам по барабану какую часть менять, а программисты с равным успехом могут налажать в проверках любой части запроса.

Урлы имеют смысл не для безопасности, а для кеширующих прокси (та самая производительность!). Но прокси на практике умеют работать только с анонимными ресурсами, у которых никаких пермов нет и можно смело всё отдавать всем. Только в этом случае есть гарантии не нарушения безопасности, т.к. нарушать собственно нечего.

S>·>Каждую комбинацию тестировать не нужно. Нужно тестировать каждую привелегию. Два теста: "разрешено всё -> возвращается инфа о фигурантах", "разрешено всё, кроме инфы о фигурантах -> не возвращается инфа о фигурантах". Ну и отдельно матрица роли <-> привилегии.

S>Перед тем, как прийти к таким выводам, придётся как-то доказать независимость code path, которые приводят к появлению тех или иных результатов. Ну, или надеяться на эту независимость, и рисковать пролезанием false negative в каких-то особенных комбинациях привилегий.
Доказывать эту независимость придётся ровно тем же способом, как и для твоих 1024 виртуальных ресурсов.
Единственное, что может помочь, что есть какой-то уже готовый фреймворк, про который авторы зуб дают, что всё безопасно, а твоя аппликуха на него полагается. Но с т.з. системы в целом — ранзицы никакой.

S>>>На практике, конечно же, таких мелкогранулярных систем безопасности не бывает. Как правило, всё квадратно-гнездовое, и набор ролей фиксирован в бизнес-модели (и зачастую ещё и продиктован регулятором).

S>·>Ролей может быть, да. А привилегии — их может быть очень много.
S>Повторюсь: это зависит от задачи. Модель привилегий и ролей — это всего лишь оптимизация процесса "пересмотр ролей". В зарегулированном бизнесе набор привилегий жёстко связан с ролью.
S>И это не просто так: бездумная раздача привилегий слишком легко позволяет создавать дырки там, где их не ожидаешь. Типа, напрямую посмотреть реквизиты произвольного контрагента я не могу (прав нет), но могу отправить ему 1 рубль через платёж по номеру телефона. А код формирования ресурса "квитанция о платеже" ничего о привилегиях на контрагентов не знает — у него свой набор привилегий, и вот я уже вижу все реквизиты встроенными прямо в тело квитанции.
Как твои виртуальные ресурсы помогут? Ну да, "GET /contragents?id=vasya" тебе запрещён, а "POST /payments?to=vasya" тебе выдаст квиток с инфой конртагента.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 09.04.2025 9:28 · . Предыдущая версия .
Re[20]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.04.25 11:35
Оценка:
Здравствуйте, ·, Вы писали:

·>Или нет смешанных пермиссий. Скажем, получить список инвойсов за период, и получится что некоторые инвойсы где ты плательщик, а некоторые получатель.

Ну, вот на практике есть два разных списка: "инвойсы, которые выставил я", и "инвойсы, которые выставили мне". Всё, никаких чудес. Зачем делать единый список, а потом мужественно бороться с разнотипностью — .
Посмотрите на тот же Paypal API — он, хоть и не-REST, но устроен именно так.

·>Угу. А потом получится, что сегодня два, завтра внезапно понадобился третий, а через год у нас их уже 1024. Так что ещё и немножечко вредный.

Или не получится.

·>Интересно узнать какой такой магией фрагмент в урле повышает безопасность?

Очень просто: у нас нет "частичной безопасности", когда содержимое ресурса зависит от каких-то эфемерных вещей. У нас для запроса есть ровно два возможных ответа: 403 или 200.

·>Как ты будешь доказывать или тестировать безопасность 1024 конфигураций ресурса, пусть и виртуальных?

Очень просто. 2048 тестов, если уж у нас есть 1024 конфигурации ресурса. Но я повторю ещё раз: у нас не будет 1024 конфигураций. Этот способ не подходит для таких API, в которых есть 10 разных привилегий на один и тот же ресурс.

·>Урлы имеют смысл не для безопасности, а для кеширующих прокси (та самая производительность!). Но прокси на практике умеют работать только с анонимными ресурсами, у которых никаких пермов нет и можно смело всё отдавать всем. Только в этом случае есть гарантии не нарушения безопасности, т.к. нарушать собственно нечего.

На практике прокси прекрасно работают и с ресурсами, у которых cache-control: private. Это как раз позволяет каждому клиенту построить у себя частичную реплику распределённого состояния, и эффективно с ней взаимодействовать.

·>Доказывать эту независимость придётся ровно тем же способом, как и для твоих 1024 виртуальных ресурсов.

Ну так для 1024 ресурсов у нас будет 2048 тестов. Вам же кажется, что можно как-то обойтись 20 тестами.
·>Как твои виртуальные ресурсы помогут? Ну да, "GET /contragents?id=vasya" тебе запрещён, а "POST /payments?to=vasya" тебе выдаст квиток с инфой конртагента.
Виртуальные ресурсы помогут избавиться от иллюзии того, что данные Васи видны только тем, у кого есть привилегия "видеть данные Васи".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[21]: Каких программ вам не хватает?
От: · Великобритания  
Дата: 09.04.25 13:21
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Ну, вот на практике есть два разных списка: "инвойсы, которые выставил я", и "инвойсы, которые выставили мне". Всё, никаких чудес. Зачем делать единый список, а потом мужественно бороться с разнотипностью — .

S>Посмотрите на тот же Paypal API — он, хоть и не-REST, но устроен именно так.
Я что-то перестал понимать о чём идёт речь. Вроде ты обсуждал возможность, что некий запрос какого-то ресурса, который выдаёт результат, раскрывающий детали других ресурсов. Т.е. когда например результирующий инвойс содержит приватные данные контрагента.
То что Paypal API устроен именно так... ну дык он для того, чтобы человеки хорошо понимали, а не для безопасности.

S>·>Интересно узнать какой такой магией фрагмент в урле повышает безопасность?

S>Очень просто: у нас нет "частичной безопасности", когда содержимое ресурса зависит от каких-то эфемерных вещей. У нас для запроса есть ровно два возможных ответа: 403 или 200.
Частичной нет, верно... да вообще никакой нет. Ну выдал ваш /invoices-for-me?id=123 json 200, в котором внезапно появится секция contragent: {homeAddress: {...}}, хотя разрешено только companyAddress выдавать. А ещё злоумышленник подменит урл на /my-invoices?id=123 и увидит вообще всё.

S>·>Как ты будешь доказывать или тестировать безопасность 1024 конфигураций ресурса, пусть и виртуальных?

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

S> Но я повторю ещё раз: у нас не будет 1024 конфигураций. Этот способ не подходит для таких API, в которых есть 10 разных привилегий на один и тот же ресурс.

А пофиг. Привилегии должны проверяться на источнике данных, а не на endpoint-е. Источник данных контрагента — это скорее всего один метод где-то внутрях. И именно он потребует наличие ровно одной конкретной привилегии. И не отдаст данные если привилегии нет, вне зависимости от того как обратились к сервису — через /contragents, через инвойсы или через платежи.
Ещё раз, ресурсы-сервисы-endpoints — это всё о том в каком формате обменяться данными по сети, и отношение к безопасности имеет чисто иллюзорное.

S>·>Урлы имеют смысл не для безопасности, а для кеширующих прокси (та самая производительность!). Но прокси на практике умеют работать только с анонимными ресурсами, у которых никаких пермов нет и можно смело всё отдавать всем. Только в этом случае есть гарантии не нарушения безопасности, т.к. нарушать собственно нечего.

S>На практике прокси прекрасно работают и с ресурсами, у которых cache-control: private. Это как раз позволяет каждому клиенту построить у себя частичную реплику распределённого состояния, и эффективно с ней взаимодействовать.
Не понял. "cache-control: private" — это как раз инструкция отключающая кеш на прокси. Или под "работают" ты тут имел в виду "по идее должен сам отключаться"? И ты так и не пояснил какое это отношение имеет к безопасности.

S>Ну так для 1024 ресурсов у нас будет 2048 тестов. Вам же кажется, что можно как-то обойтись 20 тестами.

S>·>Как твои виртуальные ресурсы помогут? Ну да, "GET /contragents?id=vasya" тебе запрещён, а "POST /payments?to=vasya" тебе выдаст квиток с инфой конртагента.
S>Виртуальные ресурсы помогут избавиться от иллюзии того, что данные Васи видны только тем, у кого есть привилегия "видеть данные Васи".
А откуда такая иллюзия появится в принципе особенно в случае эвфемерных вещей??
Как раз вот илюзия "запретим выдачу деталей контрагента по урлу GET /contragents — значит детали контрагента никому не будут видны без нужной привилегии" и создаётся.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 09.04.2025 13:25 · . Предыдущая версия .
Re[22]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 09.04.25 15:47
Оценка:
Здравствуйте, ·, Вы писали:
·>Я что-то перестал понимать о чём идёт речь. Вроде ты обсуждал возможность, что некий запрос какого-то ресурса, который выдаёт результат, раскрывающий детали других ресурсов. Т.е. когда например результирующий инвойс содержит приватные данные контрагента.
Нет, не содержит он никаких приватных данных контрагента. Инвойс, выставленный мне, ни в каком случае не показывает "закрытые" атрибуты отправителя. Независимо от моих "привилегий".
Инвойс, который выставил я, содержит дополнительные поля, которые на стороне получателя видны не будут. Опять же, мне они видны независимо от моих привилегий.
Доступ к "моим" инвойсам всем остальным пользователям закрыт не на уровне отдельных полей и привилегий, а на уровне всего субресурса
·>То что Paypal API устроен именно так... ну дык он для того, чтобы человеки хорошо понимали, а не для безопасности.
У пейпала с безопасностью всё в порядке.

·>Частичной нет, верно... да вообще никакой нет. Ну выдал ваш /invoices-for-me?id=123 json 200, в котором внезапно появится секция contragent: {homeAddress: {...}}, хотя разрешено только companyAddress выдавать. А ещё злоумышленник подменит урл на /my-invoices?id=123 и увидит вообще всё.

Нет, не увидит. Внутри ресурса /my-invoices/ нет никаких чужих инвойсов.
·>Если у тебя уже для такой тривиальной вещи 2048 тестов, то они абсолютно бесполезны, т.к. их никто не сможет проанализировать на безопасность и поддерживать в дальнейшем.
У меня складывается впечатление, что вы читаете не всё, что я пишу. Давайте вы ещё раз перечитаете всё с самого начала и внимательно посмотрите, с какими тезисами вы спорите.

·>А пофиг. Привилегии должны проверяться на источнике данных, а не на endpoint-е.

Забавная идея. Ну, вот у вас "источник данных" — реляционная БД. Какие вы там привилегии собрались проверять?

·>Ещё раз, ресурсы-сервисы-endpoints — это всё о том в каком формате обменяться данными по сети, и отношение к безопасности имеет чисто иллюзорное.

Ну, иллюзорную так иллюзорную.

·>Не понял. "cache-control: private" — это как раз инструкция отключающая кеш на прокси.

Я неправильно написал. Речь не о прокси, а о кэшировании на стороне клиента. Оно точно так же поднимает производительность, т.к. позволяет нам не отдавать неизменные данные; при этом оно никак не противоречит безопасности.


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

·>А откуда такая иллюзия появится в принципе особенно в случае эвфемерных вещей??
Ну вот у вас она уже возникла.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[23]: Каких программ вам не хватает?
От: · Великобритания  
Дата: 10.04.25 11:20
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Я что-то перестал понимать о чём идёт речь. Вроде ты обсуждал возможность, что некий запрос какого-то ресурса, который выдаёт результат, раскрывающий детали других ресурсов. Т.е. когда например результирующий инвойс содержит приватные данные контрагента.

S>Нет, не содержит он никаких приватных данных контрагента. Инвойс, выставленный мне, ни в каком случае не показывает "закрытые" атрибуты отправителя.
S>Независимо от моих "привилегий".
Закрытые для кого? Закрытость — вещь относительная. На какие-то атрибуты у тебя есть привилегии, на какие-то нет. У других пользователей будет другой доступ к атрибутам.

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

S>Доступ к "моим" инвойсам всем остальным пользователям закрыт не на уровне отдельных полей и привилегий, а на уровне всего субресурса
Каким образом закрыт-то? Заклятие наложено что-ли? Субресурс возвращает джсон, как ты ресурсом гарантируешь отстутствие там какого-нибудь contragent.homeAddress?

S>·>То что Paypal API устроен именно так... ну дык он для того, чтобы человеки хорошо понимали, а не для безопасности.

S>У пейпала с безопасностью всё в порядке.
Верю. Но не потому, что "API устроен именно так".

S>·>Частичной нет, верно... да вообще никакой нет. Ну выдал ваш /invoices-for-me?id=123 json 200, в котором внезапно появится секция contragent: {homeAddress: {...}}, хотя разрешено только companyAddress выдавать. А ещё злоумышленник подменит урл на /my-invoices?id=123 и увидит вообще всё.

S>Нет, не увидит. Внутри ресурса /my-invoices/ нет никаких чужих инвойсов.
Почему ты так решил? Ты так говоришь, что ресурс это какая-то охраняемая зона за колючей проволокой. Нет, это просто несколько байт в определённом месте хедера запроса, а обрабатывает всё это дело тот же самый условно питонячий код, что и для других ресурсов.

S>·>Если у тебя уже для такой тривиальной вещи 2048 тестов, то они абсолютно бесполезны, т.к. их никто не сможет проанализировать на безопасность и поддерживать в дальнейшем.

S>У меня складывается впечатление, что вы читаете не всё, что я пишу. Давайте вы ещё раз перечитаете всё с самого начала и внимательно посмотрите, с какими тезисами вы спорите.
Я спорю с тем, что ресурсы и урлы каким-то образом обеспечивают хоть какую-то безопасность. Что в зависимости от того куда поместить данные — в урл или хедер — вдруг внезапно станет "Зато всё безопасно".

S>·>А пофиг. Привилегии должны проверяться на источнике данных, а не на endpoint-е.

S>Забавная идея. Ну, вот у вас "источник данных" — реляционная БД. Какие вы там привилегии собрались проверять?
А чё так мелко? Сразу уж говори — источник данных — заряд на затворе транзистора. Да, там с привилегиями как-то не очень. Я вообще-то говорил о дизайне самого сервиса. БД — это уже другой сервис.

S>·>Не понял. "cache-control: private" — это как раз инструкция отключающая кеш на прокси.

S>Я неправильно написал. Речь не о прокси, а о кэшировании на стороне клиента. Оно точно так же поднимает производительность, т.к. позволяет нам не отдавать неизменные данные; при этом оно никак не противоречит безопасности.
А, так да. Но собственно тут обсуждалось "Безопасность очень часто входит в конфликт с производительностью", мой вопрос был "Интересно узнать какой такой магией фрагмент в урле повышает безопасность?". И в качестве ответа ты внезапно рассказываешь о "поднимает производительность" "не противоречит безопасности".

S>·>А откуда такая иллюзия появится в принципе особенно в случае эвфемерных вещей??

S>Ну вот у вас она уже возникла.
С чего ты взял? Никогда не было. А вот у тебя иллюзия в полный рост, цитирую: "чтобы границы безопасности совпадали с границами ресурсов. Потому что это и надёжнее,", "Зато всё безопасно". Нет, конечно. Надёжность тут не причём. Так просто удобнее и понятнее для человеков: не видно, что не должно быть видно. Т.е. чисто иллюзорные результаты.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 10.04.2025 11:24 · . Предыдущая версия .
Re[24]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 11.04.25 05:11
Оценка: 2 (1)
Здравствуйте, ·, Вы писали:
S>>Независимо от моих "привилегий".
·>Закрытые для кого? Закрытость — вещь относительная. На какие-то атрибуты у тебя есть привилегии, на какие-то нет. У других пользователей будет другой доступ к атрибутам.
Нет. Вот как раз предлагаемая вами модель чревата дырками и крайне тяжела в поддержке и верификации.
Я вам рассказываю практический замкнутый пример: нет никаких "привилегий на втрибуты". Есть два вида ресурсов. Доступ к каждому их них либо есть, либо нету.

·>Каким образом закрыт-то? Заклятие наложено что-ли? Субресурс возвращает джсон, как ты ресурсом гарантируешь отстутствие там какого-нибудь contragent.homeAddress?

Очень просто: contragent.homeAddress там нет вообще, вне зависимости от привилегий.

·>Верю. Но не потому, что "API устроен именно так".



·>Почему ты так решил? Ты так говоришь, что ресурс это какая-то охраняемая зона за колючей проволокой.

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

·>Я спорю с тем, что ресурсы и урлы каким-то образом обеспечивают хоть какую-то безопасность. Что в зависимости от того куда поместить данные — в урл или хедер — вдруг внезапно станет "Зато всё безопасно".

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

·>А чё так мелко? Сразу уж говори — источник данных — заряд на затворе транзистора. Да, там с привилегиями как-то не очень. Я вообще-то говорил о дизайне самого сервиса. БД — это уже другой сервис.

Нет, БД — это не "другой сервис", это неотъемлемый компонент сервиса, оборудованного персистентным состоянием.
Впрочем, можно спроектировать и так, как вы предлагаете — вынести БД в отдельный сервис, из которого торчит очень широкий контракт.
И, естественно, у него очень грубый набор привилегий. Ваша модель "давайте мы будем ограничивать доступ путём передачи пользовательского токена вдоль всей иерархии вызовов" работает примерно никогда.
Все практики безопасности как раз так и устроены, что у принципала X нету привилегий к "сырому" ресурсу A, но есть привилегии на доступ к "производному" ресурсу B. Сервис, выполняющий построение ресурса B, выполняется под принципалом Y, который имеет полные привилегии для A, но учитывает привилегии X при отдаче ему производного ресурса B.
Типичный пример применения концепции в рамках SQL99: пользователю нельзя видеть часть строк в таблице X. Мы не можем навесить привилегию на каждую строку. Зато можем сделать следующее:
1. Отбираем у пользователя привилегии на таблицу X
2. Строим на основе X представление Y, в котором добавлен предикат безопасности (типа select * from X where TotalAmount < 100000)
3. Выдаём пользователю привилегии на Y и указываем, что Y исполняется под привилегиями админа, а не пользователя.
Аналогично мы могли бы поступить и с ограничениями на колонки таблицы. Квадратно-гнездовая система, где чётко видна вся схема Y и легко проверить, что туда попадает, а что нет, как методом статического анализа, так и методом выполнения динамических тестов. Вы предлагаете заменить её на некий невнятный код "хранимой процедуры", которая возвращает всякий раз разный набор строк и колонок в зависимости от рантайм-содержимого параметров.
И всё это под тем предлогом, что "какая нам разница, где передаётся информация о привилегиях — всё равно это текст SQL запроса".
Нет, увы. Факторизация кода позволяет нам изолировать разные сценарии.

·>А, так да. Но собственно тут обсуждалось "Безопасность очень часто входит в конфликт с производительностью", мой вопрос был "Интересно узнать какой такой магией фрагмент в урле повышает безопасность?". И в качестве ответа ты внезапно рассказываешь о "поднимает производительность" "не противоречит безопасности".

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

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

·>С чего ты взял? Никогда не было. А вот у тебя иллюзия в полный рост, цитирую: "чтобы границы безопасности совпадали с границами ресурсов. Потому что это и надёжнее,", "Зато всё безопасно". Нет, конечно. Надёжность тут не причём. Так просто удобнее и понятнее для человеков: не видно, что не должно быть видно. Т.е. чисто иллюзорные результаты.

Это оттого, что я плохо объясняю. Разница в подходах есть. Понятно, что достичь приемлемого результата можно в любом подходе, но стоимость получается разной. Когда границы безопасности совпадают с границами ресурсов, это сильно упрощает доказательство корректности. Я выше примерно написал, каким именно способом. Но вы можете мне не верить и писать более дорогостоящие и трудноподдерживаемые системы — у нас же нет архитектурной полиции
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[25]: Каких программ вам не хватает?
От: · Великобритания  
Дата: 11.04.25 12:24
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Закрытые для кого? Закрытость — вещь относительная. На какие-то атрибуты у тебя есть привилегии, на какие-то нет. У других пользователей будет другой доступ к атрибутам.

S>Нет. Вот как раз предлагаемая вами модель чревата дырками и крайне тяжела в поддержке и верификации.
S>Я вам рассказываю практический замкнутый пример: нет никаких "привилегий на втрибуты". Есть два вида ресурсов. Доступ к каждому их них либо есть, либо нету.
Где есть два вида? В спеке?

S>·>Каким образом закрыт-то? Заклятие наложено что-ли? Субресурс возвращает джсон, как ты ресурсом гарантируешь отстутствие там какого-нибудь contragent.homeAddress?

S>Очень просто: contragent.homeAddress там нет вообще, вне зависимости от привилегий.
Как конкретно это гарантируется? Только не говори: "в API нету!". Не слыхал историй, когда какие-нибудь rest-endpoints случайно возвращали json с неожиданными полями. All tests passed, всё у всех работает.

S>·>Почему ты так решил? Ты так говоришь, что ресурс это какая-то охраняемая зона за колючей проволокой.

S>Потому что я так спроектировал, и это легко проверить как методом "белого ящика", так и "чёрного ящика".
Ну о чём и говорю. "Спроектировал" — это написал тонну word-документов и презентаций. И проверить легко, не спорю. А на самом деле json генерится питонячим кодом, который рефлексией вываливает всё что есть. И даже если всё было красиво в начале проекта, то после нескольких лет поддержки, рефакторингов и переписывания вдруг может что-то где-то вылезти.

S>·>Нет, это просто несколько байт в определённом месте хедера запроса, а обрабатывает всё это дело тот же самый условно питонячий код, что и для других ресурсов.

S>Если у вас так, то всё плохо. Попробуйте перепроектировать так, чтобы в одном коде не смешивалась обработка "своих" и "чужих" инвойсов. Человечество изобрело массу способов факторизации кода.
Может такое только в каких-то мелких проектах и пройдёт... И то никакой гарантии, что кто-то куда-то по ошибке не скопипастит. В серьёзных случаях у тебя будет 1024 вида своих и чужих.

S>·>А чё так мелко? Сразу уж говори — источник данных — заряд на затворе транзистора. Да, там с привилегиями как-то не очень. Я вообще-то говорил о дизайне самого сервиса. БД — это уже другой сервис.

S>Нет, БД — это не "другой сервис", это неотъемлемый компонент сервиса, оборудованного персистентным состоянием.
Надо технически смотреть, а не по поверпоинт презентациям. БД — компонент, если embedded, в лучшем случае. А так по сути другой сервис — формируешь запрос (query) и получаешь ответ (recordset). Ничем по факту от какого-нибудь rest не отличается, только протокол общения другой, вместо http у тебя sql.

S>Впрочем, можно спроектировать и так, как вы предлагаете — вынести БД в отдельный сервис, из которого торчит очень широкий контракт.

S>И, естественно, у него очень грубый набор привилегий. Ваша модель "давайте мы будем ограничивать доступ путём передачи пользовательского токена вдоль всей иерархии вызовов" работает примерно никогда.
Что значит "вынести"? Попробуй хоть как-нибудь не "вынести" какой-нибудь оракл из твоего питоячего сервиса.

S>Все практики безопасности как раз так и устроены, что у принципала X нету привилегий к "сырому" ресурсу A, но есть привилегии на доступ к "производному" ресурсу B. Сервис, выполняющий построение ресурса B, выполняется под принципалом Y, который имеет полные привилегии для A, но учитывает привилегии X при отдаче ему производного ресурса B.

S>Типичный пример применения концепции в рамках SQL99: пользователю нельзя видеть часть строк в таблице X. Мы не можем навесить привилегию на каждую строку. Зато можем сделать следующее:
S>1. Отбираем у пользователя привилегии на таблицу X
S>2. Строим на основе X представление Y, в котором добавлен предикат безопасности (типа select * from X where TotalAmount < 100000)
S>3. Выдаём пользователю привилегии на Y и указываем, что Y исполняется под привилегиями админа, а не пользователя.
S>Аналогично мы могли бы поступить и с ограничениями на колонки таблицы. Квадратно-гнездовая система, где чётко видна вся схема Y и легко проверить, что туда попадает, а что нет, как методом статического анализа, так и методом выполнения динамических тестов. Вы предлагаете заменить её на некий невнятный код "хранимой процедуры", которая возвращает всякий раз разный набор строк и колонок в зависимости от рантайм-содержимого параметров.
Я как-то более о практике. Статический анализ, конечно, хорошо... Но если тебе для этого придётся описывать "1024 виртуальных ресурсов" и писать 2048 тестов для них, но это не заработает на практике, хотя в теории, конечно, красота...

S>Нет, увы. Факторизация кода позволяет нам изолировать разные сценарии.

Если у тебя два сценария, то, конечно, это щастье. А вот когда сценариев 1024...

S>·>А, так да. Но собственно тут обсуждалось "Безопасность очень часто входит в конфликт с производительностью", мой вопрос был "Интересно узнать какой такой магией фрагмент в урле повышает безопасность?". И в качестве ответа ты внезапно рассказываешь о "поднимает производительность" "не противоречит безопасности".

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

S>В вашем же подходе в коде какая-то каша, отдаётся примерно произвольный набор атрибутов с соответствии с хитро устроенными предикатами, смешивающими проверку нескольких разных claims. Доказать, что код выдаёт корректное сочетание атрибутов для произвольного сочетания claims — та ещё задача.

У меня проверка конкретного claim одна же. Я писал выше. Будет что-то
Json getInvoiceForRest(..., securityContext, ...) {
  verifyPrivilegesForTheInvoice(..., securityContext, ...);//тут мы проверяем привилегии для данного инвойса
  return new Invoice {
    date: getInvoiceDate(...)
    contragent: getContragentDetails(..., securityContext, ...)// вот внутри этого метода и проверятся имеющиеся привилегии и вернутся только доступные данные конртагента.
    shipping: getShippingDetails(..., securityContext, ...)// вот внутри этого метода и проверятся имеющиеся привилегии и вернутся только доступные данные о доставке.
  }
}

Конечно, это не отменяет "Правила роутинга" и другие подобные подходы, а дополняет. Как тут Miroff швейцарский сыр упоминал.

S>·>С чего ты взял? Никогда не было. А вот у тебя иллюзия в полный рост, цитирую: "чтобы границы безопасности совпадали с границами ресурсов. Потому что это и надёжнее,", "Зато всё безопасно". Нет, конечно. Надёжность тут не причём. Так просто удобнее и понятнее для человеков: не видно, что не должно быть видно. Т.е. чисто иллюзорные результаты.

S>Это оттого, что я плохо объясняю. Разница в подходах есть. Понятно, что достичь приемлемого результата можно в любом подходе, но стоимость получается разной. Когда границы безопасности совпадают с границами ресурсов, это сильно упрощает доказательство корректности. Я выше примерно написал, каким именно способом. Но вы можете мне не верить и писать более дорогостоящие и трудноподдерживаемые системы — у нас же нет архитектурной полиции
Ну я просто написал когда этот твой способ не работает. Но я, конечно, верю, что в некоторых случаях и твой способ работает. Но это не значит, что он универсальный всемогутер.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[26]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 12.04.25 06:20
Оценка:
Здравствуйте, ·, Вы писали:

·>Где есть два вида? В спеке?

Да, прямо в спеке.

·>Как конкретно это гарантируется? Только не говори: "в API нету!". Не слыхал историй, когда какие-нибудь rest-endpoints случайно возвращали json с неожиданными полями. All tests passed, всё у всех работает.

Я вроде написал, как. Историй про то, как all tests passed, а результат не соответствует — нет, не слышал.
Но вы отклоняетесь: если у вас такие кривые тесты, которые пропускают наличие лишних полей, то в системе с "привилегией на атрибуты" у вас тем более будет бардак.

S>>Потому что я так спроектировал, и это легко проверить как методом "белого ящика", так и "чёрного ящика".

·>Ну о чём и говорю. "Спроектировал" — это написал тонну word-документов и презентаций.
Нет конечно. "Спроектировал" — это построил фреймворк, в котором не так много возможностей накосячить. Архитектура софта не исчерпывается "внешними" требованиями.

·>И проверить легко, не спорю. А на самом деле json генерится питонячим кодом, который рефлексией вываливает всё что есть.

Вот это ваше "На самом деле" означает, что вы пытаетесь тестировать код как чёрный ящик. Это не единственный (и не самый эффективный) способ обеспечения качества.
Надо смотреть внутрь, и желающих генерить всё что есть через рефлексию бить палкой на code review.
·>Может такое только в каких-то мелких проектах и пройдёт...
Вот то, что вы предлагаете, как раз и проходит только в мелких проектах. В крупных проектах требуется более строгий контроль, чем "я проверил всю динамику глазами и всё сошлось".
·>И то никакой гарантии, что кто-то куда-то по ошибке не скопипастит.
Гарантия предотвращения ошибочных копипастов достигается юнит-тестированием. Преимущество тут в том, что в коде нет ветвлений — поэтому нам не надо беспокоиться, что "вдруг" в результате образуется сontragent: {homeAddress: {...}}. Достаточно 1 (одного) теста на то, что такого в json нету, и вопрос закрыт.
·>В серьёзных случаях у тебя будет 1024 вида своих и чужих.
Это как раз в несерьёзных случаях. Серьёзные случаи — это как раз какой-нибудь банковский API. И вот там всё именно так, как я сказал. Потому что нельзя позволить ни криворукому девелоперу нечаянно проверить не ту привилегию, так и криворукому админу нечаянно выдать кому-то не ту привилегию.

·>Надо технически смотреть, а не по поверпоинт презентациям. БД — компонент, если embedded, в лучшем случае. А так по сути другой сервис — формируешь запрос (query) и получаешь ответ (recordset). Ничем по факту от какого-нибудь rest не отличается, только протокол общения другой, вместо http у тебя sql.

Верно мыслите, так всё и есть.

·>Что значит "вынести"? Попробуй хоть как-нибудь не "вынести" какой-нибудь оракл из твоего питоячего сервиса.

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

·>Я как-то более о практике.

Это я о практике, а вы рассказываете какие-то сказки. Ну, точнее вы предлагаете заведомо неудачные решения, через которые мы проходили по нескольку раз начиная ещё с девяностых.
·>Статический анализ, конечно, хорошо... Но если тебе для этого придётся описывать "1024 виртуальных ресурсов" и писать 2048 тестов для них, но это не заработает на практике, хотя в теории, конечно, красота...
Не придётся. Я уже писал причины.
·>Если у тебя два сценария, то, конечно, это щастье. А вот когда сценариев 1024...
Когда у вас 1024 сценария, то деваться некуда. Но чаще это означает, что кто-то не справился с факторизацией сценария, в котором есть 8 отдельных подсценариев, каждый из которых работает независимо и его можно протестировать отдельно, т.к. там вариантов гораздо меньше.

·>Ну будет у тебя 1024 кусков кода и неподдерживаемая кодовая база.

И какая альтернатива?

·>У меня проверка конкретного claim одна же. Я писал выше. Будет что-то

·>
·>Json getInvoiceForRest(..., securityContext, ...) {
·>  verifyPrivilegesForTheInvoice(..., securityContext, ...);//тут мы проверяем привилегии для данного инвойса
·>  return new Invoice {
·>    date: getInvoiceDate(...)
·>    contragent: getContragentDetails(..., securityContext, ...)// вот внутри этого метода и проверятся имеющиеся привилегии и вернутся только доступные данные конртагента.
·>    shipping: getShippingDetails(..., securityContext, ...)// вот внутри этого метода и проверятся имеющиеся привилегии и вернутся только доступные данные о доставке.
·>  }
·>}
·>

Прекрасно. Видна некоторая попытка факторизации. Которая, к слову, совершенно бесполезна в вашем подходе с чёрным ящиком: а вдруг завтра кто-то заменит всю вашу кунсткамеру на "питонячий код, который по рефлексии вываливает сразу всё"?
Ну, и вредна конечно тоже: потому что завтра кто-то в эксплуатации не нарулит кому-то такой набор привилегий, который не даёт выполнить реальный сценарий. Зачем так делать — непонятно. Ну, кроме "нам было лень проектировать, и мы свалили ответственность на кого-то другого.

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

Скорее наоборот: бывают задачи с низкой ответственностью, где можно игнорировать best practices и писать с надеждой на авось.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 13.04.2025 2:39 Sinclair . Предыдущая версия .
Re[27]: Каких программ вам не хватает?
От: · Великобритания  
Дата: 14.04.25 10:43
Оценка: :)
Здравствуйте, Sinclair, Вы писали:

S>·>Где есть два вида? В спеке?

S>Да, прямо в спеке.
Именно. А толку? Никакой безопасности это само по себе не обеспечит. В проде у тебя работает код, а не спека. Cпека ничего не может гарантировать о реализации.

S>·>Как конкретно это гарантируется? Только не говори: "в API нету!". Не слыхал историй, когда какие-нибудь rest-endpoints случайно возвращали json с неожиданными полями. All tests passed, всё у всех работает.

S>Я вроде написал, как. Историй про то, как all tests passed, а результат не соответствует — нет, не слышал.
Ну лень гуглить, но периодически выскакивают истории типа как из сайтов авиакомпаний и продаж билетов выковыривают инфу о том кто куда летает и где живёт.

S>Но вы отклоняетесь: если у вас такие кривые тесты, которые пропускают наличие лишних полей, то в системе с "привилегией на атрибуты" у вас тем более будет бардак.

Очередная иллюзия у тебя. Даже если в тесте не выдаётся лишних полей, это не значит что они не выдаются.

S>·>Ну о чём и говорю. "Спроектировал" — это написал тонну word-документов и презентаций.

S>Нет конечно. "Спроектировал" — это построил фреймворк, в котором не так много возможностей накосячить. Архитектура софта не исчерпывается "внешними" требованиями.
Гы. Очередной фреймворк-всемогутер.

S>·>И проверить легко, не спорю. А на самом деле json генерится питонячим кодом, который рефлексией вываливает всё что есть.

S>Вот это ваше "На самом деле" означает, что вы пытаетесь тестировать код как чёрный ящик. Это не единственный (и не самый эффективный) способ обеспечения качества.
S>Надо смотреть внутрь, и желающих генерить всё что есть через рефлексию бить палкой на code review.
Именно. Т.е. твой "правильный API" — это каша из топора. Начал ты "нарисуем границы API — и всё безопасно", а оказывается ещё надо досыпать тестирование, ящики разных цветов, палки и review.

Ещё раз повторю мой тезис: дизайн API — это в первую очередь про то как обеспечить взаимодействие систем удобным для человеков и эффективным для железяк способом. Поэтому твой 3й способ
Автор: Sinclair
Дата: 08.04.25
с ссылками никуда не годится, только снижает эффективность взаимодейсвтвия, а твоё "Зато всё безопасно" — чистая иллюзия.

S>·>И то никакой гарантии, что кто-то куда-то по ошибке не скопипастит.

S>Гарантия предотвращения ошибочных копипастов достигается юнит-тестированием.
Хорошо, но уже лучше. Осталось раскрыть а причём тут вообще "API устроен именно так".

S>Преимущество тут в том, что в коде нет ветвлений — поэтому нам не надо беспокоиться, что "вдруг" в результате образуется сontragent: {homeAddress: {...}}. Достаточно 1 (одного) теста на то, что такого в json нету, и вопрос закрыт.

Тут ведь как. Либо где-то будут ветвления, либо 1024 копипасты, в которой хрен разберёшься.

S>·>В серьёзных случаях у тебя будет 1024 вида своих и чужих.

S>Это как раз в несерьёзных случаях. Серьёзные случаи — это как раз какой-нибудь банковский API. И вот там всё именно так, как я сказал. Потому что нельзя позволить ни криворукому девелоперу нечаянно проверить не ту привилегию, так и криворукому админу нечаянно выдать кому-то не ту привилегию.
Я как раз такое и видел в банковском API. Когда есть какие-то таблицы роутингов и пермишеннов. Настолько огромные, что их никто толком проверить не может. В итоге эти таблицы либо копипастятся на авось, либо генерятся скриптами, с ветвлениями. Зато статичное и проверяемое (правда в теории только).

S>·>Что значит "вынести"? Попробуй хоть как-нибудь не "вынести" какой-нибудь оракл из твоего питоячего сервиса.

S>Ну вот так и не выносим. Оракл является подробностью реализации этого "питонячьего сервиса" и ниоткуда больше не виден. Впрочем, это может быть и не оракл — "питонячьему сервису" с запасом может хватить sqlite.
Он виден с т.з. анализа безопасности.

S>·>Если у тебя два сценария, то, конечно, это щастье. А вот когда сценариев 1024...

S>Когда у вас 1024 сценария, то деваться некуда. Но чаще это означает, что кто-то не справился с факторизацией сценария, в котором есть 8 отдельных подсценариев, каждый из которых работает независимо и его можно протестировать отдельно, т.к. там вариантов гораздо меньше.
Ты почему-то с больной головы на здоровую стал перекладывать. 1024 виртуальных ресурса предложил ты, так что 1024 сценария это у вас. А я как раз писал: "Каждую комбинацию тестировать не нужно. Нужно тестировать каждую привелегию." С чем ты тут же стал спорить. Ты уж определись.

S>·>Ну будет у тебя 1024 кусков кода и неподдерживаемая кодовая база.

S>И какая альтернатива?
Написал же неделю назад: "Каждую комбинацию тестировать не нужно".

S>Прекрасно. Видна некоторая попытка факторизации. Которая, к слову, совершенно бесполезна в вашем подходе с чёрным ящиком: а вдруг завтра кто-то заменит всю вашу кунсткамеру на "питонячий код, который по рефлексии вываливает сразу всё"?

Про тестирование чёрного ящика ты придумал сам, сам и спорь.

S>Ну, и вредна конечно тоже: потому что завтра кто-то в эксплуатации не нарулит кому-то такой набор привилегий, который не даёт выполнить реальный сценарий. Зачем так делать — непонятно. Ну, кроме "нам было лень проектировать, и мы свалили ответственность на кого-то другого.

Не очень понял проблему. Если реальный сценарий не ложится на привилегии — это ошбика бизнес-требований.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[4]: Помогите правильно спроектировать микросервисное приложение
От: busk  
Дата: 16.04.25 06:30
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Можем посмотреть на пример:


S>Сервис номер 2 расположен по адресу https://graph.microsoft.com/{version}/{resource}?{query-parameters}.

S>Токены для него получаем из сервиса номер 1 по адресу https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize

S>Наличие в сервисе 1 авторизации означает, что у вас сервис номер один возвращает в токене не просто идентити пользователя, а и некий набор привилегий, которые сервис номер 2 считывает для принятия решения о том, чего пользователю делать можно, а чего — нельзя. В частности, для Microsoft Graph мы прямо в запросе авторизационного токена сервису логина указываем список scopes, к которым хотим получить доступ. И в случае успеха, токен будет содержать информацию об этом.


а вот у меня получается 2 бэкенд сервиса, а на фронте тоже 2 реакт приложения, или на фронте одним можно разрулить?
Re[5]: Помогите правильно спроектировать микросервисное приложение
От: busk  
Дата: 16.04.25 07:11
Оценка:
Здравствуйте, busk, Вы писали:


S>>Наличие в сервисе 1 авторизации означает, что у вас сервис номер один возвращает в токене не просто идентити пользователя, а и некий набор привилегий, которые сервис номер 2 считывает для принятия решения о том, чего пользователю делать можно, а чего — нельзя. В частности, для Microsoft Graph мы прямо в запросе авторизационного токена сервису логина указываем список scopes, к которым хотим получить доступ. И в случае успеха, токен будет содержать информацию об этом.


B>а вот у меня получается 2 бэкенд сервиса, а на фронте тоже 2 реакт приложения, или на фронте одним можно разрулить?


вопрос еще связан с тем, что у меня SPA а если будет 2 фронт приложения то тут же получается будет обновление страницы после страницы логина на основное приложение
Re[6]: Помогите правильно спроектировать микросервисное приложение
От: Qulac Россия  
Дата: 16.04.25 07:26
Оценка:
Здравствуйте, busk, Вы писали:

B>Здравствуйте, busk, Вы писали:



S>>>Наличие в сервисе 1 авторизации означает, что у вас сервис номер один возвращает в токене не просто идентити пользователя, а и некий набор привилегий, которые сервис номер 2 считывает для принятия решения о том, чего пользователю делать можно, а чего — нельзя. В частности, для Microsoft Graph мы прямо в запросе авторизационного токена сервису логина указываем список scopes, к которым хотим получить доступ. И в случае успеха, токен будет содержать информацию об этом.


B>>а вот у меня получается 2 бэкенд сервиса, а на фронте тоже 2 реакт приложения, или на фронте одним можно разрулить?


B>вопрос еще связан с тем, что у меня SPA а если будет 2 фронт приложения то тут же получается будет обновление страницы после страницы логина на основное приложение


А зачем так делать? Фронт можно один оставить, просто сервис на беке будет сам вызывать сервис авторизации когда это надо.
Программа – это мысли спрессованные в код
Re[7]: Помогите правильно спроектировать микросервисное приложение
От: busk  
Дата: 16.04.25 11:39
Оценка:
Здравствуйте, Qulac, Вы писали:



B>>>а вот у меня получается 2 бэкенд сервиса, а на фронте тоже 2 реакт приложения, или на фронте одним можно разрулить?


B>>вопрос еще связан с тем, что у меня SPA а если будет 2 фронт приложения то тут же получается будет обновление страницы после страницы логина на основное приложение


Q>А зачем так делать? Фронт можно один оставить, просто сервис на беке будет сам вызывать сервис авторизации когда это надо.


а я думал что фронт тоже распиливают на отдельные сервисы, но подумал, что для SPA не получится тогда сделать без третьего агрегирующего фронт сервиса.
Тогда одно на фронте, понял
Re[8]: Помогите правильно спроектировать микросервисное приложение
От: Miroff Россия  
Дата: 16.04.25 11:44
Оценка:
Здравствуйте, busk, Вы писали:

B>а я думал что фронт тоже распиливают на отдельные сервисы, но подумал, что для SPA не получится тогда сделать без третьего агрегирующего фронт сервиса.


Бывает такое, микрофронтенд называется. Идея, в целом, довольно грамотная с точки зрения не превращать фронт в неподдерживаемое месиво. А еще бывает backend for frontend (BFF)
Re[8]: Помогите правильно спроектировать микросервисное приложение
От: Qulac Россия  
Дата: 16.04.25 11:55
Оценка:
Здравствуйте, busk, Вы писали:

B>Здравствуйте, Qulac, Вы писали:




B>>>>а вот у меня получается 2 бэкенд сервиса, а на фронте тоже 2 реакт приложения, или на фронте одним можно разрулить?


B>>>вопрос еще связан с тем, что у меня SPA а если будет 2 фронт приложения то тут же получается будет обновление страницы после страницы логина на основное приложение


Q>>А зачем так делать? Фронт можно один оставить, просто сервис на беке будет сам вызывать сервис авторизации когда это надо.


B>а я думал что фронт тоже распиливают на отдельные сервисы, но подумал, что для SPA не получится тогда сделать без третьего агрегирующего фронт сервиса.

B>Тогда одно на фронте, понял

Вариант с распиливанием фронта возможен, особенно для систем с устанавливаемыми плагинами.
Программа – это мысли спрессованные в код
Re[6]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 16.04.25 14:31
Оценка: +1
Здравствуйте, Miroff, Вы писали:

M>Здравствуйте, m2user, Вы писали:


M>>Как минимум нужно предоставить документацию на API и получить feedback


M>Прелесть RMM L3 в том, что он самодокументируемый и на 99% фидбека можно сразу отвечать: "это не RESTful, мы этого делать не будем"



RMM L3 не нужен, потому что:

1. не заменяет документацию, зато дикий оверхед
2. добавляет лишней связности — теперь я должен грузить ссылки на апи (и, судя по твоим дальнейшим постам, еще и в зависимости от пермиссий) для всех связных сущностей
3. почти всегда бесполезен, тк говорит что можно, а не что нужно — то есть протекание недоделанной (бизнес-логика на базе только доступных ссылок? ну-ну) бизнес логики туда, куда не надо
Re[12]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 16.04.25 14:34
Оценка:
Здравствуйте, Miroff, Вы писали:

[]

M>Опять же, можешь раскрыть тему? Пейджинг это простая штука если перестать думать о нем, как о RPC и начать думать как об отдельном ресурсе. Ты СОЗДАЕШЬ отдельный объект "поисковый запрос" и в ответ получаешь список страниц с результатами этого запроса. И, внезапно, у тебя уже нет проблем со стабильностью, сортировки, изменению состава страниц, кэшированию и т.п. Результат фиксируется в момент создания запроса и далее до нового поиска уже не меняется.


на практике никто так не делает потому что это просто не работает. думать можно как угодно красиво, конечно

[]
Re[13]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 16.04.25 14:40
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Например, можно воткнуть reverse proxy перед сервером приложения, и тогда он сможет снять значительную часть нагрузки с сервера.

S>Как только мы начинаем менять представление объекта в зависимости от пользователя, о кэшировании можно забыть.

чет мне кажется никто ничего уже давно не кеширует

ну и

Как только мы начинаем менять представление объекта в зависимости от пользователя


тоже повсеместная практика
Re[15]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 16.04.25 14:48
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вариантов три:

S>1. При отдаче текста постановления смотрим в токен пользователя, и либо заменяем все имена плейсхолдерами, либо вставляем как надо.
S>2. Делаем два ресурса: "анонимизированное постановление", "полноценное постановление". Анонимный доступ ко второму получает 401, аутентифицированный доступ без прав на данное дело получает 403.
S>3. Делаем два ресурса: постановление, в "тексте" которого есть ссылки на фигурантов, каждый из которых — самостоятельный ресурс. Постановления отдаём всем желающим, детали фигурантов — только авторизованным пользователям. Остальные получают 403.
S>Как по мне, так первый вариант, очевидно, самый-самый плохой. Он плохо масштабируется, и крайне плохо проверяется на корректность.


Он плохо масштабируется, и крайне плохо проверяется на корректность.


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

И вообще зря ты критикуешь "клиентскую секьюрити", просто она должна дублироваться серверной. Нормально возвращать на клиента список допустимых действий для оптимизации, но HATEOAS для этого не нужен и на сервере тоже должны быть чеки.
Re[10]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 16.04.25 14:52
Оценка: +1
Здравствуйте, Miroff, Вы писали:

M>Это же ресурс, делаешь GET, смотришь, меняешь, делаешь PUT.


PUT без параметров?

S>>2. Evolvability улучшается крайне незначительно. У нас нет никакого способа научить клиентов всегда ходить только по указанным нами ссылкам. Если мы решили перенести endpoint для рефандов в другое место, то недостаточно просто отдавать новую ссылку в links.


M>Правильно делать GET перед PUT, потому что ссылки могут измениться, и вообще конкурентные обновления. Если разработчики не умеют пользоваться REST API это их проблема. Можно тупо менять ссылки при каждом запросе, чтобы даже желания сохранять их куда либо не возникало.


а если ссылки поменялись между GET и PUT? "и вообще конкурентные обновления" — какие ужасы ты пишешь

S>>3. Накладные расходы значительно увеличиваются.


M>Я тебя умоляю, мы видео в 4К по сети гоняем и GUID вместо id используем. Несколько ссылок погоды не сделают.


я не гоняю, а ты можешь гонять свои ссылки 720p тогда раз кто-то 4k гоняет
Re[16]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.04.25 16:37
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>вообще не очевидно. в любом случае тебе надо сделать фильтр данных, как ты его сделал и на каком этапе не так важно.

Не, не в любом. Только в том, если кому-то нужны дополнительные подробности.
КЛ>введением магического второго ресурса проблема фильтра никуда не уйдет.
Я не очень понимаю, что такое "проблема фильтра". Магический второй ресурс означает, что есть два ресурса: один даже join в базе не делает с таблицами "смежных сущностей", другой делает джойн, но проверяет ровно одну пермиссию в токене, а не отдаёт 2^10 вариантов в зависимости от хидера.

КЛ>и про комбинаторный взрыв тебе правильно сказали. Номер 1 самый рабочий и адекватный — по токену получаем список пермиссий и по ним фильтруем резалтсет.

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

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

Да, тут я согласен.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[17]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 16.04.25 16:58
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Константин Л., Вы писали:


КЛ>>вообще не очевидно. в любом случае тебе надо сделать фильтр данных, как ты его сделал и на каком этапе не так важно.

S>Не, не в любом. Только в том, если кому-то нужны дополнительные подробности.

ну когда не нужны тогда и фильтр не нужен, верно?

КЛ>>введением магического второго ресурса проблема фильтра никуда не уйдет.


S>Я не очень понимаю, что такое "проблема фильтра". Магический второй ресурс означает, что есть два ресурса: один даже join в базе не делает с таблицами "смежных сущностей", другой делает джойн, но проверяет ровно одну пермиссию в токене, а не отдаёт 2^10 вариантов в зависимости от хидера.


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

КЛ>>и про комбинаторный взрыв тебе правильно сказали. Номер 1 самый рабочий и адекватный — по токену получаем список пермиссий и по ним фильтруем резалтсет.

S>Ну я бы так дизайнить не стал. Соображения я привёл. У каждого они свои — вон кто-то хочет просто написать универсальный код на питоне, который через рефлексию всю базу наружу отдаёт, а пермиссии берёт из токена.

это детали, можно и в базе пермиссии проверять

S>Можно и так делать, если результат не шибко важен.


не понимаю в чем принципиальная разница с одним ресурсом и несколькими (кроме как комбинаторики)

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

S>Да, тут я согласен.

кул
Re[18]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.04.25 06:15
Оценка:
Здравствуйте, Константин Л., Вы писали:
КЛ>ну когда не нужны тогда и фильтр не нужен, верно?
Вот эту фразу не понял.

КЛ>фильтр — это способ либо собрать либо очистить ресурс. принципиальной разницы между виртуальными ресурсами (как в твоем случае) и одним ресурсом нет вообще. все равно где-то придется ответить на вопрос какие данные возвращаем

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

S>>Ну я бы так дизайнить не стал. Соображения я привёл. У каждого они свои — вон кто-то хочет просто написать универсальный код на питоне, который через рефлексию всю базу наружу отдаёт, а пермиссии берёт из токена.

КЛ>это детали, можно и в базе пермиссии проверять
Ну... в целом-то да, можно и в базе. Но у меня возникают вопросы прежде всего к надёжности такой модели. Слишком много движущихся частей => слишком много возможных путей исполнения и вариантов, когда всё может пойти "не так". При этом большинство из возможных конфигураций как настроек, так и устройства кода, являются заведомо некорректными.
По большому счёту, это является одной из причин, по которой сейчас не принято выставлять в интернет напрямую базу данных.
Казалось бы — ачего, давайте пермиссий навесим, и алга. Что-то есть в СУБД из коробки, что-то можно докрутить при помощи хранимок и представлений. Если всё делать аккуратно, получим могучую REST-подобную систему.
Однакож, нет, не делают так. Обожглись, и клиент-сервер остался только внутри "безопасных периметров", а в открытый интернет торчит исключительно трёхзвенка.
Собственно, все эти идеи "давайте отдавать базу через рефлексию" и есть способ завернуть SQL в HTTP, и ничего полезного архитектуре не добавляют.
Более продуктивный способ, имхо, всё же делить логику по уровням так, чтобы на каждом уровне по максимуму использовать его преимущества.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[19]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 17.04.25 09:05
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Константин Л., Вы писали:

КЛ>>ну когда не нужны тогда и фильтр не нужен, верно?
S>Вот эту фразу не понял.

вот:

КЛ>вообще не очевидно. в любом случае тебе надо сделать фильтр данных, как ты его сделал и на каком этапе не так важно.

S>Не, не в любом. Только в том, если кому-то нужны дополнительные подробности.

не нужны подробности -> не нужен фильтр. нужны -> нужен фильтр или доп-ресурс.

КЛ>>фильтр — это способ либо собрать либо очистить ресурс. принципиальной разницы между виртуальными ресурсами (как в твоем случае) и одним ресурсом нет вообще. все равно где-то придется ответить на вопрос какие данные возвращаем

S>Принципиальная разница тут только в надёжности. Меньше ветвлений в коде — меньше способов в них напороть.

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

S>В остальном — да, другие решения (типа расположение фильтра "перед запросом" или "после результата") сильнее влияют на параметры итогового сервиса.


S>>>Ну я бы так дизайнить не стал. Соображения я привёл. У каждого они свои — вон кто-то хочет просто написать универсальный код на питоне, который через рефлексию всю базу наружу отдаёт, а пермиссии берёт из токена.

КЛ>>это детали, можно и в базе пермиссии проверять
S>Ну... в целом-то да, можно и в базе. Но у меня возникают вопросы прежде всего к надёжности такой модели. Слишком много движущихся частей => слишком много возможных путей исполнения и вариантов, когда всё может пойти "не так". При этом большинство из возможных конфигураций как настроек, так и устройства кода, являются заведомо некорректными.

я до сих пор не понимаю как добавление ресурсов принципиально что-то меняет в твоей модели и что это у меня за такая модель?

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


а кто такое предлагает? я такое не предлагаю. и, кстати, популярный graphql именно это и делает, насколько я понимаю и я это осуждаю.

S>Казалось бы — ачего, давайте пермиссий навесим, и алга. Что-то есть в СУБД из коробки, что-то можно докрутить при помощи хранимок и представлений. Если всё делать аккуратно, получим могучую REST-подобную систему.


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

S>Однакож, нет, не делают так. Обожглись, и клиент-сервер остался только внутри "безопасных периметров", а в открытый интернет торчит исключительно трёхзвенка.

S>Собственно, все эти идеи "давайте отдавать базу через рефлексию" и есть способ завернуть SQL в HTTP, и ничего полезного архитектуре не добавляют.
S>Более продуктивный способ, имхо, всё же делить логику по уровням так, чтобы на каждом уровне по максимуму использовать его преимущества.

graphql! и да, это бред, но это не то, что я предлагаю
Re[25]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 17.04.25 11:42
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

[]

S>Я вам рассказываю практический замкнутый пример: нет никаких "привилегий на втрибуты". Есть два вида ресурсов. Доступ к каждому их них либо есть, либо нету.


что значит есть? физически есть? то есть реально в базе 2 таблицы?

S>·>Каким образом закрыт-то? Заклятие наложено что-ли? Субресурс возвращает джсон, как ты ресурсом гарантируешь отстутствие там какого-нибудь contragent.homeAddress?

S>Очень просто: contragent.homeAddress там нет вообще, вне зависимости от привилегий.

кто его оттуда вырезал?

S>·>Нет, это просто несколько байт в определённом месте хедера запроса, а обрабатывает всё это дело тот же самый условно питонячий код, что и для других ресурсов.

S>Если у вас так, то всё плохо. Попробуйте перепроектировать так, чтобы в одном коде не смешивалась обработка "своих" и "чужих" инвойсов. Человечество изобрело массу способов факторизации кода.

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

S>Впрочем, можно спроектировать и так, как вы предлагаете — вынести БД в отдельный сервис, из которого торчит очень широкий контракт.


он так не предлагает

S>И, естественно, у него очень грубый набор привилегий. Ваша модель "давайте мы будем ограничивать доступ путём передачи пользовательского токена вдоль всей иерархии вызовов" работает примерно никогда.


примерно всегда так и работает

S>Все практики безопасности как раз так и устроены, что у принципала X нету привилегий к "сырому" ресурсу A, но есть привилегии на доступ к "производному" ресурсу B. Сервис, выполняющий построение ресурса B, выполняется под принципалом Y, который имеет полные привилегии для A, но учитывает привилегии X при отдаче ему производного ресурса B.


ну то есть делает именно то, что мы тут тебе и говорим, только ты все это каким-то магическим образом разрулишь всего лишь через роутинг?

S>Типичный пример применения концепции в рамках SQL99: пользователю нельзя видеть часть строк в таблице X. Мы не можем навесить привилегию на каждую строку.


почему это мы не можем?

Зато можем сделать следующее:
S>1. Отбираем у пользователя привилегии на таблицу X

зачем? только столбцы выбросить? такое работает только когда у тебя 2-3 роли в аппке и мало пермиссий

S>2. Строим на основе X представление Y, в котором добавлен предикат безопасности (типа select * from X where TotalAmount < 100000)

S>3. Выдаём пользователю привилегии на Y и указываем, что Y исполняется под привилегиями админа, а не пользователя.

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

S>Аналогично мы могли бы поступить и с ограничениями на колонки таблицы. Квадратно-гнездовая система, где чётко видна вся схема Y и легко проверить, что туда попадает, а что нет, как методом статического анализа, так и методом выполнения динамических тестов. Вы предлагаете заменить её на некий невнятный код "хранимой процедуры", которая возвращает всякий раз разный набор строк и колонок в зависимости от рантайм-содержимого параметров.


действительно, результат поискового запроса зависит от критериев поиска (пермиссии один из них внезапно), вот это дичь, да?

S>И всё это под тем предлогом, что "какая нам разница, где передаётся информация о привилегиях — всё равно это текст SQL запроса".

S>Нет, увы. Факторизация кода позволяет нам изолировать разные сценарии.

для петпроджектов да, но мы о нормальных системах говорим

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


а если ты внутри роутинга не туда нароутил?

S>В вашем же подходе в коде какая-то каша, отдаётся примерно произвольный набор атрибутов с соответствии с хитро устроенными предикатами, смешивающими проверку нескольких разных claims. Доказать, что код выдаёт корректное сочетание атрибутов для произвольного сочетания claims — та ещё задача.


ну это звучит как "доказать, что код выдает правильную поисковую выдачу та еще задача".
Re[27]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 17.04.25 11:46
Оценка:
Здравствуйте, Sinclair, Вы писали:

[]

S>·>Ну я просто написал когда этот твой способ не работает. Но я, конечно, верю, что в некоторых случаях и твой способ работает. Но это не значит, что он универсальный всемогутер.

S>Скорее наоборот: бывают задачи с низкой ответственностью, где можно игнорировать best practices и писать с надеждой на авось.

сколько систем ты так сделал и сколько у них юзеров и ролей?
Re[26]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.04.25 12:37
Оценка: +1
Здравствуйте, Константин Л., Вы писали:

S>>Я вам рассказываю практический замкнутый пример: нет никаких "привилегий на втрибуты". Есть два вида ресурсов. Доступ к каждому их них либо есть, либо нету.

КЛ>что значит есть? физически есть? то есть реально в базе 2 таблицы?
Совершенно необязательно. В базе это может быть одна таблица, 2 таблицы, или 17 таблиц. Структура базы тут очень косвенно задействована.

КЛ>кто его оттуда вырезал?

Я не понимаю термина "вырезал". Никто никого ниоткуда не "вырезал".
Просто у нас есть такой тип данных, InboundInvoice, который отличается от OutboundInvoice.
На стороне сервиса они могут находиться в каких-то взаимоотношениях друг с другом (типа — оба отнаследованы от общей базы, или собираются путём алгебры типов из запчастей, вроде CommonInvoice & InboundInvoice / CommonInvoice & OutboundInvoice).
Снаружи мы всё равно видим только схему Json, и она разная для разных ендпоинтов.

КЛ>ты тут смешиваешь свои и чужие инвойсы, чтобы навести туману.

Это не я смешиваю
КЛ>давай мы будем всегда про свои инвойсы, но в одном случае для отчетности, в другом для превью — как хранить будешь и разделять?
Я не буду их разделять — зачем? Мне непонятен сценарий. Если дадите больше деталей — можно спроектировать.
КЛ>он так не предлагает
Цитирую:

А на самом деле json генерится питонячим кодом, который рефлексией вываливает всё что есть

КЛ>примерно всегда так и работает
Поверю вам на слово.

КЛ>ну то есть делает именно то, что мы тут тебе и говорим

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

КЛ>почему это мы не можем?

Потому что row level security есть далеко не в любой СУБД. И даже в тех, где такое есть, далеко не всегда это будет наиболее эффективным решением.
КЛ>зачем? только столбцы выбросить?
И строки тоже.

КЛ>такое работает только когда у тебя 2-3 роли в аппке и мало пермиссий

Такое работает примерно всегда. Я ещё ни разу не видел настолько безумной архитектуры, чтобы SQL исполнялся в контексте безопасности конечного пользователя, пришедшего из интернета.

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

"Его" — это кого? Поле?

КЛ>селект учитывает пермиссии юзера, получение по токену, чтобы выбрать только нужные строки, поля. работает с любым сочетанием и количеством ролей и пермиссий.

Можете привести пример такого select? Я вроде понимаю, о чём вы пишете, но уверенности нет.

КЛ>а ведь еще ничего не сказано про ABAC, который часто нужен и который в твою схему вообще не вписывается

Да, для ABAC нужна другая схема. Но он очень часто является оверкиллом.

КЛ>действительно, результат поискового запроса зависит от критериев поиска (пермиссии один из них внезапно), вот это дичь, да?

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

КЛ>для петпроджектов да, но мы о нормальных системах говорим

Ну, критерии "нормальных систем" у всех разные.

КЛ>а если ты внутри роутинга не туда нароутил?

То это будет обнаружено первым же smoke test.

КЛ>ну это звучит как "доказать, что код выдает правильную поисковую выдачу та еще задача".

Конечно. Это вполне актуальная проблема; и основное её решение ровно то же, что я предлагаю — факторизация. Потому что иначе вы точно так же утонете в объёме тестирования.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[20]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.04.25 12:47
Оценка:
Здравствуйте, Константин Л., Вы писали:


КЛ>>вообще не очевидно. в любом случае тебе надо сделать фильтр данных, как ты его сделал и на каком этапе не так важно.

S>>Не, не в любом. Только в том, если кому-то нужны дополнительные подробности.
КЛ>не нужны подробности -> не нужен фильтр. нужны -> нужен фильтр или доп-ресурс.
Вы фильтром называете проекцию или что-то ещё?

КЛ>ну смотри, у тебя два юзкейса — полный ресурс или урезанный.

Строго говоря, у нас может быть и больше юзкейзов.

КЛ>даже если ты будешь разруливать их через отдельные ресурсы где-то будет ветвление на создание (фильтрацию, сборку, как угодно) одного и другого.

Если я это делаю явным образом, то у меня это ветвление покрывается системой типов, и его статически контролирует компилятор ещё до того, как запустятся тесты.

КЛ>я до сих пор не понимаю как добавление ресурсов принципиально что-то меняет в твоей модели и что это у меня за такая модель?

У вас модель, в которой заранее неизвестно, какая структура ресурса будет получена при обращении по тому или иному ендпоинту. Она типа "динамически" определяется где-то внутри кода непрозрачными правилами.

КЛ>а кто такое предлагает? я такое не предлагаю. и, кстати, популярный graphql именно это и делает, насколько я понимаю и я это осуждаю.

Нет, graphQL делает не это.

КЛ>не понимаю к чему ты. мы лишь про то, что по токену на беке получаем пермиссии и в зависимости он них отдаем респонс.

Как мы это будем тестировать?

КЛ>graphql! и да, это бред, но это не то, что я предлагаю

В graphQL принято несколько неудачных решений, но не потому, что он "выставляет наружу БД".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[27]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 17.04.25 15:07
Оценка: +1
Здравствуйте, Sinclair, Вы писали:

S>Снаружи мы всё равно видим только схему Json, и она разная для разных ендпоинтов.


и как и откуда в эту схему кладутся данные?

я, кажется, начинаю понимать — по dto на роль, ORM, отдельные endpoints и вуаля? но тут есть 2 проблемы — работает только на простых security моделях, и никак не решает проблем, когда внутри этого dto есть многокардинальные поля (массивы etc, которые тоже надо фильтровать по роли).


ну есть еще конечно другой вариант — когда очень много денег и очень много ответственности и надо прям все по-красоте. но это не индустриальный стандарт

КЛ>>ты тут смешиваешь свои и чужие инвойсы, чтобы навести туману.

S>Это не я смешиваю


КЛ>>давай мы будем всегда про свои инвойсы, но в одном случае для отчетности, в другом для превью — как хранить будешь и разделять?

S>Я не буду их разделять — зачем? Мне непонятен сценарий. Если дадите больше деталей — можно спроектировать.
КЛ>>он так не предлагает

мне кажется он специально преувеличил, но не суть

S>Цитирую:

S>

S>А на самом деле json генерится питонячим кодом, который рефлексией вываливает всё что есть



КЛ>>ну то есть делает именно то, что мы тут тебе и говорим

S>Нет, делает ровно противоположное. Более того, прямо тут в топике приводились утверждения про то, что "на самом деле" микросервисы не только пользовательских принципалов не используют, а вообще бегают все под одним и тем же аккаунтом.

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

КЛ>>почему это мы не можем?

S>Потому что row level security есть далеко не в любой СУБД. И даже в тех, где такое есть, далеко не всегда это будет наиболее эффективным решением.

вот и начинается, часть пермиссий через средства субд, часть сбоку, чтд

КЛ>>зачем? только столбцы выбросить?

S>И строки тоже.

так у вас нет row-level permission же

КЛ>>такое работает только когда у тебя 2-3 роли в аппке и мало пермиссий


S>Такое работает примерно всегда. Я ещё ни разу не видел настолько безумной архитектуры, чтобы SQL исполнялся в контексте безопасности конечного пользователя, пришедшего из интернета.


" SQL исполнялся в контексте безопасности конечного пользователя " — вот это вообще что такое? что тут ты понимаешь под контекстом? а когда ты делаешь эти свои лишние телодвижения из 3х пунктов, ты разве не мапишь "контексте безопасности конечного пользователя, пришедшего из интернета" на конкретную роль в базе?

sql исполняется от админа всегда и точка. резалсеты зависят от пермиссий юзера из интернета.

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

S>"Его" — это кого? Поле?

строку, опечатка конечно

КЛ>>селект учитывает пермиссии юзера, получение по токену, чтобы выбрать только нужные строки, поля. работает с любым сочетанием и количеством ролей и пермиссий.

S>Можете привести пример такого select? Я вроде понимаю, о чём вы пишете, но уверенности нет.

select * from rows r
join rows_perms p on r.id = p.r_id
where p.perm = 'row_read' and p.user_id = :userid

или

select * from rows r where r.group_id in :user_groups

userid/user_groups резолвятся по токену из запроса

да как угодно, при том, что работает тот самый швейцарский сыр, про который тут упоминали —
возможность вообще что-то читать из таблицы rows проверяется до отправки sql в базу. все запросы от админа. в базе никакой встроенной security не используется, потому что, опять же, нет нормально row-level security встроенного. С полями — либо динамически генеришь строку sql, либо jooq (предпочтительнее)

select fields from field_perm where field_perm.perm = 'row_read_for_report'

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


КЛ>>а ведь еще ничего не сказано про ABAC, который часто нужен и который в твою схему вообще не вписывается

S>Да, для ABAC нужна другая схема. Но он очень часто является оверкиллом.

ABAC, это когда ты принимаешь решение не только от роли, но и контекста — времени запроса и пт. То есть довольно часто.

КЛ>>действительно, результат поискового запроса зависит от критериев поиска (пермиссии один из них внезапно), вот это дичь, да?

S>Отож. Не, я всё это умею делать. Но в эксплуатации такие "гибкие схемы" гораздо чаще приносят вред, чем пользу.
S>Вот, в прошлом году в одной крупной компании был случай, когда из-за цепочки нечаянных совпадений и недопониманий друг друга, одному из сотрудников выдали чутка больше "пермиссий", чем надо было по его роли.
S>В итоге сотрудник из лучших побуждений наворотил делов. Хорошо, что речь шла о добронамеренном сотруднике, поэтому последствия были не очень тяжкими (ну, там, зарплату задержали на пару дней части сотрудников). А в иной ситуации это бы вышло таким боком, что никакая экономия на архитектуре не окупилась бы.

да, но ничего с точки зрения трейдоффа разработка/результат не придумали. открываешь google aim и видишь и роли, и отдельные пермиссии.

КЛ>>для петпроджектов да, но мы о нормальных системах говорим

S>Ну, критерии "нормальных систем" у всех разные.

много ролей, много разных пермиссий

КЛ>>а если ты внутри роутинга не туда нароутил?

S>То это будет обнаружено первым же smoke test.

ага, то есть любой смоук тест у нас в лобой системе обнаруживает любую проблему?

КЛ>>ну это звучит как "доказать, что код выдает правильную поисковую выдачу та еще задача".

S>Конечно. Это вполне актуальная проблема; и основное её решение ровно то же, что я предлагаю — факторизация. Потому что иначе вы точно так же утонете в объёме тестирования.

и никто ее не делает, потому что это невозможно поддерживать
Отредактировано 17.04.2025 15:24 Кt Л. . Предыдущая версия .
Re[21]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 17.04.25 15:17
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Вы фильтром называете проекцию или что-то ещё?


да

КЛ>>ну смотри, у тебя два юзкейса — полный ресурс или урезанный.

S>Строго говоря, у нас может быть и больше юзкейзов.

вам и говорят, когда много, тогда у вас проблемы.

КЛ>>даже если ты будешь разруливать их через отдельные ресурсы где-то будет ветвление на создание (фильтрацию, сборку, как угодно) одного и другого.

S>Если я это делаю явным образом, то у меня это ветвление покрывается системой типов, и его статически контролирует компилятор ещё до того, как запустятся тесты.

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

КЛ>>я до сих пор не понимаю как добавление ресурсов принципиально что-то меняет в твоей модели и что это у меня за такая модель?

S>У вас модель, в которой заранее неизвестно, какая структура ресурса будет получена при обращении по тому или иному ендпоинту. Она типа "динамически" определяется где-то внутри кода непрозрачными правилами.

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

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


КЛ>>а кто такое предлагает? я такое не предлагаю. и, кстати, популярный graphql именно это и делает, насколько я понимаю и я это осуждаю.

S>Нет, graphQL делает не это.

а что он делает?

КЛ>>не понимаю к чему ты. мы лишь про то, что по токену на беке получаем пермиссии и в зависимости он них отдаем респонс.

S>Как мы это будем тестировать?

а как вы тестируете все остальное, что отдает данные на основании пользовательского контекста?

КЛ>>graphql! и да, это бред, но это не то, что я предлагаю

S>В graphQL принято несколько неудачных решений, но не потому, что он "выставляет наружу БД".

да, но выставляет наружу псевдо-бд
Отредактировано 17.04.2025 15:38 Кt Л. . Предыдущая версия .
Re[28]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.04.25 17:10
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>и как и откуда в эту схему кладутся данные?

Маппером, который сгенерирован по типу данных. Кладутся из DTO, который поднят из базы.

КЛ>я, кажется, начинаю понимать — по dto на роль, ORM, отдельные endpoints и вуаля?

Конечно.
КЛ>но тут есть 2 проблемы — работает только на простых security моделях,
Модели безопасности и должны быть простыми. Сложные модели снижают надёжность. Парадокс в том, что когда стоимость ошибки в секурити низкая, на неё можно просто забить. А когда высокая, то сложные модели слишком рискованны.
КЛ>и никак не решает проблем, когда внутри этого dto есть многокардинальные поля (массивы etc, которые тоже надо фильтровать по роли).
Их не надо фильтровать по роли. Это плохая идея — слишком легко напороть.
КЛ>ну есть еще конечно другой вариант — когда очень много денег и очень много ответственности и надо прям все по-красоте. но это не индустриальный стандарт
Да, я и говорю про серьёзные приложения, где ответственности много. А если её мало, то примерно любая секурити подойдёт.

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

Ну, с моей точки зрения криво делаете вы.
Получившимся API совершенно невозможно пользоваться, кроме как напрямую выкладывать его в ГУЙ. Любой сервис, который пытается стать клиентом этого API, вынужден жить в постоянной готовности получить не данные, а фигу.
И как-то осмысленно объяснять пользователю, что "у тебя, брат, нет пермиссии на атрибут 'НДС', сходи к админу, попроси, чтоб выдал". Ну, либо (что скорее всего) внезапно падать с internal error 500, потому что внутрях там NullReferenceException. Разобраться, что там стало первопричиной, потребует привлечения дорогостоящего саппорта. Так что в пет проджектах, где пользователей меньше, чем ролей и пермиссий вместе взятых — велком, можно и так. А для чего-то серьёзного, где нужна предсказуемость работы — увольте.

КЛ>вот и начинается, часть пермиссий через средства субд, часть сбоку, чтд

Где это я предложил часть пермиссий через средства СУБД? Я вам привёл стандартный способ разделения доступа "построчно" без использования row-level security.

КЛ>так у вас нет row-level permission же

Я вроде дал пошаговое описание того, как решается проблема отсутствия row-level permissions в RDBMS. Причём не как рекомендацию использовать эту схему, а как пример реализации "послойной" безопасности.
Точно такие же рецепты применяются и в сервисах.
То есть токен пользователя у нас роляет только во фронт-сервисе. Когда пользователь U запрашивает API сервиса A, а сервис A в рамках этого запроса стучится в сервис B, то никакие токены U в этот B не передаются. У U вообще нет никакого доступа до сервиса B. В этот сервис обращается A, под собственным токеном.
Поэтому идея "а давайте у нас инвойс будет содержать данные контрагента если у вызывающего пользователя есть пермиссия на этого контрагента" сразу идёт навзничь.
У вызываюшего пользователя нет никаких пермиссий на контрагента — господь упаси. А вот право видеть реквизиты получателя платежа по выставленному счёту у него есть в силу федерального закона.


КЛ>" SQL исполнялся в контексте безопасности конечного пользователя " — вот это вообще что такое?

Это когда мы заводим для пользователя №44487 виндовый аккаунт, даём этому аккаунту права на SQL базу с табличками forums, topics, replies, и marks, при логине на сайт создаём виндовую сессию, и все запросы в SQL Server выполняем через новое соединение под правами этого виндового аккаунта.

КЛ>что тут ты понимаешь под контекстом? а когда ты делаешь эти свои лишние телодвижения из 3х пунктов, ты разве не мапишь "контексте безопасности конечного пользователя, пришедшего из интернета" на конкретную роль в базе?

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

КЛ>sql исполняется от админа всегда и точка. резалсеты зависят от пермиссий юзера из интернета.

Воот, уже лучше.

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

S>>"Его" — это кого? Поле?

КЛ>строку, опечатка конечно


КЛ>>>селект учитывает пермиссии юзера, получение по токену, чтобы выбрать только нужные строки, поля. работает с любым сочетанием и количеством ролей и пермиссий.

S>>Можете привести пример такого select? Я вроде понимаю, о чём вы пишете, но уверенности нет.

КЛ>select * from rows r

КЛ>join rows_perms p on r.id = p.r_id
КЛ> where p.perm = 'row_read' and p.user_id = :userid
Прекрасно. Вы себе представляете производительность такого решения? Когда у нас табличка на пару миллионов строк, и есть тысяча пользователей с частично пересекающимися правами на эти строки?
КЛ>или
КЛ>select * from rows r where r.group_id in :user_groups
А как же "любое сочетание ролей и пермиссий"? Вы прибиваете доступ к r только к одной роли; невозможно выдать доступ к конкретной строке нескольким разным группам.
Собственно, вы изобретаете нормальную изоляцию на основе бизнес-правил, только задом наперёд.
Потому что в прямом направлении мы не стесняемся назвать этот абстрактный group_id своим именем.
Например,
select * from topics t where t.owner_id = :user_id

Или
select * from invoices i where i.sender_organization = :current_org_id


Это — совершенно нормальная история, которая работает и применяется.
КЛ>userid/user_groups резолвятся по токену из запроса
Ну, вы же только что собирались сделать универсальную схему, которая работает с любым количеством пермиссий и ролей. А получается вполне себе узкоспециализированная штука.

КЛ>да как угодно, при том, что работает тот самый швейцарский сыр, про который тут упоминали —

КЛ>возможность вообще что-то читать из таблицы rows проверяется до отправки sql в базу. все запросы от админа. в базе никакой встроенной security не используется, потому что, опять же, нет нормально row-level security встроенного. С полями — либо динамически генеришь строку sql, либо jooq (предпочтительнее)
КЛ>select fields from field_perm where field_perm.perm = 'row_read_for_report'
КЛ>и дальше ипользуешь результат для генерации верхних селектов с конкретными полями.
КЛ>либо уже просто в коде фильтруешь
Непонятно. Как я смогу получить разный набор полей для разных строк одной и той же таблицы? Речь-то шла именно об этом — что состав результата определяется не объектом, а правами пользователя на него.

КЛ>ABAC, это когда ты принимаешь решение не только от роли, но и контекста — времени запроса и пт. То есть довольно часто.

Я в курсе.

КЛ>да, но ничего с точки зрения трейдоффа разработка/результат не придумали. открываешь google aim и видишь и роли, и отдельные пермиссии.

Надо внимательно смотреть на то, как именно используются эти роли и пермиссии. Потому что я так-то не против ролей и пермиссий; я против смешивания мух и котлет.
И если я запрашиваю, допустим, GET /resources/{resourceId}?$include(usageHistory), а у меня нет пермиссий смотреть usageHistory по этому ресурсу, я получаю не пустую историю и не JSON с "вырезанным" атрибутом, а честный 403. А если у меня пермиссия есть, то я могу полагаться на то, что в результате будет атрибут usageHistory с описанной в схеме структурой.
В итоге сторонний сервис, который потребляет мой API, может выдавать осмысленные ошибки, которые можно быстро исправить. А не просто падать оттого, что его авторы тестировались с аккаунтом, у которого были все нужные пермиссии, и понятия не имели, что у кого-то пермиссия на usageHistory на какой-то отдельный объект может быть отобрана.

Обратите внимание — это то самое "разведение ресурсов по разным URL", над которым вы так потешались.


КЛ>много ролей, много разных пермиссий

Количество ни на что не влияет.

КЛ>ага, то есть любой смоук тест у нас в лобой системе обнаруживает любую проблему?

Нет, только детерминистические вещи.

КЛ>и никто ее не делает, потому что это невозможно поддерживать

Надо просто уметь это готовить.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[22]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.04.25 17:23
Оценка:
Здравствуйте, Константин Л., Вы писали:
S>>Вы фильтром называете проекцию или что-то ещё?
КЛ>да
Хорошо.

КЛ>вам и говорят, когда много, тогда у вас проблемы.

У нас нет никаких проблем.

КЛ>система типов это только про поля, а что со строками?

КЛ>это не гибко
Излишняя гибкость, в целом, только вредит. Но фильтрация, в отличие от проекции, не нарушает никаких инвариантов.
Поэтому в ней запросто можно учитывать предикаты, в том числе и построенные на токене пользователя.
По-прежнему у нас границы безопасности совпадают с границами ресурса.
Смотрите: вот я делаю какой-нибудь
GET /invoices/

И мне приезжает какой-то список инвойсов. Как правило — без деталей, только ссылки.
И если я пойду по этим ссылкам, типа GET /invoices/32423423423/, то скорее всего буду получать 200 Ok.
А вот те из инвойсов, на которые у меня прав нет, дадут при прямом обращении 403 либо 404, и в общем списке фигурировать не будут.

КЛ>еше раз — пермисии это такой же фильтр данных как и остальные. разрулить их системой типов хорошо не получится.

Прекрасно получится.

КЛ>пример — у юзера есть 5 основных воркфлоу, в один из которых входит создание внутренних отчетов из таблицы rows, в другой создание анонимизтированных отчетов для регулятора.

КЛ>внутренний отчет показывает конфиденциальные данные, для регулятора отрезает их.
КЛ>опиши security-модель для такого? сколько ролей? как все работает?
Пока что роль ровно одна. Всё работает так, как написано. Причин изобретать разные роли тут нет.
Для проектирования безопасности у нас недостаточно вводных.

КЛ>а что он делает?

Он делает возможность формировать запросы к иерархии объектов, без всех плюшек REST.

КЛ>а как вы тестируете все остальное, что отдает данные на основании пользовательского контекста?



КЛ>да, но выставляет наружу псевдо-бд

Есть много разных способов выставить наружу псевдо-бд, и не все из них одинаково полезны.
Надо понимать, какая проблема решается.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[29]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 17.04.25 18:02
Оценка:
Здравствуйте, Sinclair, Вы писали:

КЛ>>и как и откуда в эту схему кладутся данные?

S>Маппером, который сгенерирован по типу данных. Кладутся из DTO, который поднят из базы.

то есть пермисии определяются декларативно просто наличием полей в типе? ну-ну

КЛ>>я, кажется, начинаю понимать — по dto на роль, ORM, отдельные endpoints и вуаля?

S>Конечно.

вау

КЛ>>но тут есть 2 проблемы — работает только на простых security моделях,

S>Модели безопасности и должны быть простыми. Сложные модели снижают надёжность. Парадокс в том, что когда стоимость ошибки в секурити низкая, на неё можно просто забить. А когда высокая, то сложные модели слишком рискованны.

"хорошо это когда хорошо".

КЛ>>и никак не решает проблем, когда внутри этого dto есть многокардинальные поля (массивы etc, которые тоже надо фильтровать по роли).

S>Их не надо фильтровать по роли. Это плохая идея — слишком легко напороть.

тогда _какую_ проблему решают твои виртуальные ресурсы? никакую

КЛ>>ну есть еще конечно другой вариант — когда очень много денег и очень много ответственности и надо прям все по-красоте. но это не индустриальный стандарт

S>Да, я и говорю про серьёзные приложения, где ответственности много. А если её мало, то примерно любая секурити подойдёт.

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

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

S>Ну, с моей точки зрения криво делаете вы.
S>Получившимся API совершенно невозможно пользоваться, кроме как напрямую выкладывать его в ГУЙ. Любой сервис, который пытается стать клиентом этого API, вынужден жить в постоянной готовности получить не данные, а фигу.

при чем здесь вообще апи, когда мы говорим про то, как делать секьюрити внутри?

S>И как-то осмысленно объяснять пользователю, что "у тебя, брат, нет пермиссии на атрибут 'НДС', сходи к админу, попроси, чтоб выдал".


Google Cloud так и делает если что

S>Ну, либо (что скорее всего) внезапно падать с internal error 500, потому что внутрях там NullReferenceException.


домыслы

S>Разобраться, что там стало первопричиной, потребует привлечения дорогостоящего саппорта. Так что в пет проджектах, где пользователей меньше, чем ролей и пермиссий вместе взятых — велком, можно и так. А для чего-то серьёзного, где нужна предсказуемость работы — увольте.


дорогостоящий саппорт? дороже разработки?

КЛ>>вот и начинается, часть пермиссий через средства субд, часть сбоку, чтд

S>Где это я предложил часть пермиссий через средства СУБД? Я вам привёл стандартный способ разделения доступа "построчно" без использования row-level security.

ваши роли в субд и есть часть пермиссий через субд

КЛ>>так у вас нет row-level permission же

S>Я вроде дал пошаговое описание того, как решается проблема отсутствия row-level permissions в RDBMS. Причём не как рекомендацию использовать эту схему, а как пример реализации "послойной" безопасности.

а что тогда надо использовать в твоем случае?

S>Точно такие же рецепты применяются и в сервисах.

S>То есть токен пользователя у нас роляет только во фронт-сервисе. Когда пользователь U запрашивает API сервиса A, а сервис A в рамках этого запроса стучится в сервис B, то никакие токены U в этот B не передаются. У U вообще нет никакого доступа до сервиса B. В этот сервис обращается A, под собственным токеном.
S>Поэтому идея "а давайте у нас инвойс будет содержать данные контрагента если у вызывающего пользователя есть пермиссия на этого контрагента" сразу идёт навзничь.
S>У вызываюшего пользователя нет никаких пермиссий на контрагента — господь упаси. А вот право видеть реквизиты получателя платежа по выставленному счёту у него есть в силу федерального закона.

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

КЛ>>" SQL исполнялся в контексте безопасности конечного пользователя " — вот это вообще что такое?

S>Это когда мы заводим для пользователя №44487 виндовый аккаунт, даём этому аккаунту права на SQL базу с табличками forums, topics, replies, и marks, при логине на сайт создаём виндовую сессию, и все запросы в SQL Server выполняем через новое соединение под правами этого виндового аккаунта.

кто так вообще делает? выглядит как полная дичь если честно

КЛ>>>>селект учитывает пермиссии юзера, получение по токену, чтобы выбрать только нужные строки, поля. работает с любым сочетанием и количеством ролей и пермиссий.

S>>>Можете привести пример такого select? Я вроде понимаю, о чём вы пишете, но уверенности нет.

КЛ>>select * from rows r

КЛ>>join rows_perms p on r.id = p.r_id
КЛ>> where p.perm = 'row_read' and p.user_id = :userid
S>Прекрасно. Вы себе представляете производительность такого решения? Когда у нас табличка на пару миллионов строк, и есть тысяча пользователей с частично пересекающимися правами на эти строки?

да, индекс на rows_perms.user_id и будет быстро, плюс там еще в 99 процентах будет предикат на индексированное поле как часть фильтра поискового запроса. да, часто данные для всех лежать в одной таблице и когда нужен row-level security у тебя просто нет выбора. либо копии данных (твои views видимо) либо так.

но это на запрос коллекции, а на запрос одного ресурса вообще все быстро потому что индекс и на rows_perms.id

select * from rows r
join rows_perms p on r.id = p.r_id
where p.perm = 'row_read' and p.user_id = :userid and r.id = :id

КЛ>>или

КЛ>>select * from rows r where r.group_id in :user_groups
S>А как же "любое сочетание ролей и пермиссий"? Вы прибиваете доступ к r только к одной роли; невозможно выдать доступ к конкретной строке нескольким разным группам.
S>Собственно, вы изобретаете нормальную изоляцию на основе бизнес-правил, только задом наперёд.

ты просил примеры, я привел сложный и простой (для твоей модели где есть только роли)

S>Это — совершенно нормальная история, которая работает и применяется.

КЛ>>userid/user_groups резолвятся по токену из запроса
S>Ну, вы же только что собирались сделать универсальную схему, которая работает с любым количеством пермиссий и ролей. А получается вполне себе узкоспециализированная штука.

первый пример довольно универсален, он иллюстрирует подход "на основании токена фильтруем резалтсет и тп"

S>Непонятно. Как я смогу получить разный набор полей для разных строк одной и той же таблицы? Речь-то шла именно об этом — что состав результата определяется не объектом, а правами пользователя на него.


на чистом sql очевидно никак, для некоторых строк в резалтсете надо фильтровать поля (будут null), если ты об этом
а в твоем случае как? у тебя же схема прибита гвоздями

КЛ>>да, но ничего с точки зрения трейдоффа разработка/результат не придумали. открываешь google aim и видишь и роли, и отдельные пермиссии.

S>Надо внимательно смотреть на то, как именно используются эти роли и пермиссии. Потому что я так-то не против ролей и пермиссий; я против смешивания мух и котлет.
S>И если я запрашиваю, допустим, GET /resources/{resourceId}?$include(usageHistory), а у меня нет пермиссий смотреть usageHistory по этому ресурсу, я получаю не пустую историю и не JSON с "вырезанным" атрибутом, а честный 403. А если у меня пермиссия есть, то я могу полагаться на то, что в результате будет атрибут usageHistory с описанной в схеме структурой.

как тут помогают роутеры?

S>В итоге сторонний сервис, который потребляет мой API, может выдавать осмысленные ошибки, которые можно быстро исправить. А не просто падать оттого, что его авторы тестировались с аккаунтом, у которого были все нужные пермиссии, и понятия не имели, что у кого-то пермиссия на usageHistory на какой-то отдельный объект может быть отобрана.


да ну я тебе сделаю отдельный endpoint, но внутри будет один движок по доставке данных, который может тебе отдавать 403 в когда надо

S>Обратите внимание — это то самое "разведение ресурсов по разным URL", над которым вы так потешались.


мы потешаемся не над ним, а над тем, что ты полагаешься только на него

КЛ>>много ролей, много разных пермиссий

S>Количество ни на что не влияет.

на комбинаторный взрыв сущностей

КЛ>>ага, то есть любой смоук тест у нас в лобой системе обнаруживает любую проблему?

S>Нет, только детерминистические вещи.

это что? каков критерий?

КЛ>>и никто ее не делает, потому что это невозможно поддерживать

S> Надо просто уметь это готовить.


сколько раз мы уже это слышали по поводу всего. похоже на отмазку для неадекватного задаче решения
Отредактировано 17.04.2025 19:26 Кt Л. . Предыдущая версия . Еще …
Отредактировано 17.04.2025 18:57 Кt Л. . Предыдущая версия .
Re[23]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 17.04.25 18:11
Оценка:
Здравствуйте, Sinclair, Вы писали:

КЛ>>система типов это только про поля, а что со строками?

КЛ>>это не гибко
S>Излишняя гибкость, в целом, только вредит. Но фильтрация, в отличие от проекции, не нарушает никаких инвариантов.

не понял

S>Поэтому в ней запросто можно учитывать предикаты, в том числе и построенные на токене пользователя.

S>По-прежнему у нас границы безопасности совпадают с границами ресурса.
S>Смотрите: вот я делаю какой-нибудь
S>
S>GET /invoices/
S>

S>И мне приезжает какой-то список инвойсов. Как правило — без деталей, только ссылки.
S>И если я пойду по этим ссылкам, типа GET /invoices/32423423423/, то скорее всего буду получать 200 Ok.
S>А вот те из инвойсов, на которые у меня прав нет, дадут при прямом обращении 403 либо 404, и в общем списке фигурировать не будут.

ну у нас так же, в чем поинт?

S>Для проектирования безопасности у нас недостаточно вводных.


смешно

КЛ>>а что он делает?

S>Он делает возможность формировать запросы к иерархии объектов, без всех плюшек REST.

да не, по сути он дает тебе query language к базе

КЛ>>а как вы тестируете все остальное, что отдает данные на основании пользовательского контекста?


S>


отвечай!

КЛ>>да, но выставляет наружу псевдо-бд

S>Есть много разных способов выставить наружу псевдо-бд, и не все из них одинаково полезны.
S>Надо понимать, какая проблема решается.

ну начинается! отлыниваешь )
Re[30]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.04.25 05:31
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>то есть пермисии определяются декларативно просто наличием полей в типе? ну-ну

Не уверен, что я понял ваш вопрос, но скорее всего ответ "нет".
КЛ>"хорошо это когда хорошо".
А то.
КЛ>тогда _какую_ проблему решают твои виртуальные ресурсы? никакую
Конечно. Они не создают проблему. А решают они задачу — задачу ограничения доступа к определённым атрибутам для определённых групп пользователей.

КЛ>серьезные приложения это софт для АЭС, там можно. В 99 процентах других случаев твоя модель не работает, такая моя оценка

Софт для АЭС тут ни при чём. А серьёзные приложения — это те, к примеру, которые автоматизируют деятельность, подлежащую регулированию каким-нибудь FinCEN.
Когда у вас есть реальная перспектива продолбать крипты суммарной стоимостью под миллиард, рассуждения в стиле "у меня в коде всё было правильно, это тупой админ нечаянно выдал лишнюю пермиссию на один из атрибутов одного из объектов одному из пользователей" не сильно помогают избежать кусания локтей.

КЛ>при чем здесь вообще апи, когда мы говорим про то, как делать секьюрити внутри?

С чего это вдруг?
Мы всю дорогу говорили именно про то, как должен выглядеть API снаружи. То, что там внутри, в первую очередь определяется контрактом протокола, затем — общей архитектурой решения. А уже дальше идут подробности реализации. Собственно, спор-то и идёт о том, то ли выставлять наружу 1 ресурс, схема которого сложным образом зависит от погоды на Венере, то ли выставлять наружу N ресурсов с предсказуемыми схемами.

КЛ>Google Cloud так и делает если что

Не знаком с Google Cloud, поэтому не могу осмысленно обсуждать его поведение. Если приведёте пример API-запроса, и пример клиента этого API, который этот запрос отправляет и его результатами пользуется, можно будет что-нибудь конструктивно обсудить.

КЛ>домыслы

А то. Основанные на многолетнем опыте проектирования, изготовления, и использования различных сервисов

КЛ>дорогостоящий саппорт? дороже разработки?

Естественно. Потому что разработка делается 1 раз, а саппорт — всю жизнь. Вы себе представляете себестоимость 1 инцидента в саппорте?

КЛ>ваши роли в субд и есть часть пермиссий через субд

Какая-то путаница. У меня нет никаких "ролей в СУБД".

КЛ>а что тогда надо использовать в твоем случае?

Запросы.
Пишем
  IQueryable<OutboundInvoice> InvoicesSentBy(CompanyID currentCompany) =>  
    from i in db.OutboundInvoices where i.originator = currentCompany select i;
  IQueryable<InboundInvoice> InvoicesSentTo(CompanyID currentCompany) =>
    from i in db.InboundInvoices where i.destination = currentCompany select i;

Всё. Поверх этого в контроллере проверяем, что у текущего пользователя есть пермиссия на доступ к InboundInvoices/OutboundInvoices. Всё.

КЛ>даже не буду комментировать, причем тут несколько сервисов и их взаимодействие я не понимаю, это вне контекста нашего обсуждения

Это очень странно. Топик называется "помогите спроектировать микросервисное приложение". Как мы ухитрились выкинуть из контекста API для взаимодействия между микросервисами?
И что тогда вообще осталось в контексте?

КЛ>кто так вообще делает? выглядит как полная дичь если честно

Я не знаю. Но если вы вдруг захотите применить Row Level Security из любой СУБД, в которой она есть, вам так и придётся делать.

КЛ>>>select * from rows r

КЛ>>>join rows_perms p on r.id = p.r_id
КЛ>>> where p.perm = 'row_read' and p.user_id = :userid
S>>Прекрасно. Вы себе представляете производительность такого решения? Когда у нас табличка на пару миллионов строк, и есть тысяча пользователей с частично пересекающимися правами на эти строки?

КЛ>да, индекс на rows_perms.user_id и будет быстро,

Ну ок.

КЛ>плюс там еще в 99 процентах будет предикат на индексированное поле как часть фильтра поискового запроса.

И это всё ещё будет медленнее, чем прямой поиск по предикату, который применяется к единственной таблице (см. пример кода с Invoices выше).
КЛ>да, часто данные для всех лежать в одной таблице и когда нужен row-level security у тебя просто нет выбора. либо копии данных (твои views видимо) либо так.
Эмм, views никаких "копий данных" не создают. Если хотите — могу рассказать, как они работают.

Да, если вам некомпететный продакт навязал вот эту вот модельку "кому угодно можно выдавать доступ на что угодно", то выбора действительно нет. Ну, точнее, даже там бывают возможности поиграть с оптимизацией, но не очень большие. Тут вопросов нет. Вопрос как раз в том, как проектировать API, чтобы вот таких вот необходимостей не возникало.
КЛ>но это на запрос коллекции, а на запрос одного ресурса вообще все быстро потому что индекс и на rows_perms.id


КЛ>первый пример довольно универсален, он иллюстрирует подход "на основании токена фильтруем резалтсет и тп"

Нет конечно. В первом примере нет решительно никакой возможности управлять видимостью отдельных атрибутов на уровне отдельных объектов.
КЛ>а в твоем случае как? у тебя же схема прибита гвоздями
Во-первых, в моём случае этого не надо, т.к. я против самой идеи разделять доступ "по-объектно". Это ухудшает сразу примерно всё: и эффективность, и безопасность, и удобство использования.
Во-вторых, если даже нам такое припёрло, у меня состав заказанных полей известен в момент получения запроса, и мне не надо пытаться "доставать всё, для чего довольно пермиссий", с риском достать что-то не нужное или неожиданное.

КЛ>как тут помогают роутеры?

Кто такие роутеры и почему они должны мне помогать?

КЛ>да ну я тебе сделаю отдельный endpoint, но внутри будет один движок по доставке данных, который может тебе отдавать 403 в когда надо

Ну так и слава байту. С чем спорим-то тогда?

КЛ>мы потешаемся не над ним, а над тем, что ты полагаешься только на него

Непонятно, что значит "только".

КЛ>на комбинаторный взрыв сущностей

Комбинаторный взрыв сущностей у нас случился в тот момент, когда кто-то решил разрешать доступ к различным атрибутам ресурса при помощи независимых пермиссий. Дальше вопрос только в том, признаём мы его или нет, и что мы с ним делаем.
Когда у нас состав атрибутов явно заказывается в урле, нам очень легко такое тестировать:
1. делаем запрос к ?$include(attribute1) с токеном без пермиссии на attribute1, убеждаемся, что получаем 403
2. делаем запрос к ?$include(attribute1) с токеном с пермиссией на attribute1, убеждаемся, что получаем 200.
3. делаем запрос к ?$include(attribute1) с токеном с полными пермиссиями, убеждаемся, что получаем 200 и в ответе нет никаких лишних атрибутов.
4. Повторяем так для всех атрибутов, доступ к которым контролируется пермиссиями.
третье — факультативно: это если мы не верим в то, что код контроллера факторизован на предмет запрошенных атрибутов. Факторизацию можно отдельно проверять юнит-тестами.
Это гораздо надёжнее, чем пытаться перебирать все комбинации пермиссий и смотреть, какой получится результат.

КЛ>сколько раз мы уже это слышали по поводу всего. похоже на отмазку для неадекватного задаче решения

Как скажете.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[24]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.04.25 05:46
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>не понял

А что тут понимать? Когда я делаю запрос, я ожидаю получить результат типа
{
  id: number;
  senderId: string;
  senderName: string;
  amount: number;
  expiresAt?: Date;
}[]

Если мне вдруг приезжает массив, где у каких-то объектов нет senderName, то это нарушает приведённую здесь спецификацию типа.
Можно, конечно, принудительно приписать вопросики ко всем свойствам этого интерфейса, и написать обработку в приложении на каждый такой случай, но это, скажем так, непрактично.
А вот если мне приезжает массив, в котором не 100, а 10, или 0 элементов, инвариант сохранён. Я в любом случае не ожидаю, что мне вернётся ровно 100 элементов.

КЛ>ну у нас так же, в чем поинт?

Не знаю, как "у вас", а у исходного оппонента, судя по всему, в JSON будет список не ссылок, а объектов, причём состав объектов произвольно зависит не от самих объектов или аргументов запроса, а от наличия в токене тех или иных пермиссий.

КЛ>смешно

А вы всегда проектируете безопасность на основании описаний вроде "у нас есть пять типов отчётов, один из них внутренний, другой внешний". Вам сразу всё понятно, и можно бежать нарезать таблички в базе?
Я раньше и сам выяснял подробности проекта на основании телепатии и ясновидения, но лет 15 тому мне их за неуплату отключили, поэтому я предпочитаю проектировать на основании установленных фактов, а также вероятных будущих изменений.

КЛ>да не, по сути он дает тебе query language к базе

По сути там вообще может не быть никакой базы.

КЛ>отвечай!

Зависимость от контекста называется "модальностью" и является одним из признаков плохого проектирования. Такой зависимости нужно по возможности избегать.
Если уж никак-никак от неё избавиться не удалось, нужно факторизовывать пространство состояний для этого контекста. И следить за тем, чтобы факторизация была корректной, т.е. изоляция между ортогональными классами состояний должна сохраняться.
Успешная факторизация позволяет нам перейти от K*M тестов к K+M тестам. Всё.

КЛ>ну начинается! отлыниваешь )

Если вы хотите конструктивного обсуждения, можно попытаться взять какую-нибудь практическую задачу и посмотреть, как её решать в стиле REST — как с помощью микросервисов, так и без оных.
Можно попытаться понять, ради чего вообще может в голову прийти "выставлять наружу псевдо-бд", и как это делать правильно, и чем это отличается от неправильного решения.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[31]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 18.04.25 09:29
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Константин Л., Вы писали:


КЛ>>то есть пермисии определяются декларативно просто наличием полей в типе? ну-ну

S>Не уверен, что я понял ваш вопрос, но скорее всего ответ "нет".
КЛ>>"хорошо это когда хорошо".
S>А то.
КЛ>>тогда _какую_ проблему решают твои виртуальные ресурсы? никакую
S>Конечно. Они не создают проблему. А решают они задачу — задачу ограничения доступа к определённым атрибутам для определённых групп пользователей.

а теперь читаем выше твой же ответ — "то есть пермисии определяются декларативно просто наличием полей в типе? ну-ну" — "...но скорее всего ответ "нет".
задача ограничения доступа это и есть по сути то, что решают пермиссии. то есть ответ да, просто ты не понимаешь уже что пишешь

КЛ>>серьезные приложения это софт для АЭС, там можно. В 99 процентах других случаев твоя модель не работает, такая моя оценка

S>Софт для АЭС тут ни при чём. А серьёзные приложения — это те, к примеру, которые автоматизируют деятельность, подлежащую регулированию каким-нибудь FinCEN.
S>Когда у вас есть реальная перспектива продолбать крипты суммарной стоимостью под миллиард, рассуждения в стиле "у меня в коде всё было правильно, это тупой админ нечаянно выдал лишнюю пермиссию на один из атрибутов одного из объектов одному из пользователей" не сильно помогают избежать кусания локтей.

ла-ла-ла

КЛ>>при чем здесь вообще апи, когда мы говорим про то, как делать секьюрити внутри?

S>С чего это вдруг?
S>Мы всю дорогу говорили именно про то, как должен выглядеть API снаружи. То, что там внутри, в первую очередь определяется контрактом протокола, затем — общей архитектурой решения. А уже дальше идут подробности реализации. Собственно, спор-то и идёт о том, то ли выставлять наружу 1 ресурс, схема которого сложным образом зависит от погоды на Венере, то ли выставлять наружу N ресурсов с предсказуемыми схемами.

ты сам себе противоречишь. невозможно говорить про апи без того, зачем он такой нужен. мы тут не про форму, а про сочетание ее с сожержанием

КЛ>>домыслы

S>А то. Основанные на многолетнем опыте проектирования, изготовления, и использования различных сервисов

ну, люди много лет умудряются делать всякую фигню, это не показатель.

КЛ>>дорогостоящий саппорт? дороже разработки?

S>Естественно. Потому что разработка делается 1 раз, а саппорт — всю жизнь. Вы себе представляете себестоимость 1 инцидента в саппорте?

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

КЛ>>ваши роли в субд и есть часть пермиссий через субд

S>Какая-то путаница. У меня нет никаких "ролей в СУБД".
ну у тебя там есть юзер, у которого явно есть роль или зачем там была написана вся та бесполезная портянка

КЛ>>а что тогда надо использовать в твоем случае?

S>Запросы.
S>Пишем
S>
S>  IQueryable<OutboundInvoice> InvoicesSentBy(CompanyID currentCompany) =>  
S>    from i in db.OutboundInvoices where i.originator = currentCompany select i;
S>  IQueryable<InboundInvoice> InvoicesSentTo(CompanyID currentCompany) =>
S>    from i in db.InboundInvoices where i.destination = currentCompany select i;
S>

S>Всё. Поверх этого в контроллере проверяем, что у текущего пользователя есть пермиссия на доступ к InboundInvoices/OutboundInvoices. Всё.

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

это читерство. у тебя одна таблица, а не две. зачем сюда писать какой-то детский сад?

КЛ>>даже не буду комментировать, причем тут несколько сервисов и их взаимодействие я не понимаю, это вне контекста нашего обсуждения

S>Это очень странно. Топик называется "помогите спроектировать микросервисное приложение". Как мы ухитрились выкинуть из контекста API для взаимодействия между микросервисами?
S>И что тогда вообще осталось в контексте?

какая разница как называется топик, когда эта ветка давно уже про rest levels?

КЛ>>кто так вообще делает? выглядит как полная дичь если честно

S>Я не знаю. Но если вы вдруг захотите применить Row Level Security из любой СУБД, в которой она есть, вам так и придётся делать.

у тебя windows головного мозга

КЛ>>>>select * from rows r

КЛ>>>>join rows_perms p on r.id = p.r_id
КЛ>>>> where p.perm = 'row_read' and p.user_id = :userid
S>>>Прекрасно. Вы себе представляете производительность такого решения? Когда у нас табличка на пару миллионов строк, и есть тысяча пользователей с частично пересекающимися правами на эти строки?

КЛ>>да, индекс на rows_perms.user_id и будет быстро,

S> Ну ок.

КЛ>>плюс там еще в 99 процентах будет предикат на индексированное поле как часть фильтра поискового запроса.

S>И это всё ещё будет медленнее, чем прямой поиск по предикату, который применяется к единственной таблице (см. пример кода с Invoices выше).

да да, а твои проверки row-level секьюрити в контроллере стоят ноль да? и ты поташищь весь резалтсет для всех юзеров из базы да?
и пейджинг от этого у тебя не будет работать

КЛ>>да, часто данные для всех лежать в одной таблице и когда нужен row-level security у тебя просто нет выбора. либо копии данных (твои views видимо) либо так.

S>Эмм, views никаких "копий данных" не создают. Если хотите — могу рассказать, как они работают.

да ты тут уже нарассказывал

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


ну спроектируй тут, только без хаков с db.OutboundInvoices, db.InboundInvoices — одна таблица, row-level security и тп. я тебе набросал _примерную_ схему, ты
же мне ответил "недостаточно вводных данных".

КЛ>>но это на запрос коллекции, а на запрос одного ресурса вообще все быстро потому что индекс и на rows_perms.id

S>

ну смейся

КЛ>>первый пример довольно универсален, он иллюстрирует подход "на основании токена фильтруем резалтсет и тп"

S>Нет конечно. В первом примере нет решительно никакой возможности управлять видимостью отдельных атрибутов на уровне отдельных объектов.

это уже хоть что-то для иллюстрации. у тебя же нет вообще ничего

КЛ>>а в твоем случае как? у тебя же схема прибита гвоздями

S>Во-первых, в моём случае этого не надо, т.к. я против самой идеи разделять доступ "по-объектно". Это ухудшает сразу примерно всё: и эффективность, и безопасность, и удобство использования.

а, ну да. теперь понятно какие ты там приложения годами делаешь

S>Во-вторых, если даже нам такое припёрло, у меня состав заказанных полей известен в момент получения запроса, и мне не надо пытаться "доставать всё, для чего довольно пермиссий", с риском достать что-то не нужное или неожиданное.


какая чушь

S>Это гораздо надёжнее, чем пытаться перебирать все комбинации пермиссий и смотреть, какой получится результат.


это все слова, которые ты не готов обратить в код

КЛ>>сколько раз мы уже это слышали по поводу всего. похоже на отмазку для неадекватного задаче решения

S>Как скажете.

был гораздо лучшего мнения о тебе
Re[25]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 18.04.25 09:47
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, Константин Л., Вы писали:


S>Если мне вдруг приезжает массив, где у каких-то объектов нет senderName, то это нарушает приведённую здесь спецификацию типа.


охо-хо, ну-ну. спецификацию типа

S>Можно, конечно, принудительно приписать вопросики ко всем свойствам этого интерфейса, и написать обработку в приложении на каждый такой случай, но это, скажем так, непрактично.


это, скажем так, реальность

S>А вот если мне приезжает массив, в котором не 100, а 10, или 0 элементов, инвариант сохранён. Я в любом случае не ожидаю, что мне вернётся ровно 100 элементов.


какая-то чушь

КЛ>>ну у нас так же, в чем поинт?

S>Не знаю, как "у вас", а у исходного оппонента, судя по всему, в JSON будет список не ссылок, а объектов, причём состав объектов произвольно зависит не от самих объектов или аргументов запроса, а от наличия в токене тех или иных пермиссий.

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

кому нужен пустой список только с ссылками? на вебе его не покажешь. будут легкие объекты и ссылки если надо взять конкретный полный.

КЛ>>смешно

S>А вы всегда проектируете безопасность на основании описаний вроде "у нас есть пять типов отчётов, один из них внутренний, другой внешний". Вам сразу всё понятно, и можно бежать нарезать таблички в базе?

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

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


какой-то бред

КЛ>>да не, по сути он дает тебе query language к базе

S>По сути там вообще может не быть никакой базы.

а по факту в 99 случаев она там есть

КЛ>>отвечай!

S>Зависимость от контекста называется "модальностью" и является одним из признаков плохого проектирования. Такой зависимости нужно по возможности избегать.

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

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


мне кажется я начинаю понимать, откуда такой казеный язык и нажим на систему типов — многолетнее преподавание и .net?

S>Успешная факторизация позволяет нам перейти от K*M тестов к K+M тестам. Всё.


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

КЛ>>ну начинается! отлыниваешь )

S>Если вы хотите конструктивного обсуждения, можно попытаться взять какую-нибудь практическую задачу и посмотреть, как её решать в стиле REST — как с помощью микросервисов, так и без оных.


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


S>Можно попытаться понять, ради чего вообще может в голову прийти "выставлять наружу псевдо-бд", и как это делать правильно, и чем это отличается от неправильного решения.



да мне это все неинтересно, потмому что в 99 случаев я против выставления хоть правильно, хоть неправильно. да и в "правильно" я не верю
jfyi
Re[11]: Каких программ вам не хватает?
От: Miroff Россия  
Дата: 18.04.25 11:38
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>PUT без параметров?


С параметрами, которые GET вернул. Структура ресурса не должна зависеть от метода

КЛ>а если ссылки поменялись между GET и PUT? "и вообще конкурентные обновления" — какие ужасы ты пишешь


А ты дизайни систему так, чтобы ссылки не менялись между GET и PUT. Идемпотентность, версии объекта и все дела. Ссылки не отменяют, а дополняют коды ошибок.
Re[25]: Каких программ вам не хватает?
От: Miroff Россия  
Дата: 18.04.25 11:47
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Можно, конечно, принудительно приписать вопросики ко всем свойствам этого интерфейса, и написать обработку в приложении на каждый такой случай, но это, скажем так, непрактично.


Вполне практично, в Swagger спеке ты так или иначе должен для каждого поля указать обязательное оно или опциональное. И, соответственно, в имплементации обработать все случаи. У тебя не может быть такой ситуации, когда поле обязательное, а сервер его по какой-то причине не отдает. По-хорошему, у тебя даже код не должен скомпилироваться.
Re[12]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 18.04.25 12:34
Оценка:
Здравствуйте, Miroff, Вы писали:

M>Здравствуйте, Константин Л., Вы писали:


КЛ>>PUT без параметров?


M>С параметрами, которые GET вернул. Структура ресурса не должна зависеть от метода


контракт на базе возвращаемых полей для конкретного инстанса ресурсы, а не на основе сваггера?
это ты какой-то очень простой crud описываешь. а что если мне надо заполнить пустое поле, которого нет в GET? или ты всегда
null возвращаешь для всех полей? допустим

КЛ>>а если ссылки поменялись между GET и PUT? "и вообще конкурентные обновления" — какие ужасы ты пишешь


M>А ты дизайни систему так, чтобы ссылки не менялись между GET и PUT. Идемпотентность, версии объекта и все дела. Ссылки не отменяют, а дополняют коды ошибок.


ты пишешь "Правильно делать GET перед PUT, потому что ссылки могут измениться, и вообще конкурентные обновления. ".
так вот между GET и PUT ты залил на сервер новую версию, клиент при этом не в курсе и держит у себя в текущем состоянии твой старый PUT.
Твои действия? меняешь версию апи в url и PUT идет на старый url?

"вообще конкурентные обновления" — что это значит в контексте ссылок, раскрой тему
Отредактировано 18.04.2025 12:40 Кt Л. . Предыдущая версия .
Re[6]: Помогите правильно спроектировать микросервисное приложение
От: Ziaw Россия  
Дата: 18.04.25 12:38
Оценка:
Здравствуйте, gandjustas, Вы писали:

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


У Линуса получилось. Ядро линукса это монолит. Лично я считаю, что МСА возникла от нехватки времени на тщательное проектирование. В текущих бизнес процессах никто не хочет давать время на проектирование и перепроектирование кода и процессов в командах. Поскольку это слабо предсказуемые и сложно планируемые активности, а ЛПР хотят понимать, что происходит и не готовы просто доверять технарям по вполне понятным человеческим причинам. Ну или это очень одаренные ЛПР.

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

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

Например есть опердень на котором работает более тысячи организаций и несколько сотен тысяч человек (не менее сотни тысяч в день). За тысячу форм, процессов и отчетов, интеграции с десятками других систем. И этот монолит способен развиваться и поддерживаться командой в составе которой один программист, годовой бюджет которой единицы миллионов для заказчика. Коллеги, как думаете, насколько счастлив заказчик, что для него это все настолько дешево? Про счастье этого незаменимого программиста я тоже боюсь писать.
Re[7]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.04.25 13:01
Оценка:
Здравствуйте, Ziaw, Вы писали:

Z>Здравствуйте, gandjustas, Вы писали:


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


Z>У Линуса получилось. Ядро линукса это монолит.

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

Z>Лично я считаю, что МСА возникла от нехватки времени на тщательное проектирование. В текущих бизнес процессах никто не хочет давать время на проектирование и перепроектирование кода и процессов в командах. Поскольку это слабо предсказуемые и сложно планируемые активности, а ЛПР хотят понимать, что происходит и не готовы просто доверять технарям по вполне понятным человеческим причинам. Ну или это очень одаренные ЛПР.

Много причин использования МСА, далеко не все они оправданы с точки зрения экономики.
Re[32]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.04.25 13:04
Оценка:
Здравствуйте, Константин Л., Вы писали:


КЛ>задача ограничения доступа это и есть по сути то, что решают пермиссии. то есть ответ да, просто ты не понимаешь уже что пишешь

Нет, но я не совсем понимаю, что мне пишете вы. Да, теперь понятно — ответ "да".

КЛ>ла-ла-ла



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

Ну так мы про это и говорим, а вы вдруг начинаете "при чём тут АПИ"

КЛ>ну, люди много лет умудряются делать всякую фигню, это не показатель.

Отож.

КЛ>всю жизнь? да твой код будет переписан через пару лет. сколько стоит индийский саппорт? представляю. но не надо мне зубы заговаривать — тейк про баги

Не было никакого тейка про баги. Я, наверное, плохо объяснил сценарий.
Вот у нас есть какая-нибудь экзотическая пермиссия на атрибут "НДС". Зачем? Да низачем. Просто пришёл в своё время суперуверенный в себе архитект, и набросал "универсальное RBAC-решение на все времена".
На практике следуюшие 10 лет никто не задурялся настройкой этой пермиссии, потому что нафиг никому не упёрлись инвойсы без атрибута НДС. Нет такого бизнес-сценария.
Внешние разработчики, которые писали интеграцию с этим АПИ, всегда видели этот атрибут в JSON. Да и в OpenAPI спецификации этот атрибут приведён. Где-то, в какой-то доке, наверное, кто-то упомянул, что этот атрибут требует пермиссию Invoice.VAT.Read для конкретного инвойса. Но поскольку доки никто не читает, а в тестах интеграция работало в 100% случаев, этот сторонний сервис выкачен в эксплуатацию.
И вот ещё через несколько лет какой-нибудь шибко активный админ решает поубирать у какого-нибудь пользователя "лишние" пермиссии на какие-то отдельные объекты.
Пользователь выполняет свой привычный workflow, и внезапно обнаруживает 500 internal error. Потому что сервису вдруг попался экземпляр инвойса с атрибутом НДС=null, а на это никто не рассчитывал.

КЛ>ну у тебя там есть юзер, у которого явно есть роль или зачем там была написана вся та бесполезная портянка

Я вроде три раза объяснил, в четвёртый уже не буду.

КЛ>в контроллере? ты серьезно? вот это ты гений проектирования безопасных приложений — поля отрезаем декларативно через поля в объекте (твой факторинг), пермиссии проверяем в контроллерах (кстати как?). такое где-то работает да — в очень простых системах.

Ну, научите же меня, как всё это правильно делать. А то по моей практике, простые решения — они самые надёжные.

КЛ>это читерство. у тебя одна таблица, а не две. зачем сюда писать какой-то детский сад?

Да, у меня одна таблица. Читерство-то в чём? Детский сад — это ваша манера общаться. Я что-то начал от неё уставать.

КЛ>какая разница как называется топик, когда эта ветка давно уже про rest levels?

Прекрасно.

КЛ>у тебя windows головного мозга

Windows тут ни при чём. Row-level security в каком-нибудь Постгрес устроены ровно так же.
Пример, который я привёл, про этот сайт — поэтому упомянут Windows и SQL Server.

КЛ>да да, а твои проверки row-level секьюрити в контроллере стоят ноль да? и ты поташищь весь резалтсет для всех юзеров из базы да?

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

КЛ>и пейджинг от этого у тебя не будет работать

Прекрасно он будет работать.

КЛ>да ты тут уже нарассказывал

Я как-то не рассчитывал встретить в архитектурной дискуссии незнание основ RDBMS. Поэтому надеюсь на то, что я просто вас не так понял. Но, на всякий случай выражаю готовность поделиться всем, что знаю сам.

КЛ>ну спроектируй тут, только без хаков с db.OutboundInvoices, db.InboundInvoices — одна таблица, row-level security и тп.

Непонятно, что такое "хаки".

КЛ>я тебе набросал _примерную_ схему, ты

КЛ>же мне ответил "недостаточно вводных данных".
Вы жульничаете. Схему вы набросали про фильтрацию строк, а задачу, про которую я ответил "недостаточно данных" — про какую-то (непонятно какую) обрезку атрибутов.
И ни в одном из случаев нет ничего про постановку задачи. Выглядит так, что вы стараетесь сначала придумать решение, а уже потом любой ценой его защитить, вообще не задумываясь, а зачем такое решение нужно.

КЛ>ну смейся

А то.



КЛ>какая чушь

Не, беру свои слова по поводу "поделиться всем, чем знаю" обратно.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[13]: Каких программ вам не хватает?
От: Miroff Россия  
Дата: 18.04.25 14:03
Оценка: +1
Здравствуйте, Константин Л., Вы писали:

КЛ>это ты какой-то очень простой crud описываешь


Именно! В этом и смысл REST, выражать сложную логику через очень простой CRUD
Re[8]: Помогите правильно спроектировать микросервисное приложение
От: Ziaw Россия  
Дата: 18.04.25 14:45
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Угу, модульный монолит. Лучшая архитектура по балансу изоляции и накладных расходов. Но ядро линукса очень стабильно и цикл разработки у него не так, как у бизнес-приложений


Про лучший вариант архитектуры соглашусь. Если взять что-то сравнимое по сложности, типа Яндекс 360, то релизный цикл похожий. Более того, подозреваю, что именно микросервисная архитектура этого изделия сделала невозможной его доводку до приличного состояния. То же можно сказать и про интерфейс яндекс облака, там уши всяких узкосервисных проблем торчат регулярно, но там хотя бы бабло идет. Интересно, есть тут кто-то близкий к этим командам Яндекса, чтобы прокомментировать.

G>Много причин использования МСА, далеко не все они оправданы с точки зрения экономики.


Я могу назвать только одну — борьба со сложностью, но, имхо, это фикция. На уровне конечного продукта сложность возрастает. Иногда мне кажется, что на это всем пофиг, главное работой обеспечены.
Re[14]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 18.04.25 15:04
Оценка:
Здравствуйте, Miroff, Вы писали:

M>Здравствуйте, Константин Л., Вы писали:


КЛ>>это ты какой-то очень простой crud описываешь


M>Именно! В этом и смысл REST, выражать сложную логику через очень простой CRUD


это очень упрощенный взгляд на вещи
Re[33]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 18.04.25 15:27
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Нет, но я не совсем понимаю, что мне пишете вы. Да, теперь понятно — ответ "да".


ну так ведь это же даже не секьюрити а костыль.

S>Не было никакого тейка про баги. Я, наверное, плохо объяснил сценарий.

[]
S>Пользователь выполняет свой привычный workflow, и внезапно обнаруживает 500 internal error. Потому что сервису вдруг попался экземпляр инвойса с атрибутом НДС=null, а на это никто не рассчитывал.

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

КЛ>>ну у тебя там есть юзер, у которого явно есть роль или зачем там была написана вся та бесполезная портянка

S>Я вроде три раза объяснил, в четвёртый уже не буду.

я реально не понял, видимо потмоу что мне в голову такое не приходило делать

S>Ну, научите же меня, как всё это правильно делать. А то по моей практике, простые решения — они самые надёжные.


простые — да. но мир сложен. учим вот но ты ни в какую)

КЛ>>это читерство. у тебя одна таблица, а не две. зачем сюда писать какой-то детский сад?

S>Да, у меня одна таблица. Читерство-то в чём? Детский сад — это ваша манера общаться. Я что-то начал от неё уставать.

я тоже, сори

КЛ>>у тебя windows головного мозга

S>Windows тут ни при чём. Row-level security в каком-нибудь Постгрес устроены ровно так же.
S>Пример, который я привёл, про этот сайт — поэтому упомянут Windows и SQL Server.

какой сайт? rsdn? ужас.

Вы писали "Это когда мы заводим для пользователя №44487 виндовый аккаунт, даём этому аккаунту права на SQL базу с табличками forums, topics, replies, и marks, при логине на сайт создаём виндовую сессию, и все запросы в SQL Server выполняем через новое соединение под правами этого виндового аккаунта."

в постгрес так никто делать не будет в здравом уме

КЛ>>да да, а твои проверки row-level секьюрити в контроллере стоят ноль да? и ты поташищь весь резалтсет для всех юзеров из базы да?

S>Вот зачем вы пишете бред. Я же показал вам тремя абзацами выше код запросов — где там "весь резалтсет для всех юзеров из базы"?

а где там фильтрация по юзеру? Это:

 IQueryable<OutboundInvoice> InvoicesSentBy(CompanyID currentCompany) =>  
    from i in db.OutboundInvoices where i.originator = currentCompany select i;
  IQueryable<InboundInvoice> InvoicesSentTo(CompanyID currentCompany) =>
    from i in db.InboundInvoices where i.destination = currentCompany select i;


КЛ>>да ты тут уже нарассказывал

S>Я как-то не рассчитывал встретить в архитектурной дискуссии незнание основ RDBMS. Поэтому надеюсь на то, что я просто вас не так понял. Но, на всякий случай выражаю готовность поделиться всем, что знаю сам.

ты про то, что я написал views вместо materialized views? жестко

КЛ>>ну спроектируй тут, только без хаков с db.OutboundInvoices, db.InboundInvoices — одна таблица, row-level security и тп.

S>Непонятно, что такое "хаки".

разбиение на 2 таблицы, потому что по коду неочевидно, что у тебя одна таблица

КЛ>>я тебе набросал _примерную_ схему, ты

КЛ>>же мне ответил "недостаточно вводных данных".
S>Вы жульничаете. Схему вы набросали про фильтрацию строк, а задачу, про которую я ответил "недостаточно данных" — про какую-то (непонятно какую) обрезку атрибутов.
S>И ни в одном из случаев нет ничего про постановку задачи. Выглядит так, что вы стараетесь сначала придумать решение, а уже потом любой ценой его защитить, вообще не задумываясь, а зачем такое решение нужно.

да я просто рассказываю что я видел в своей практике

КЛ>>какая чушь

S>Не, беру свои слова по поводу "поделиться всем, чем знаю" обратно.

"Вот зачем вы пишете бред." — твои слова выше. Но согласен, я мог бы и помягче, сори
Отредактировано 18.04.2025 15:31 Кt Л. . Предыдущая версия .
Re[34]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.04.25 16:12
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>ну так ведь это же даже не секьюрити а костыль.

Хотелось бы более внятных обоснований такого утверждения.

КЛ>вы ошибку использования переносите на ошибку проектирования.

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

КЛ>простые — да. но мир сложен. учим вот но ты ни в какую)

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


КЛ>я тоже, сори




КЛ>Вы писали "Это когда мы заводим для пользователя №44487 виндовый аккаунт, даём этому аккаунту права на SQL базу с табличками forums, topics, replies, и marks, при логине на сайт создаём виндовую сессию, и все запросы в SQL Server выполняем через новое соединение под правами этого виндового аккаунта."

КЛ>в постгрес так никто делать не будет в здравом уме
И нигде не будет. Этот подход придуман в SQL-92, расширен в SQL-99, и примерно тогда же устарел навсегда.
Я приводил его как пример применения принципа, который был уместен в клиент-серверных приложениях эпохи девяностых.
Сейчас технологическая основа изменилась, а принципы — нет.
Вместо "таблицы" у нас "микроссервис с сырыми данными", вместо "хранимой процедуры" или "представления" у нас "микросервис фасада", а в остальном всё так же и есть.

Оппонент предлагает таскать токен внешнего пользователя сквозь всю иерархию сервисов — типа я обращаюсь к микросервису "invoices", а тот, в свою очередь, бежит за подробностями отправителя инвойса к микросервису "counteragents", отдавая туда мой же токен. И вот тут-то у нас и возникает чудо голодания: микросервис "counteragents" возвращает ему 403/404, если у меня нет привилегии смотреть запрошенного контрагента. И, соответственно, фасадный микросервис отдаёт мне инвойс без атрибута sender.

Я пытаюсь объяснить, что такое решение не вполне удачно.

КЛ>а где там фильтрация по юзеру? Это:


КЛ>
КЛ> IQueryable<OutboundInvoice> InvoicesSentBy(CompanyID currentCompany) =>  
КЛ>    from i in db.OutboundInvoices where i.originator = currentCompany select i;
КЛ>  IQueryable<InboundInvoice> InvoicesSentTo(CompanyID currentCompany) =>
КЛ>    from i in db.InboundInvoices where i.destination = currentCompany select i;
КЛ>

Да. В исходной задаче не было никакого требования "разделять инвойсы по конкретным пользователям". В 99% случаев такого бизнес-сценария вовсе нет.
Например, на сайте RSDN нет никаких ручек для выдачи произвольным пользователям произвольных пермиссий на конкретные сообщения. И ничего, за 25 лет никто не умер от этого.
Так и тут — мы провели анализ предметной области, выяснили сценарии пользователей, и обнаружили, что нужно всего два набора пермиссий — на "инвойсы, выставленные компанией, где пользователь работает", и на "инвойсы, полученные компанией, где пользователь работает". (Это ещё сложный случай, сервис для B2B взаимодействия. Если мы пилим сервис P2P, то там модель безопасности будет ещё проще).
Откуда взялись эти наборы пермиссий? Да из того, что в исследованных нами компаниях-клиентах встречаются следующие случаи:
1. One person does all accounting
2. One department does all accounting
3. There are accounts payable and accounts receivable departments
Случаев "право оплатить конкретный один инвойс нам нужно назначить одному конкретному сотруднику" мы обнаружить не смогли, как ни старались.
Тем более у нас не нашлось случаев "надо выдать доступ к каким-то инвойсам, выставленным компанией X компании Y, сотруднику какой-то посторонней компании Z"
Поэтому мы делаем так, как описано в коде выше.
И это обеспечивает одновременно высокую безопасность (нет возможности напороть с настройками при эксплуатации), высокую производительность (все поисковые запросы можно ускорить путём построения подходящих индексов), и высокое удобство пользования.

На всякий случай, если непонятно: вот у нас сотрудник компании X (с соответствующими правами) выставляет инвойс компании Y.
Этот инвойс виден всем уполномоченным сотрудникам компании X, а особо уполномоченные могут даже его отзывать и редактировать.
Но с момента, когда он переведён в статус "отправлен получателю", он должен стать виден всем уполномоченным сотрудникам компании Y.

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

КЛ>ты про то, что я написал views вместо materialized views? жестко

Я вообще не понимаю, откуда в рассуждениях взялись materialized views.

КЛ>разбиение на 2 таблицы, потому что по коду неочевидно, что у тебя одна таблица

Ну хорошо, давайте поговорим об этом подробнее.
public class PaypalDb : LinqToDB.Data.DataConnection
{
  public PaypalDb() : base("everything") { }

  public ITable<OutboundInvoice>  OutboundInvoices => this.GetTable<OutboundInvoice>();
  public IQueryable<InboundInvoice> Category => from oi in this.OutboundInvoices select Mapper<OutboundInvoice, InboundInvoice>.DefaultMapper.Map(oi);
  // ... other tables ...
}

Если бы речь шла не о дотнете, а о чём-то более убогом, то я бы, наверное, создал в БД
create view InboundInvoices as
select 
    originator, 
    originatorName, 
    amount, 
    sentDate, 
    status
    expirationDate, 
    destination, 
    destinationName, 
    amount, 
    amountVAT
from Invoices

КЛ>да я просто рассказываю что я видел в своей практике
Отлично.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[14]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 18.04.25 16:22
Оценка:
Здравствуйте, Miroff, Вы писали:

M>Именно! В этом и смысл REST, выражать сложную логику через очень простой CRUD

Именно поэтому у Paypal — не REST.
На основе их API, вы никак не можете "сделать GET" на рефанд, потому что у них рефанд — это POST на магический урл.
И никто вам параметров этого рефанда не подскажет, кроме, собственно, документации.
А если у вас есть документация, то вам нафиг не упала ссылка на рефанд в каждом пейменте. Вы и так поймёте, куда нужно отправить те параметры, которые описаны в документации.

Вообще, первый REST API, в разработке которого я участвовал, был устроен как раз примерно так.
Только мы были ещё более радикальны: в зависимости от того, какое расширение было в конце запрошенного URL (.xml, .json, .html), отдавалось разное представление ресурса.
И вот в HTML представлении в тех местах, откуда можно было делать параметризованные запросы (включая POST), приезжала форма с полями и соответствующим action.
И если у параметров было конечное количество значений, то они там в input type=select указывались.
Так что по API можно было бродить обычным браузером. HATEOASнее уже некуда.
Даже и не помню, какой это был год. Наверное, что-то вроде 2008 или 2009.

Потом оказалось, что нафиг это всё никому не упало.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[9]: Помогите правильно спроектировать микросервисное приложение
От: TG  
Дата: 18.04.25 17:59
Оценка:
Здравствуйте, Ziaw, Вы писали:

G>>Угу, модульный монолит. Лучшая архитектура по балансу изоляции и накладных расходов. Но ядро линукса очень стабильно и цикл разработки у него не так, как у бизнес-приложений

Z>Про лучший вариант архитектуры соглашусь. Если взять что-то сравнимое по сложности, типа Яндекс 360, то релизный цикл похожий. Более того, подозреваю, что именно микросервисная архитектура этого изделия сделала невозможной его доводку до приличного состояния. То же можно сказать и про интерфейс яндекс облака, там уши всяких узкосервисных проблем торчат регулярно, но там хотя бы бабло идет. Интересно, есть тут кто-то близкий к этим командам Яндекса, чтобы прокомментировать.

А зачем сравнивать ядро линукса и современные бизнес-приложения (вернее бизнес-сервисы)? У них же разная, скажем так, архитектурная топология. Бизнес-сервисы это уже распределенные системы как минимум по причине наличия БД.
Re[35]: Каких программ вам не хватает?
От: Константин Л.  
Дата: 18.04.25 18:05
Оценка:
Здравствуйте, Sinclair, Вы писали:

[]

я понял, dto/so на роль, вьюха на роль и все. и мы приходим к первоначальному конфликту — такое не работает, когда ролей много, когда нужны fine-grained permission etc. В смысле, не работает только у нас, у тебя — работает!
Re[36]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 19.04.25 04:29
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>я понял, dto/so на роль, вьюха на роль и все. и мы приходим к первоначальному конфликту — такое не работает, когда ролей много, когда нужны fine-grained permission etc. В смысле, не работает только у нас, у тебя — работает!

Вы сначала расскажите, что за задача. А там уже можно обсуждать, какие решения лучше, а какие — хуже.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[28]: Каких программ вам не хватает?
От: Sinclair Россия https://github.com/evilguest/
Дата: 19.04.25 05:02
Оценка:
Здравствуйте, Константин Л., Вы писали:

КЛ>сколько систем ты так сделал и сколько у них юзеров и ролей?

Ну, я участвовал в системах, где у одной "инсталляции" до 1.5 миллиона пользователей. Типичный размер — около 100к.
Ролей — в пределах нескольких десятков. Это прямо максимум-максимум.
Не сказать, чтобы прямо "я сделал" — но наблюдал и участвовал в процессе проектирования изнутри компании.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[10]: Помогите правильно спроектировать микросервисное приложение
От: Ziaw Россия  
Дата: 21.04.25 07:54
Оценка:
Здравствуйте, TG, Вы писали:

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


Где я сравнивал? Я привел пример, что сложная система в виде монолита может существовать и хорошо управляться. Пример хорош тем, что все открыто и изучаемо.

Если у тебя есть поинт, что оно может существовать только потому, что там нет БД или "распределения", то раскрой тему. И тут бы уточнить, что ты имеешь в виду, уж распределеннее линуксов что-то придумать крайне сложно.
Re[9]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 21.04.25 08:05
Оценка:
Здравствуйте, Ziaw, Вы писали:

G>>Много причин использования МСА, далеко не все они оправданы с точки зрения экономики.

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

Простите, не видел чтобы внедрение МСА сложность уменьшало. Сложность системы обычно кратно возрастает после такого, а небольшой бонус в виде независимого деплоя и ускорения сборки отдельных компонент зачастую не сможет перекрыть увеличение сложности системы и проблем для конечных пользователей
Re[10]: Помогите правильно спроектировать микросервисное приложение
От: Ziaw Россия  
Дата: 21.04.25 08:08
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Простите, не видел чтобы внедрение МСА сложность уменьшало. Сложность системы обычно кратно возрастает после такого, а небольшой бонус в виде независимого деплоя и ускорения сборки отдельных компонент зачастую не сможет перекрыть увеличение сложности системы и проблем для конечных пользователей


Конечно прощу, я тоже не видел. Зато видел, как этим успешно мотивировали выбор МСА, ведь монолит сложно поддерживать.
Re[28]: Каких программ вам не хватает?
От: Ziaw Россия  
Дата: 21.04.25 12:00
Оценка:
Здравствуйте, ·, Вы писали:


S>>Ну вот так и не выносим. Оракл является подробностью реализации этого "питонячьего сервиса" и ниоткуда больше не виден. Впрочем, это может быть и не оракл — "питонячьему сервису" с запасом может хватить sqlite.

·>Он виден с т.з. анализа безопасности.

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

Что передаете в БД в качестве субьекта авторизации? id юзера? Набор ролей? Есть ли роли, которые действуют на определенные сущности (например в этой организации юзер админ, а в этой простой гость)?

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

Как поступаете, если в какое-то поле юзер может писать только если это конкретный бизнес процесс со своими правилами, предусловиями и постусловиями? К примеру храним стейт из развесистой стейт-машины и от этого стейта зависит очень многое в плане можем писать/не можем писать. Как например будет выглядеть правило "посты редактируются редактором и корректором, никто, кроме суперадмина не может удалять ссылки и фотографии из опубликованного поста, заархивированный пост нельзя увидеть в ленте никому, но в админке его видят все сотрудники и суперадмин может его вернуть в ленту"?
Re[29]: Каких программ вам не хватает?
От: · Великобритания  
Дата: 21.04.25 12:56
Оценка:
Здравствуйте, Ziaw, Вы писали:

S>>>Ну вот так и не выносим. Оракл является подробностью реализации этого "питонячьего сервиса" и ниоткуда больше не виден. Впрочем, это может быть и не оракл — "питонячьему сервису" с запасом может хватить sqlite.

Z>·>Он виден с т.з. анализа безопасности.
Z>Коллега, я немного запутался в вашей дискуссии, подскажи, я не ошибся, ты действительно топишь за обеспечение секьюрити данных на уровне БД?
Если в том смысле, что используются средства БД типа как встроенные механизмы row level security, аккаунты в самой бд, вьюшки и т.п. (о чём писал Sinclair) — то нет.

Z>Что передаете в БД в качестве субьекта авторизации? id юзера? Набор ролей? Есть ли роли, которые действуют на определенные сущности (например в этой организации юзер админ, а в этой простой гость)?

В бд формируются запросы в зависимости от пермиссий... Т.е. это просто ещё как бы один критерий поиска, как тут Константин Л писал.

Z>На каком языке пишете код правил доступа, сколько страниц текста он занимает, как его храните, ревьюите, тестируете, разворачиваете?

На оснвном ЯП, обычно java, как и любой другой код.

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

Это же несколько независимых правил.
Отдельно есть пермиссии "редактирование поста", "удаление ссылок", "удаление фот", "просмотр архива" и т.п. И отдельно роли: "редактор", "корректор", "админ", "суперадмин"... и маппинг — что какая роль может.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[30]: Каких программ вам не хватает?
От: Ziaw Россия  
Дата: 21.04.25 13:04
Оценка:
Здравствуйте, ·, Вы писали:

·>Если в том смысле, что используются средства БД типа как встроенные механизмы row level security, аккаунты в самой бд, вьюшки и т.п. (о чём писал Sinclair) — то нет


Ок. На языке высокого уровня это примерно понятно как решается.
Re[12]: Помогите правильно спроектировать микросервисное приложение
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.02.26 07:12
Оценка:
Здравствуйте, gandjustas, Вы писали:
S>>Всё верно. MSA не гарантирует безошибочности. MSA гарантирует, что падение не затронет сценарии, в которых микросервис не задействован.
G>Смотря что значит "не затронет". Если сервис А зависит от срвиса Б, а сервис Б содержит ошибку, даже неважно приводящую к падению или нет, то и А не будет корректно работать.
Зато сервис В, который не зависит ни от А, ни от Б, гарантированно продолжит работать.
Воображаемый пример: микросервис аутентификации не зависит вообще ни от кого; кривой апдейт в сервис пользовательских профилей положит почти всю систему КРОМЕ кода, который зависел только от логина. Например — OAUTH с внешними сервисами. Это кажется мелким преимуществом, но в реале это — огромный шаг вперёд по сравнению с хранением профиля и кредов в одной табличке Users под управлением монолита.

G>Падение — не самая страшная часть, даже в проде. Плохо когда: после падения не может подняться, или вместо падения не работает корректно — висит в ожидании или отдает некорректные данные. Причем второе при несинхронной разработке весьма вероятно.

КМК, это не какие-то "новые" проблемы из-за МСА, а все те же проблемы, которые прекрасно работали и в монолите. Висение в ожидании возможно примерно везде, где есть await (то есть везде). Получение некорректных данных — да запросто, если разработчик воткнул какие-нибудь try {return calculatedTax()} catch { return defaultTax }. А если не воткнул, то и в МСА у нас будет fail-fast, а не генерация мусора.

G>Его же не надо "внутри", надо чтобы разделяемое состояние польностью сохранялось и обрабатывалось ACID хранилищем, желательно внешним по отношению к самому приложению. Чтобы приложение можно было безопасно масштабировать и перезапускать.

Ну вот я о том же и говорю. Это не так просто, как кажется

G>Но у этой медали есть и обратная сторона: если у вас разные базы, то сделать джоин в них крайне сложно. Задачи которые в монолите бы решались одной строкой в SQL в MSA начинают требовать тонны кода.

Да, это известная проблема. И она-то и является основным аргументом против микросервисов.

G>Пример реальный: профили пользователей лежат в одном сервисе, данные о туристических маршрутах в другом. Важная часть логики: для несовреннолетних доступна одна часть маршрутов, для соврешеннолетных — другая. Есть еще другие признаки для фильтров: по регионам, интересам итд.

G>Теперь нам надо сделать рассылку, подобрав подходящие маршруты для клиентов.
G>В монолите — один джоин. В MSA — пожалуйста напишите тонну кода.
G>В реальности ситуация была еще хуже. Данные о бронях групп и совершенных путешествиях лежали в третьем сервисе. И конечно же надо было из предложений исключить тех кто уже ездил.
Тут дело, КМК, не в тонне кода — что там такого-то, цикл по пользователям. Скорее, тут время работы — то, что делалось 1 запросом за 15 минут, теперь — миллион обращений к двум сервисам, каждый из которых тратит по две секунды.

G>А в чем собственно преимущество? Ну если в деньгах посчитать.

В убытках от простоя внедрения изменений в сервис Х. Когда мы пилим монолит, то всегда натыкаемся на то, что "Команда Y не успела стабилизировать свой компонент, поэтому релиз задерживаем на неделю". Какой-нибудь баннер "регистрируйтся на наш конвент 1 октября" уже начинает устаревать, а мы всё никак-никак не можем зарелизиться. А зарелизить отдельно баннер нельзя — монолит-с.

G>Но сразу три замечания:

G>1) это цикл внедрения фичи удлиняет. Так как скорости выгоднее делать "вертикальное" деление проекта.
А обычно так и есть. Суперглубокие цепочки бывают редко. Там скорее два слоя: ядро/инфраструктура, и прикладные штуки. И даже если цепочки длинные, то по мере приближения к "корню" фундаментальность нарастает вместе с падением ожидаемой частоты внесения изменений. Поэтому большинство фич глубже двух уровней ничего не требуют. А те, которые требуют, и в монолите бы потребовали матёрого редизайна и адски длинного цикла согласования и объединения изменений. Ну, там, перейти от "чисто рублей" к "мультивалютности". Придётся переделать всё сверху до низу, без вариантов.
G>2) Это противоречит мысли самодостаточности сервиса. "Верхний" не работает без "нижних", а "нижние" не нужны без "верних".
Нет такой идеи, поэтому и противоречить нечему.
G>3) При падении\ошибке в "нижних" "верхние" тоже ломаются.
Главное, что при ошибках в "верхних" не ломаются нижние и соседние. Это всё ещё сильно лучше, чем монолит с "у нас тут новичок добавил компонент, который выжирает всю память, и приходится перезапускать всю машинерию, а у нас время старта 15 минут".

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

G>>>В msa весьма вероятна ситуация, что в принципе невозможно собрать работающее сочетание микросервисов. Я такое на практике видел. Было три микросервиса (А,Б,В) и два процесса(1,2), затрагивающие три сервиса. В один момент было так, что: А/HEAD, Б/HEAD, В/HEAD~1 работал сценарий 1, но не работал 2. А/HEAD, Б/HEAD~1, В/HEAD работал 2, но не работал 1 и при этом же А/HEAD~1, Б/HEAD, В/HEAD также работал 2, но не работал 1.

G>>>И каждый разработчик миросервиса доказывал что "work on my microservice" (современный аналог work on my machine)

S>>Это организационная проблема. В монолите все эти команды делали бы ровно то же самое ровно с тем же результатом.
G>Это в монорепе гораздо сложнее, до уровня "почти невозможно", так как версия всех компонент в монорепе всегда одна. Если вы не балуетесь выносом бизнес-логики во внешние пакеты.
Во-первых, может и балуемся, во-вторых, вы же не ведёте разработку в main. Вот ребята в В залили в монорепу изменение, которое сломало сценарий 2. "У нас юнит-тесты зелёные, а если у кого-то сломался сценарий — значит, они нас неправильно используют". Им дали по пальцам, откатили PR. Ребята из Б залили свой PR — сломался сценарий 1. И ребята из А заливают свой PR с тем же результатом.
Предотвращение мерджа ломающих изменений — это чисто организационная работа. То есть если у нас есть интеграционные тесты со всеми сценариями И запрет мёрджа PR, при котором ломаются такие тесты — то всё будет работать и в монорепо, и в раздельных репозиториях. А если набора интеграционных тестов нет — то и в монорепо у вас окажется ситуация, когда все юнит-тесты зелёные, покрытие кода 99%, а ни один пример из папочки examples запустить не получается.
G>Я делаю более сильное утверждение: MSA пригодна только там, где есть соответствующая оргструктура: много несвязанных команд, каждая из которых работает на процессами, слабо связанными друг с другом. Такое в банках и маркетплейсах такое очень часто встречается, а они составляют основу русского бигтеха.
G>А например в корпоративных системах оно не надо, как при кастомной разработке, так и при разработке тиражируемого продукта.
Склонен согласитья. Сервисная архитектура всё ещё может быть полезной даже и в этих сценариях, но прямо мелкогранулярное дробление — вряд ли.

G>Ну я точно также могу сказать про запись в чужую базу. Оно ведь все равно деплоится все в одно приложение докера\кубера и контейнеры видят друг друга. И все равно разраб сталкивается с тестовой средой, где может "в дикой природе" наблюдать какие еще сервисы работают и с какими хранилищами.

Ну нет, не видят.

G>Так и в MSA изоляция зачастую условная. Да, у каждого сервиса база своя, но сервер БД один. Дорого поднимать несколько экзепляров даже постгреса. И учетные данные одни.

Это всё — фикция. Культ карго. Как и совмещение всех микросервисов поверх одной БД. Недостатки МСА мы получаем, а преимущества — нет.
G>Если разработчикам не прививать дисциплину, то никакая изоляция не поможет.
Ну, дисциплина-дисциплиной, а оргмеры — оргмерами. Наша задача — не приучить разработчиков силой воли воздерживаться от простых и неправильных решений, а сделать правильные решения более простыми и прямолинейными, чем неправильные.

G>Один раз премии лишить за рефлекшн без необходимости и сразу желание пропадет так писать.

Ну, да, code review. Требует, правда, внимательного вычитывания. В TS/JS сплошь и рядом (s as any as { Value: string }).Value.

G>Ну давай честно: есть предел размера команды или предел структуры управления для монолита. С этим никто не спорит.

G>Далее возможно развитие или по пути MSA или по пути "ядро\платформа и прикладная часть".
G>Но до этого предела монолит превосходит MSA по любым параметрам.
+1

S>>Так "micro" это же не про размер команды. А про "площадь поверхности".

G>Не очень понятно в чем она измеряется.
В количестве ендпоинтов. Микросервис — это, грубо говоря, CRUD для одной главной entity type. Может, там есть 20 ендпоинтов для разных способов конструирования и поиска этих entity type, но всё ещё не так, что там и пользователи, и логины, и группы, и профили, и счета, и карты, и маршруты, и бронирования.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[13]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 01.02.26 11:44
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:

S>>>Всё верно. MSA не гарантирует безошибочности. MSA гарантирует, что падение не затронет сценарии, в которых микросервис не задействован.
G>>Смотря что значит "не затронет". Если сервис А зависит от срвиса Б, а сервис Б содержит ошибку, даже неважно приводящую к падению или нет, то и А не будет корректно работать.
S>Зато сервис В, который не зависит ни от А, ни от Б, гарантированно продолжит работать.
Да, я об этом и писал. Если все микро-сервисы могут работать независимо друг от друга, то такая архитектура жизнеспособда.
Но тогда важное условие чтобы транзакционный бизнес-процесс не пересекал границы микросервисов. Как только начинает пересекать — появляются зависимости и одно без другого уже не работает.
Например для ecommerce деление на "заказ" и "склад" в таком случае не актуально, так как при создании заказа надо на складе все забронировать.
А вот "доставку" от "магазина-склада" вполне легко отделить.



S>Воображаемый пример: микросервис аутентификации не зависит вообще ни от кого; кривой апдейт в сервис пользовательских профилей положит почти всю систему КРОМЕ кода, который зависел только от логина. Например — OAUTH с внешними сервисами. Это кажется мелким преимуществом, но в реале это — огромный шаг вперёд по сравнению с хранением профиля и кредов в одной табличке Users под управлением монолита.

Это ровно до тех пор пока БЛ не начинает опираться на роли пользователей.
Например: сотрудник магазина получает скидку 10% на все покупки в онлайн магазине. Чтобы решить эту задачу сервис "заказа" должен пойти в "auth", чтобы получить роль.
Что касается как сделать аутентификацию качественно — есть ASP.NET Identity, который работает и с OAUTH, и с собственными логинами, и федерацией. И он совсем не микросервисный.
И я пока не видел чтобы кто-то мог превзойти ASP.NET identity по возможностям, удобству использования и быстродействии.

G>>Падение — не самая страшная часть, даже в проде. Плохо когда: после падения не может подняться, или вместо падения не работает корректно — висит в ожидании или отдает некорректные данные. Причем второе при несинхронной разработке весьма вероятно.

S>КМК, это не какие-то "новые" проблемы из-за МСА, а все те же проблемы, которые прекрасно работали и в монолите. Висение в ожидании возможно примерно везде, где есть await (то есть везде). Получение некорректных данных — да запросто, если разработчик воткнул какие-нибудь try {return calculatedTax()} catch { return defaultTax }.
Вот именно. Чтобы в монолите получить некорректные данные надо что-то специально написать. В МСА надо специально писать чтобы данные были согласованы.

S>А если не воткнул, то и в МСА у нас будет fail-fast, а не генерация мусора.

За счет чего? Если мы делаем синхронный вызов, то у нас надежность сервиса, зависит от надежности другого и в сумме не превышает надежность монолита.
Если мы делаем асинхронную репликацию изменений, то отдаем неверные данные.
Опять приходим к тому, что если граница микросервисов проходит по границе транзакций, то МСА норм работает, а если пересекает, то мы автоматически получаем проблемы.

G>>Но у этой медали есть и обратная сторона: если у вас разные базы, то сделать джоин в них крайне сложно. Задачи которые в монолите бы решались одной строкой в SQL в MSA начинают требовать тонны кода.

S>Да, это известная проблема. И она-то и является основным аргументом против микросервисов.
Основной агрумент против, это то, что МСА сама по себе ничего не дает и очень многое отнимает.
Все проблемы, которые призвана решать МСА, прекрасно решаются и без МСА. Кроме, наверное, проблемы разработки на нескольких языках программирования.
И то, например, сейчас можно в дотнет процессе запускать Python код. Это не IronPython, который в IL компилирует, а это прямая загрузка питонячего рантайма в дотнет, генераторы, которые делают C# АПИ для питонячего кода и прокидывание ссылок на объекты в из дотнета в пайтон.

G>>Пример реальный: профили пользователей лежат в одном сервисе, данные о туристических маршрутах в другом. Важная часть логики: для несовреннолетних доступна одна часть маршрутов, для соврешеннолетных — другая. Есть еще другие признаки для фильтров: по регионам, интересам итд.

G>>Теперь нам надо сделать рассылку, подобрав подходящие маршруты для клиентов.
G>>В монолите — один джоин. В MSA — пожалуйста напишите тонну кода.
G>>В реальности ситуация была еще хуже. Данные о бронях групп и совершенных путешествиях лежали в третьем сервисе. И конечно же надо было из предложений исключить тех кто уже ездил.
S>Тут дело, КМК, не в тонне кода — что там такого-то, цикл по пользователям. Скорее, тут время работы — то, что делалось 1 запросом за 15 минут, теперь — миллион обращений к двум сервисам, каждый из которых тратит по две секунды.
И как это решить? Там отдельный сервис identity, который занимается аутентификацией и хранит профили (раньше кста отдельный сервис user был, но слили), и отдельный сервис БЛ.


G>>А в чем собственно преимущество? Ну если в деньгах посчитать.

S>В убытках от простоя внедрения изменений в сервис Х. Когда мы пилим монолит, то всегда натыкаемся на то, что "Команда Y не успела стабилизировать свой компонент, поэтому релиз задерживаем на неделю". Какой-нибудь баннер "регистрируйтся на наш конвент 1 октября" уже начинает устаревать, а мы всё никак-никак не можем зарелизиться. А зарелизить отдельно баннер нельзя — монолит-с.
Ну камон, фичафлаги же есть.
Даже в микросервисах часто приходится релизить с фичафлагами по тем же причинам.

G>>Но сразу три замечания:

G>>1) это цикл внедрения фичи удлиняет. Так как скорости выгоднее делать "вертикальное" деление проекта.
S>А обычно так и есть. Суперглубокие цепочки бывают редко. Там скорее два слоя: ядро/инфраструктура, и прикладные штуки. И даже если цепочки длинные, то по мере приближения к "корню" фундаментальность нарастает вместе с падением ожидаемой частоты внесения изменений. Поэтому большинство фич глубже двух уровней ничего не требуют. А те, которые требуют, и в монолите бы потребовали матёрого редизайна и адски длинного цикла согласования и объединения изменений. Ну, там, перейти от "чисто рублей" к "мультивалютности". Придётся переделать всё сверху до низу, без вариантов.
G>>2) Это противоречит мысли самодостаточности сервиса. "Верхний" не работает без "нижних", а "нижние" не нужны без "верних".
S>Нет такой идеи, поэтому и противоречить нечему.
Я выше описал почему в МСА микросервис должен быть самодостаточным, иначе проблем от МСА больше, чем преимуществ.

G>>3) При падении\ошибке в "нижних" "верхние" тоже ломаются.

S>Главное, что при ошибках в "верхних" не ломаются нижние и соседние. Это всё ещё сильно лучше, чем монолит с "у нас тут новичок добавил компонент, который выжирает всю память, и приходится перезапускать всю машинерию, а у нас время старта 15 минут".
Звучит так, что недостаток ревью и тестирования пытаются решить не на том уровне.


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

G>>>>В msa весьма вероятна ситуация, что в принципе невозможно собрать работающее сочетание микросервисов. Я такое на практике видел. Было три микросервиса (А,Б,В) и два процесса(1,2), затрагивающие три сервиса. В один момент было так, что: А/HEAD, Б/HEAD, В/HEAD~1 работал сценарий 1, но не работал 2. А/HEAD, Б/HEAD~1, В/HEAD работал 2, но не работал 1 и при этом же А/HEAD~1, Б/HEAD, В/HEAD также работал 2, но не работал 1.

G>>>>И каждый разработчик миросервиса доказывал что "work on my microservice" (современный аналог work on my machine)

S>>>Это организационная проблема. В монолите все эти команды делали бы ровно то же самое ровно с тем же результатом.
G>>Это в монорепе гораздо сложнее, до уровня "почти невозможно", так как версия всех компонент в монорепе всегда одна. Если вы не балуетесь выносом бизнес-логики во внешние пакеты.
S>Во-первых, может и балуемся, во-вторых, вы же не ведёте разработку в main. Вот ребята в В залили в монорепу изменение, которое сломало сценарий 2. "У нас юнит-тесты зелёные, а если у кого-то сломался сценарий — значит, они нас неправильно используют". Им дали по пальцам, откатили PR. Ребята из Б залили свой PR — сломался сценарий 1. И ребята из А заливают свой PR с тем же результатом.
У нас все просто — кто делает ПР тот и чинит. Тупо защита на ветке — не сольешь пока есть ошибки билда (там еще и тесты запускаются, но у нас их мало).

S>Предотвращение мерджа ломающих изменений — это чисто организационная работа. То есть если у нас есть интеграционные тесты со всеми сценариями И запрет мёрджа PR, при котором ломаются такие тесты — то всё будет работать и в монорепо, и в раздельных репозиториях. А если набора интеграционных тестов нет — то и в монорепо у вас окажется ситуация, когда все юнит-тесты зелёные, покрытие кода 99%, а ни один пример из папочки examples запустить не получается.

Я бы сказал что это чисто настройка ci\cd системы, конечно пока все в одной репе происходит.
Как только у вас требуется коммит в несколько реп для реализации функционала — начинаются танцы с бубнами (реальное описание "организационной работы")
Мы сейчас пытаемся решить такую проблему сабмодулями, но у них свои ограничения.

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

G>>А например в корпоративных системах оно не надо, как при кастомной разработке, так и при разработке тиражируемого продукта.
S>Склонен согласитья. Сервисная архитектура всё ещё может быть полезной даже и в этих сценариях, но прямо мелкогранулярное дробление — вряд ли.

G>>Ну я точно также могу сказать про запись в чужую базу. Оно ведь все равно деплоится все в одно приложение докера\кубера и контейнеры видят друг друга. И все равно разраб сталкивается с тестовой средой, где может "в дикой природе" наблюдать какие еще сервисы работают и с какими хранилищами.

S>Ну нет, не видят.

G>>Так и в MSA изоляция зачастую условная. Да, у каждого сервиса база своя, но сервер БД один. Дорого поднимать несколько экзепляров даже постгреса. И учетные данные одни.

S>Это всё — фикция. Культ карго. Как и совмещение всех микросервисов поверх одной БД. Недостатки МСА мы получаем, а преимущества — нет.
G>>Если разработчикам не прививать дисциплину, то никакая изоляция не поможет.
S>Ну, дисциплина-дисциплиной, а оргмеры — оргмерами. Наша задача — не приучить разработчиков силой воли воздерживаться от простых и неправильных решений, а сделать правильные решения более простыми и прямолинейными, чем неправильные.
Тогда нужно отказываться от МСА. Потому что МСА добавляет огромное пространство для неочевидно неправильных решений. В рамках монолита аналогичные решения были бы очевидно неправильными.
Выдача неправильных данных при ошибке одного из сервисов как раз из этой области.

G>>Один раз премии лишить за рефлекшн без необходимости и сразу желание пропадет так писать.

S>Ну, да, code review. Требует, правда, внимательного вычитывания. В TS/JS сплошь и рядом (s as any as { Value: string }).Value.
ИИ? Линтеры? Показательно бить палками тех кто так пишет?
Зачем для решения плохого кода на TS делать МСА?
Re[14]: Помогите правильно спроектировать микросервисное приложение
От: Sinclair Россия https://github.com/evilguest/
Дата: 01.02.26 13:16
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Да, я об этом и писал. Если все микро-сервисы могут работать независимо друг от друга, то такая архитектура жизнеспособда.

G>Но тогда важное условие чтобы транзакционный бизнес-процесс не пересекал границы микросервисов. Как только начинает пересекать — появляются зависимости и одно без другого уже не работает.
С транзакциями да, нормального решения для распределённых транзакций не существует. Зато есть идемпотентность.
G>Например для ecommerce деление на "заказ" и "склад" в таком случае не актуально, так как при создании заказа надо на складе все забронировать.
Либо у нас появляются разные заказы: один на стороне "корзинки", и другой на стороне склада — при его создании выполняется автоматическое резервирование.
А сервис корзинки (или там оркестрирования) как раз и занимается тем, что идемпотентно долбит "склад" запросами на создание "резерва" до тех пор, пока не получит внятный ответ.
G>А вот "доставку" от "магазина-склада" вполне легко отделить.
Ну, тут опять — ведь у нас транзакция должна и товар из "резерва" в "отгружено" перевести, и в доставке "транспортную накладную" породить.


G>Это ровно до тех пор пока БЛ не начинает опираться на роли пользователей.

G>Например: сотрудник магазина получает скидку 10% на все покупки в онлайн магазине. Чтобы решить эту задачу сервис "заказа" должен пойти в "auth", чтобы получить роль.
Либо просто посмотреть набор ролей в JWT, который был порождён auth при логине.

S>>КМК, это не какие-то "новые" проблемы из-за МСА, а все те же проблемы, которые прекрасно работали и в монолите. Висение в ожидании возможно примерно везде, где есть await (то есть везде). Получение некорректных данных — да запросто, если разработчик воткнул какие-нибудь try {return calculatedTax()} catch { return defaultTax }.

G>Вот именно. Чтобы в монолите получить некорректные данные надо что-то специально написать. В МСА надо специально писать чтобы данные были согласованы.
Не очень понятно, что имеется в виду. Если у нас в микросервисе нет аналогичного кода с try / catch, то обращение к упавшему сервису просто каскадно отдаст 500 internal error, а не неверные данные.

G>За счет чего? Если мы делаем синхронный вызов, то у нас надежность сервиса, зависит от надежности другого и в сумме не превышает надежность монолита.

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

G>Если мы делаем асинхронную репликацию изменений, то отдаем неверные данные.

Непонятно, что вы называете "асинхронной репликацией изменений". Вы имеете в виду, что я могу видеть некорректные остатки на складе из-за того, что у меня в "корзинке" есть заказ в статусе "резервируется"?
Ну так это вопрос к тому, что мы вообще называем верными данными. Потому что ответ на вопрос "а есть ли у нас сейчас в наличии на складе PlayStation 5 pro" в распределённой среде очень сильно зависит от трактовки термина "сейчас".

G>Основной агрумент против, это то, что МСА сама по себе ничего не дает и очень многое отнимает.

G>Все проблемы, которые призвана решать МСА, прекрасно решаются и без МСА. Кроме, наверное, проблемы разработки на нескольких языках программирования.
Ну, "прекрасно" — вопрос спорный. Масштабируемость, к примеру, штука такая, которую трудно решить монолитом. Контраргумент к этому тоже известен: в современную коробку влезает настолько много дури, что хорошо написанный монолит справляется в ней с нагрузкой, которую "красивая МСА" тащит тремястами машинками в облаке.
Но я видел и ребят, у которых такие масштабы нагрузки и объёмы данных, что даже самая передовая современная коробка пробуксовывает. И решают они это именно тем, что пилят систему на мелкие неэффективные запчасти.
То есть делаем в десять раз более прожорливую по ресурсам реализацию, и отдаём ей в сто раз больше ресурсов — получаем таки десятикратный рост пропускной способности по сравнению с топовым сервером.

G>И то, например, сейчас можно в дотнет процессе запускать Python код. Это не IronPython, который в IL компилирует, а это прямая загрузка питонячего рантайма в дотнет, генераторы, которые делают C# АПИ для питонячего кода и прокидывание ссылок на объекты в из дотнета в пайтон.

Да зачастую проблема языков не в языках, а в том, что одна команда уже переехала на Java 22, а другая всё ещё пердолится с Java 17. И как запустить их код в одном JVM — вопрос интересный.

G>>>Пример реальный: профили пользователей лежат в одном сервисе, данные о туристических маршрутах в другом. Важная часть логики: для несовреннолетних доступна одна часть маршрутов, для соврешеннолетных — другая. Есть еще другие признаки для фильтров: по регионам, интересам итд.

G>>>Теперь нам надо сделать рассылку, подобрав подходящие маршруты для клиентов.
G>>>В монолите — один джоин. В MSA — пожалуйста напишите тонну кода.
G>>>В реальности ситуация была еще хуже. Данные о бронях групп и совершенных путешествиях лежали в третьем сервисе. И конечно же надо было из предложений исключить тех кто уже ездил.
S>>Тут дело, КМК, не в тонне кода — что там такого-то, цикл по пользователям. Скорее, тут время работы — то, что делалось 1 запросом за 15 минут, теперь — миллион обращений к двум сервисам, каждый из которых тратит по две секунды.
G>И как это решить? Там отдельный сервис identity, который занимается аутентификацией и хранит профили (раньше кста отдельный сервис user был, но слили), и отдельный сервис БЛ.
В смысле "как"? Получаем список профилей, и идём по нему в цикле. Можно порезать его на 20 батчей и идти по ним параллельно на 20 машинах.
В общем-то, join делает то же самое — K операций стоимостью O(logM).

G>Ну камон, фичафлаги же есть.

G>Даже в микросервисах часто приходится релизить с фичафлагами по тем же причинам.
А причём тут фичафлаги? Они ортогональны обсуждаемой проблеме. Фичафлаг не автоматизирует ребейз моего "баннера" к "предыдущему релизу".

S>>Нет такой идеи, поэтому и противоречить нечему.

G>Я выше описал почему в МСА микросервис должен быть самодостаточным, иначе проблем от МСА больше, чем преимуществ.
Никакой сервис самодостаточным быть не может — ни микро, ни макро. Потому, что иначе это не "сервис", а целое отдельное приложение. Пользовательский экспириенс так или иначе пересекает границы таких "сервисов".
Даже если мы возьмём какой-нибудь, прости господи, Яндекс, у которого в одном приложении и такси, и лавка, и кино — все они зависят от ID-сервиса, т.к. без логина я ни в один из них не попаду.

G>Звучит так, что недостаток ревью и тестирования пытаются решить не на том уровне.

Усиление ревью и тестирования ещё сильнее удлиняет цикл релиза.
Помнится, была весёлая история про то, как пилили фичу про шатдаун в винде 10. Там от коммита в kernel до возможности протестировать кнопку в explorer.exe проходило три месяца — несмотря на отсутствие микросервисов и монолитность.

S>>Во-первых, может и балуемся, во-вторых, вы же не ведёте разработку в main. Вот ребята в В залили в монорепу изменение, которое сломало сценарий 2. "У нас юнит-тесты зелёные, а если у кого-то сломался сценарий — значит, они нас неправильно используют". Им дали по пальцам, откатили PR. Ребята из Б залили свой PR — сломался сценарий 1. И ребята из А заливают свой PR с тем же результатом.

G>У нас все просто — кто делает ПР тот и чинит. Тупо защита на ветке — не сольешь пока есть ошибки билда (там еще и тесты запускаются, но у нас их мало).

S>>Предотвращение мерджа ломающих изменений — это чисто организационная работа. То есть если у нас есть интеграционные тесты со всеми сценариями И запрет мёрджа PR, при котором ломаются такие тесты — то всё будет работать и в монорепо, и в раздельных репозиториях. А если набора интеграционных тестов нет — то и в монорепо у вас окажется ситуация, когда все юнит-тесты зелёные, покрытие кода 99%, а ни один пример из папочки examples запустить не получается.

G>Я бы сказал что это чисто настройка ci\cd системы, конечно пока все в одной репе происходит.
Я и говорю — всё это сводится к настройке ci/cd. Если в ней есть интеграционные тесты, то неважно, монорепо там, или микросервисы с отдельными репозиториями, или всё одно приложение в ./src.
А если нету — то никакой монорепо вас не спасёт.

G>Как только у вас требуется коммит в несколько реп для реализации функционала — начинаются танцы с бубнами (реальное описание "организационной работы")

Ну, может быть.

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

G>Тогда нужно отказываться от МСА. Потому что МСА добавляет огромное пространство для неочевидно неправильных решений. В рамках монолита аналогичные решения были бы очевидно неправильными.
G>Выдача неправильных данных при ошибке одного из сервисов как раз из этой области.
Я всё ещё не понимаю сценарий выдачи неправильных данных.

G>ИИ? Линтеры? Показательно бить палками тех кто так пишет?

G>Зачем для решения плохого кода на TS делать МСА?
Потому что радикальные решения иногда эффективнее теоретически правильных, но необязательных.
Вроде всем разработчикам уже тридцать лет долбят "API first", но пока фронтенд живёт "рядом" с API в том же ASP.NET приложении, почему-то всегда появляются волшебные скрины, которые дают сделать невозможные через API вещи.
И только хардкорная смена архитектуры на "фронт ходит к нам в бэк только через тот же API, который мы отдаём наружу", заставляет волосы стать мягкими и шелковистыми.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[15]: Помогите правильно спроектировать микросервисное приложение
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 01.02.26 14:45
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>Да, я об этом и писал. Если все микро-сервисы могут работать независимо друг от друга, то такая архитектура жизнеспособда.

G>>Но тогда важное условие чтобы транзакционный бизнес-процесс не пересекал границы микросервисов. Как только начинает пересекать — появляются зависимости и одно без другого уже не работает.
S>С транзакциями да, нормального решения для распределённых транзакций не существует. Зато есть идемпотентность.
G>>Например для ecommerce деление на "заказ" и "склад" в таком случае не актуально, так как при создании заказа надо на складе все забронировать.
S>Либо у нас появляются разные заказы: один на стороне "корзинки", и другой на стороне склада — при его создании выполняется автоматическое резервирование.
S>А сервис корзинки (или там оркестрирования) как раз и занимается тем, что идемпотентно долбит "склад" запросами на создание "резерва" до тех пор, пока не получит внятный ответ.
Идемпотентность гарантирует только согласованность в конечном счете, но не гарантирует больше ничего.
Например два сервиса Заказ и Склад.
1) Заказ сохраняет у себя данные о том, что пользователь хочет заказать товары
2) Заказ резервирует товары на складе — делает идемпотентный запрос
3) После успешного резервирования пользователь попадает на страницу оплаты

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

Это, конечно, мелочь, в одном месте можно забить. Но проблема в том, что такие мелочи всплывают на каждом пересечении границ МС.
А ели один сервис вынужден деграть не один, а два сервиса, то становится хуже.


G>>А вот "доставку" от "магазина-склада" вполне легко отделить.

S>Ну, тут опять — ведь у нас транзакция должна и товар из "резерва" в "отгружено" перевести, и в доставке "транспортную накладную" породить.
При появлении или смене статуса заказа сервис асинхронно отправляет данные в "Доставку". Тут согласованность в конечном счете устраивает, ибо у доставки вообще нет конкуренции за одни и те же данные.



G>>Это ровно до тех пор пока БЛ не начинает опираться на роли пользователей.

G>>Например: сотрудник магазина получает скидку 10% на все покупки в онлайн магазине. Чтобы решить эту задачу сервис "заказа" должен пойти в "auth", чтобы получить роль.
S>Либо просто посмотреть набор ролей в JWT, который был порождён auth при логине.
Как посмотреть? Хранить в кэше? Тогда наткнемся на устаревание данных
Каждому пользователю в токен писать значение — распухнет токен, да и то же устаревание на время жизни токена.
Получается согласованную в смысле acid логику не сделаешь. Может в этом конкретном случае не нужна, а если всё-таки нужна, то что делать?
И если уже было спроектировано в режиме "согласованности в конечном счете", то фарш назад не провернешь.

S>>>КМК, это не какие-то "новые" проблемы из-за МСА, а все те же проблемы, которые прекрасно работали и в монолите. Висение в ожидании возможно примерно везде, где есть await (то есть везде). Получение некорректных данных — да запросто, если разработчик воткнул какие-нибудь try {return calculatedTax()} catch { return defaultTax }.

G>>Вот именно. Чтобы в монолите получить некорректные данные надо что-то специально написать. В МСА надо специально писать чтобы данные были согласованы.
S>Не очень понятно, что имеется в виду. Если у нас в микросервисе нет аналогичного кода с try / catch, то обращение к упавшему сервису просто каскадно отдаст 500 internal error, а не неверные данные.
Я говорю про обращение к сервису, который зависит от упавшего.
Как в примере выше Заказ зависит от auth. Если auth упал сразу же после смены статуса сотрудника для пользователя, то работа Заказа будет некорректной.

G>>За счет чего? Если мы делаем синхронный вызов, то у нас надежность сервиса, зависит от надежности другого и в сумме не превышает надежность монолита.

S>За счёт того, что мы вообще не зависим от того, работают ли сервисы за пределами используемой цепочки.
Если бизнес-процесс, который должен выполняться транзакционно, охватывает цепочку сервисов, то с точки зрения надежности выгодно эту цепочку реализовать в монолите.
А если вязть весь набор таких процессов, то окажется что выгоднее в целом монолит иметь.

S>А в монолите у нас полное домино, когда любая фигня может привести к тому, что просто весь монолит не стартует из-за кривого коммита в маловажный угол.

Ну конечно же не может, ибо в рамках ci\cd процесса все проверяется. А kubernetes даже обновлять все экземпляры не будет если одна из нод не стартанет.
Короче проблема попадания плохого кода на прод и плохого деплоя решается вообще на другом уровне, а не на уровне архитектуры внутри вашего кода.

G>>Если мы делаем асинхронную репликацию изменений, то отдаем неверные данные.

S>Непонятно, что вы называете "асинхронной репликацией изменений". Вы имеете в виду, что я могу видеть некорректные остатки на складе из-за того, что у меня в "корзинке" есть заказ в статусе "резервируется"?
На примере Склад-Доставка. Склад получил статус заказа "оплачен". Фоновый процесс в сервисе заказ видит это изменение и публикует это изменение в рапределенной очереди.
Сервис доставки сидит сообщение из очереди и дает сигнал курьеру приехать забрать заказ.
Курьер приезжает на склад, берет заказ, в сервисе Склад статус меняется на Отгружен, и далее через тот же фоновый процесс данные попадают в сервис доставки.

S>Ну так это вопрос к тому, что мы вообще называем верными данными. Потому что ответ на вопрос "а есть ли у нас сейчас в наличии на складе PlayStation 5 pro" в распределённой среде очень сильно зависит от трактовки термина "сейчас".

Не надо задавать такие вопросы. В книге Pragmatic Programmer описан принцип "tell, don't ask". В соответствии с ним мы должны просто отправлять команду на бронирование PlayStation 5 pro.
Но мы тогда должны что-то делать если этого не произойдет.
В рамках монолита все просто — транзакция. Она атомарная, если что откатится целиком.
В рамках МСА у нас проблема, нет атомарности. Мы вызывали два сервиса в режиме "tell, don't ask", но второй вернул ошибку, а первый нет. Консистентность системы уже нарушена. Остается только вопрос можете ли вы с этим жить.
И самое главное сможете ли вы с этим жить в будущем.

G>>Основной агрумент против, это то, что МСА сама по себе ничего не дает и очень многое отнимает.

G>>Все проблемы, которые призвана решать МСА, прекрасно решаются и без МСА. Кроме, наверное, проблемы разработки на нескольких языках программирования.
S>Ну, "прекрасно" — вопрос спорный. Масштабируемость, к примеру, штука такая, которую трудно решить монолитом.
Да ладно, как-то решали до изобретения МСА (как термина), и сейчас решают неплохо.

Я с 2010 года работал с SharePoint, а с 2023 по середину 2025.
Оба продукта монолитные, оба масштабируются. Причем масштабируются так, как большинству приложений на МСА не снилось.
На уровне кода масштабирование происходит вокруг модулей.
То есть ты говоришь: на сервере А модули 1,2,3, а на сервере Б модули 4,5,6
В 1С еще круче, там каждую фоновую задачу можно "прибить" к своему набору серверов.

На уровне баз — каждая секция данных работает со своей базой (со своей строкой подключения).
В 1С вообще просто — одна Информационная База — одна БД. Можно каждую на своем сервере БД развернуть.
Связаны они между собой через веб-сервисы. То есть каждая база делает честный вызов веб-сервиса. Чтобы легко работала аутентификация можно всю аутентификацию проводить через одну базу (oauth) и есть консоль управления которая учетки в разные базы умеет пропихивать. Тоже через веб-сервисы.
Общих сервисов по сути нет, кроме поиска (он горизонтально масштабируется). Тенанты (наборы баз) изолированы за счет учетных записей — нет учетки в целевой базе, ты туда не попадешь. Консоль управления знает про тенанты, а больше никто не знает.
Каждая база в 1С это микросервис, который полностью покрывает один процесс.

В SharePoint по другому. Там нет "цепочек сервисов". Каждый бизнес-процесс может быть реализован как независимый "сайт" со своими "фичами", каждый "сайт может лежать в своей БД. Из серверного кода возможно прямое обращение к бд любого сайта.

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

S>Контраргумент к этому тоже известен: в современную коробку влезает настолько много дури, что хорошо написанный монолит справляется в ней с нагрузкой, которую "красивая МСА" тащит тремястами машинками в облаке.

Но вот из недавних видео стало известно, что OpenAI долгое время сидит на одном кластере Postgres с одним мастером.

S>Но я видел и ребят, у которых такие масштабы нагрузки и объёмы данных, что даже самая передовая современная коробка пробуксовывает. И решают они это именно тем, что пилят систему на мелкие неэффективные запчасти.

А вот я не видел если честно. Я знаком как там внутри в Ozon и WB — там сокращение МСА в некоторых местах сильно бы повысило быстродействие пропускную способность на том же железе. Возможно во всех повысило бы, я просто не про все процессы в курсе.

До смешного: на моем текущем месте было 24 МС на 12 человек разработки. Предыдущий архитектор "свалил в закат" и меня позвали починить. Все тормозило, ошибки в данных и еще куча других проблем.
После починки 8 самых тормозящих запросов оказалось, что сервера средней руки (24 ядра, 96 гб ОП) достаточно чтобы все запустить на проде. Для БД еще одни такой же.
А если убрать МСА и заняться оптимизацией, то есть ощущение что эти требования сократятся еще в 4 раза. Чем я сейчас и занимаюсь.
Те проблемы, о которых пишу, я вижу и чиню каждый день.

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

А самое главное сколько при этом программистов можно загрузить работой

G>>И то, например, сейчас можно в дотнет процессе запускать Python код. Это не IronPython, который в IL компилирует, а это прямая загрузка питонячего рантайма в дотнет, генераторы, которые делают C# АПИ для питонячего кода и прокидывание ссылок на объекты в из дотнета в пайтон.

S>Да зачастую проблема языков не в языках, а в том, что одна команда уже переехала на Java 22, а другая всё ещё пердолится с Java 17. И как запустить их код в одном JVM — вопрос интересный.
У жабы какой-то свой мир со своими проблемами, которые мне малопонятны честно говоря.
Но у меня вопрос: а как изначально получилось что у двух команд разные версии? Кто-то же принял такое решение что две команды, должны независимо друг от друга что-то делать. Какая была логика в этом решении?

G>>>>Пример реальный: профили пользователей лежат в одном сервисе, данные о туристических маршрутах в другом. Важная часть логики: для несовреннолетних доступна одна часть маршрутов, для соврешеннолетных — другая. Есть еще другие признаки для фильтров: по регионам, интересам итд.

G>>И как это решить? Там отдельный сервис identity, который занимается аутентификацией и хранит профили (раньше кста отдельный сервис user был, но слили), и отдельный сервис БЛ.
S>В смысле "как"? Получаем список профилей, и идём по нему в цикле. Можно порезать его на 20 батчей и идти по ним параллельно на 20 машинах.
S>В общем-то, join делает то же самое — K операций стоимостью O(logM).
Ага, а тут мы еще получили протекание логики из БЛ в identity. Сменится "предикат" этого корсс-сервисного джоина и придется править в двух местах, со всеми прелестями, что омы обсуждаем ниже.

G>>Ну камон, фичафлаги же есть.

G>>Даже в микросервисах часто приходится релизить с фичафлагами по тем же причинам.
S>А причём тут фичафлаги? Они ортогональны обсуждаемой проблеме. Фичафлаг не автоматизирует ребейз моего "баннера" к "предыдущему релизу".
Ок, восстанавливаем контекст:
1) Если два набора функциональности, в рамках одного монолита асинхронно, то есть точек пересечения мало.
2) Но оба набора набора должны вместе.
Решение: пилим в разных ветках, делаем слабую связность через "контракты" чтобы меньше было конфликтов на пересечении, деплоим под фича флагами, то есть если флаг выключен, но работает все по старому, если включен — по новому.

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


S>>>Нет такой идеи, поэтому и противоречить нечему.

G>>Я выше описал почему в МСА микросервис должен быть самодостаточным, иначе проблем от МСА больше, чем преимуществ.
S>Никакой сервис самодостаточным быть не может — ни микро, ни макро. Потому, что иначе это не "сервис", а целое отдельное приложение. Пользовательский экспириенс так или иначе пересекает границы таких "сервисов".
S>Даже если мы возьмём какой-нибудь, прости господи, Яндекс, у которого в одном приложении и такси, и лавка, и кино — все они зависят от ID-сервиса, т.к. без логина я ни в один из них не попаду.
Я не говорю про пользовательский экспериентс, я говорю конкретно о границе бизнес-процесса.
Что касается Кинопоиска и Такси мне кажется это как раз те случаем где они являются самодостаточными предложениями. Они конечно все обращаются к ИД яндекса, но если бы не обращались что для них изменилось бы?
Я вот могу сделать свой сайт и прикрутить ИД Яндекса, он же не становится частью яндекса? А если стиль сайта будет как у яндекса и яндекс в какой-то из своих каталогов добавит ссылку на мой сайт?

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


G>>Звучит так, что недостаток ревью и тестирования пытаются решить не на том уровне.

S>Усиление ревью и тестирования ещё сильнее удлиняет цикл релиза.
Это как раз касается вопроса масштабирования команд. На него ответа нет. Нельзя с командой в 100 человек использовать те же процессы, которые работают с командой на 10 человек. МСА дает какой-то ответ, пока лучше ничего не придумали.
Но до сих пор никто не знает где граница. Является ли МСА необходимостью если у тебя 50 человек — хз.

S>Помнится, была весёлая история про то, как пилили фичу про шатдаун в винде 10. Там от коммита в kernel до возможности протестировать кнопку в explorer.exe проходило три месяца — несмотря на отсутствие микросервисов и монолитность.

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

S>Я и говорю — всё это сводится к настройке ci/cd. Если в ней есть интеграционные тесты, то неважно, монорепо там, или микросервисы с отдельными репозиториями, или всё одно приложение в ./src.

S>А если нету — то никакой монорепо вас не спасёт.
А если несколько реп у каждого свой CI\CD как этот контроль и запуск интеграционнаых тестов обеспечить?
У нас реально было такое что в двух сервисах внесли изменения так, что они работали с предыдущей версией другого сервиса, поэтому прекрасно проходили тесты, а когда зарелизили обе новых версии все упало.



S>И только хардкорная смена архитектуры на "фронт ходит к нам в бэк только через тот же API, который мы отдаём наружу", заставляет волосы стать мягкими и шелковистыми.

Это тоже ни разу не гарантия.
Случай из практики: новые требования по валидации форм клента.
На клиенте запилили, а на сервере забыли. А когда я пошел выяснять почему забыли — оказалось что и не знали что надо все правила на сервере проверять.
По сути часть важной БЛ так и существует только на клиенте.

Причем ранее был код на blazor (для прототипа), там все проверки в одном месте пилилсь, а потом решили делать фронт на ангуляр по этой самой причине, чтобы делали все хорошо. Вот так "хорошо" и получилось.
Re[16]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 02.02.26 12:57
Оценка:
Здравствуйте, gandjustas, Вы писали:

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

G>В рамках монолита все просто — транзакция. Она атомарная, если что откатится целиком.
Это типичная ошибка "как дОлжно" vs "как оно физически работает". Ты описываешь идеальный мир с единорогами. Всё так, так и должно работать, спору нет, я тоже за всё хорошее и против всего плохого.
А потом внезапно получается, что от момента нажатия кнопочки "хочу купить" до команды на отправку товара со склада — может пройти час, а то и неделя, а то и вообще плюнуть и уйти. Будешь держать распределённую транзакцию открытой?

G>В рамках МСА у нас проблема, нет атомарности. Мы вызывали два сервиса в режиме "tell, don't ask", но второй вернул ошибку, а первый нет. Консистентность системы уже нарушена. Остается только вопрос можете ли вы с этим жить.

Это не в рамках МСА, а в реальном физическом мире не бывает атомарности. МСА это делает очевидным и заставляет выражать явно.

G>И самое главное сможете ли вы с этим жить в будущем.

Хошь, не хошь, а придётся.

G>Сервис доставки сидит сообщение из очереди и дает сигнал курьеру приехать забрать заказ.

А товара на складе нет, т.к. минуту назад другой курьер забрал последнее для другого заказа.
И нам придётся писать извинительное письмо "счастливому" клиенту, оплатить курьеру потраченное время и бензин, откатить платёж по кредитке, оплатив banking fees.
Вот такой он, откат атомарной транзакции.

G>Но у меня вопрос: а как изначально получилось что у двух команд разные версии? Кто-то же принял такое решение что две команды, должны независимо друг от друга что-то делать. Какая была логика в этом решении?

А как ты себе представляешь перевести огромную систему над которой работают десятки-сотни девов с одной версии на другую? У разных частей системы могут быть разные требования к памяти-процу-сети-диску.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 02.02.2026 13:35 · . Предыдущая версия . Еще …
Отредактировано 02.02.2026 13:33 · . Предыдущая версия .
Re[17]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 03.02.26 10:56
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


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

G>>В рамках монолита все просто — транзакция. Она атомарная, если что откатится целиком.
·>Это типичная ошибка "как дОлжно" vs "как оно физически работает". Ты описываешь идеальный мир с единорогами. Всё так, так и должно работать, спору нет, я тоже за всё хорошее и против всего плохого.
·>А потом внезапно получается, что от момента нажатия кнопочки "хочу купить" до команды на отправку товара со склада — может пройти час, а то и неделя, а то и вообще плюнуть и уйти. Будешь держать распределённую транзакцию открытой?
Вы процитировали кусок, как-будто не читали предыдущую переписку.
Там много обсуждали разные задачи и конкретно этот кейс про задачу, которая может быть решена в рамках одной транзакции БД.
Или вы хотите сказать что вообще не надо опираться на транзакции БД потому что бывают бизнес-процессы, которые не укладываются в транзакции БД?

G>>В рамках МСА у нас проблема, нет атомарности. Мы вызывали два сервиса в режиме "tell, don't ask", но второй вернул ошибку, а первый нет. Консистентность системы уже нарушена. Остается только вопрос можете ли вы с этим жить.

·>Это не в рамках МСА, а в реальном физическом мире не бывает атомарности. МСА это делает очевидным и заставляет выражать явно.
В рамках одного процесса вполне себе бывает.

G>>И самое главное сможете ли вы с этим жить в будущем.

·>Хошь, не хошь, а придётся.
Угу, ровно до тех пор пока не появятся требования чтобы этого не было.


G>>Сервис доставки сидит сообщение из очереди и дает сигнал курьеру приехать забрать заказ.

·>А товара на складе нет, т.к. минуту назад другой курьер забрал последнее для другого заказа.
То есть вы где-то ранее потеряли транзакционность зарезервировав товаров больше чем есть на складе.

·>И нам придётся писать извинительное письмо "счастливому" клиенту, оплатить курьеру потраченное время и бензин, откатить платёж по кредитке, оплатив banking fees.

·>Вот такой он, откат атомарной транзакции.
Тут дело не в транзакции выдачи со склада, тут вы транзакционность потеряли парой шагов бизнес-процесса ранее.


G>>Но у меня вопрос: а как изначально получилось что у двух команд разные версии? Кто-то же принял такое решение что две команды, должны независимо друг от друга что-то делать. Какая была логика в этом решении?

·>А как ты себе представляешь перевести огромную систему над которой работают десятки-сотни девов с одной версии на другую? У разных частей системы могут быть разные требования к памяти-процу-сети-диску.
Ну для команды в 25 человек я делал, ничего катастрофичного, тем более зачастую есть обратная совместимость.
Но я не знаю проблемы конкретно жабы, что там за проблема перехода с версии на версию. У меня в проекте сейчас работает C# 10-летней давности. Да, неэффективно по сравнению с текущей версией, но работает.
Re: Помогите правильно спроектировать микросервисное приложение
От: gyraboo  
Дата: 03.02.26 11:46
Оценка: 12 (1)
Здравствуйте, busk, Вы писали:

B>Как-то только в теории читал про микросервисы, но вот новый проект планируется небольшой и хотел как раз попробовать микросервисы тут.


Опиши в JHipster мета-модель на доменном DSL-языке (https://www.jhipster.tech/jdl-studio) и по ней он тебе сгенерит микросервисную архитектуру (даже сразу с микрофронтами).
И изучи её, там всё реализовано в коде для твоей бизнес-модели — и модель данных, и роли (стандартные админ и юзер), и микросервисы, и микрофронты. А бизнес-логику и доп. роли ты уже сам допишешь в код.
Есть книга JHipster (https://www.infoq.com/minibooks/jhipster-mini-book-7), там описано как применять микросервисы и микрофронты.

А микросервисы без микрофронтов — это уже прошлый век, это не круто, не по-хипстерски.
Re[18]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 03.02.26 16:17
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>·>Это типичная ошибка "как дОлжно" vs "как оно физически работает". Ты описываешь идеальный мир с единорогами. Всё так, так и должно работать, спору нет, я тоже за всё хорошее и против всего плохого.

G>·>А потом внезапно получается, что от момента нажатия кнопочки "хочу купить" до команды на отправку товара со склада — может пройти час, а то и неделя, а то и вообще плюнуть и уйти. Будешь держать распределённую транзакцию открытой?
G>Вы процитировали кусок, как-будто не читали предыдущую переписку.
Каюсь, переписку я читал невнимательно. Скорее всего чего-то упустил.

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

G>Или вы хотите сказать что вообще не надо опираться на транзакции БД потому что бывают бизнес-процессы, которые не укладываются в транзакции БД?
Так ведь вопрос идёт об архитектуре. Откуда так получилось, что разные независимые физические сущности оказались в одной БД? Склад, корзина пользователя, каталоги товаров — всё лежит в одном монолите... Удобно, конечно, но работает плохо, т.к. неверно моделирует реальность.

G>·>Это не в рамках МСА, а в реальном физическом мире не бывает атомарности. МСА это делает очевидным и заставляет выражать явно.

G>В рамках одного процесса вполне себе бывает.
Ну вот плохо получается, если моделировать одним процессом. Участники бизнес-процессов в реальном мире — независимы, географически распределены, работают параллельно.

G>>>И самое главное сможете ли вы с этим жить в будущем.

G>·>Хошь, не хошь, а придётся.
G>Угу, ровно до тех пор пока не появятся требования чтобы этого не было.
Законы физики требованиями не изменишь.

G>·>И нам придётся писать извинительное письмо "счастливому" клиенту, оплатить курьеру потраченное время и бензин, откатить платёж по кредитке, оплатив banking fees.

G>·>Вот такой он, откат атомарной транзакции.
G>Тут дело не в транзакции выдачи со склада, тут вы транзакционность потеряли парой шагов бизнес-процесса ранее.
Это разные транзакции в разных частях системы.

G>·>А как ты себе представляешь перевести огромную систему над которой работают десятки-сотни девов с одной версии на другую? У разных частей системы могут быть разные требования к памяти-процу-сети-диску.

G>Ну для команды в 25 человек я делал, ничего катастрофичного, тем более зачастую есть обратная совместимость.
G>Но я не знаю проблемы конкретно жабы, что там за проблема перехода с версии на версию.
В мелкомасштабных проектах работает, да. А так банально даже не в версии дело, а скажем, есть Azul и Oracle. С разной стоимостью, лицензиями и разными свойствами. Да даже просто ключи запуска могут отличаться.

G>У меня в проекте сейчас работает C# 10-летней давности. Да, неэффективно по сравнению с текущей версией, но работает.

Именно. А начнёшь переезд — заколебаешься для большого проекта.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 03.02.2026 16:19 · . Предыдущая версия . Еще …
Отредактировано 03.02.2026 16:18 · . Предыдущая версия .
Re[19]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 04.02.26 11:58
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


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

G>>Или вы хотите сказать что вообще не надо опираться на транзакции БД потому что бывают бизнес-процессы, которые не укладываются в транзакции БД?
·>Так ведь вопрос идёт об архитектуре. Откуда так получилось, что разные независимые физические сущности оказались в одной БД? Склад, корзина пользователя, каталоги товаров — всё лежит в одном монолите... Удобно, конечно, но работает плохо, т.к. неверно моделирует реальность.
А в чем смысл "верно моделировать реальность"? тем более что у слов "верно" и "реальность" очень субъективное понимание?
Я думаю есть единственно верный способ моделирования — отталкиваться от конкретных решаемых задач. Если нам нужно транзакционно обновить состояние корзины с резервов на складе, то они должны быть в одной базе и работать в одной транзакции.

G>>·>Это не в рамках МСА, а в реальном физическом мире не бывает атомарности. МСА это делает очевидным и заставляет выражать явно.

G>>В рамках одного процесса вполне себе бывает.
·>Ну вот плохо получается, если моделировать одним процессом.
Тогда можно атомарность сделать за счет одной бд на одном сервере.
СУБД умеет обеспечивать атомарность изменений в рамках одного сервера\бд. Прям гарантировано умеет, в отличие от наколеночных поделок.

·>Участники бизнес-процессов в реальном мире — независимы, географически распределены, работают параллельно.

Как это связано? Если они все приходят в итоге на одни сервер? бд например, то задача решаема.

G>>>>И самое главное сможете ли вы с этим жить в будущем.

G>>·>Хошь, не хошь, а придётся.
G>>Угу, ровно до тех пор пока не появятся требования чтобы этого не было.
·>Законы физики требованиями не изменишь.
Так ты архитектуру подстраивай под физику, а не требования под архитектуру. Архитектура это вообще наименее ценное что у тебя есть в проекте.

G>>·>И нам придётся писать извинительное письмо "счастливому" клиенту, оплатить курьеру потраченное время и бензин, откатить платёж по кредитке, оплатив banking fees.

G>>·>Вот такой он, откат атомарной транзакции.
G>>Тут дело не в транзакции выдачи со склада, тут вы транзакционность потеряли парой шагов бизнес-процесса ранее.
·>Это разные транзакции в разных частях системы.
И что? Все равно потеряли.
В рамках бизес-процесса бывают шаги, где нужна транзакционно, а бывает что не нужна. Я же описывал что резервирование товаров на складе и смена статуса заказа на "зарезервирован" должны быть в одной транзакции, также как и уменьшение остатков на складе при смене статуса на "отгружен". А вот между этими двумя шагами никакая транзакционность не нужна.

G>>·>А как ты себе представляешь перевести огромную систему над которой работают десятки-сотни девов с одной версии на другую? У разных частей системы могут быть разные требования к памяти-процу-сети-диску.

G>>Ну для команды в 25 человек я делал, ничего катастрофичного, тем более зачастую есть обратная совместимость.
G>>Но я не знаю проблемы конкретно жабы, что там за проблема перехода с версии на версию.
·>В мелкомасштабных проектах работает, да. А так банально даже не в версии дело, а скажем, есть Azul и Oracle. С разной стоимостью, лицензиями и разными свойствами. Да даже просто ключи запуска могут отличаться.
И это повод не пользоваться транзакциями?

G>>У меня в проекте сейчас работает C# 10-летней давности. Да, неэффективно по сравнению с текущей версией, но работает.

·>Именно. А начнёшь переезд — заколебаешься для большого проекта.
Так у меня большой проект. За 10 лет огого сколько всего написали.
Re[20]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 04.02.26 15:00
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>·>Так ведь вопрос идёт об архитектуре. Откуда так получилось, что разные независимые физические сущности оказались в одной БД? Склад, корзина пользователя, каталоги товаров — всё лежит в одном монолите... Удобно, конечно, но работает плохо, т.к. неверно моделирует реальность.

G>А в чем смысл "верно моделировать реальность"? тем более что у слов "верно" и "реальность" очень субъективное понимание?
G>Я думаю есть единственно верный способ моделирования — отталкиваться от конкретных решаемых задач. Если нам нужно транзакционно обновить состояние корзины с резервов на складе, то они должны быть в одной базе и работать в одной транзакции.
А откуда возникла такая задача "транзакционно обновить состояние корзины с резервов на складе"? По-моему, ты путаешь цель и средство.

Аналогия. Мне это напоминает давнишние споры cvcs vs dvcs. Ну как типа "нам нужно точно пронумеровать каждый changeset, как в svn. А вот git так не умеет, значит отстой". Вот только git точнее описывает реально происходящее при работе с исходниками, а не случайно придуманные "нам нужно". Не бывает в ральном мире точно пронумерованных changesets. Поэтому svn умер.

G>>>В рамках одного процесса вполне себе бывает.

G>·>Ну вот плохо получается, если моделировать одним процессом.
G>Тогда можно атомарность сделать за счет одной бд на одном сервере.
G>СУБД умеет обеспечивать атомарность изменений в рамках одного сервера\бд. Прям гарантировано умеет, в отличие от наколеночных поделок.
Да не важно. СУБД это просто такой готовый процесс.

G>·>Участники бизнес-процессов в реальном мире — независимы, географически распределены, работают параллельно.

G>Как это связано? Если они все приходят в итоге на одни сервер? бд например, то задача решаема.
Мы можем попытаться их заставить ходить на один сервер, реализуя такую монолитную архитектуру. И это даже как-то будет работать. Пока этот один сервер не ляжет.

G>>>Тут дело не в транзакции выдачи со склада, тут вы транзакционность потеряли парой шагов бизнес-процесса ранее.

G>·>Это разные транзакции в разных частях системы.
G>И что? Все равно потеряли.
G>В рамках бизес-процесса бывают шаги, где нужна транзакционно, а бывает что не нужна. Я же описывал что резервирование товаров на складе и смена статуса заказа на "зарезервирован" должны быть в одной транзакции
А как это решит твою проблему "пользователь плюет на это дело и идет в другой магазин"? Какую вообще проблему решает эта твоя "одна транзакция"?

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

G>·>В мелкомасштабных проектах работает, да. А так банально даже не в версии дело, а скажем, есть Azul и Oracle. С разной стоимостью, лицензиями и разными свойствами. Да даже просто ключи запуска могут отличаться.

G>И это повод не пользоваться транзакциями?
Это повод не использовать один процесс. И транзакции, если ими пользоваться, будут распределёнными. Что кошмар и ужас.

G>>>У меня в проекте сейчас работает C# 10-летней давности. Да, неэффективно по сравнению с текущей версией, но работает.

G>·>Именно. А начнёшь переезд — заколебаешься для большого проекта.
G>Так у меня большой проект. За 10 лет огого сколько всего написали.
Так и я о том же. В рамках МСА была бы группка мелких проектов, то можно было бы есть слона по частям. А сейчас ты в тупике.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 04.02.2026 15:16 · . Предыдущая версия .
Re[21]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 06.02.26 22:33
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>·>Так ведь вопрос идёт об архитектуре. Откуда так получилось, что разные независимые физические сущности оказались в одной БД? Склад, корзина пользователя, каталоги товаров — всё лежит в одном монолите... Удобно, конечно, но работает плохо, т.к. неверно моделирует реальность.

G>>А в чем смысл "верно моделировать реальность"? тем более что у слов "верно" и "реальность" очень субъективное понимание?
G>>Я думаю есть единственно верный способ моделирования — отталкиваться от конкретных решаемых задач. Если нам нужно транзакционно обновить состояние корзины с резервов на складе, то они должны быть в одной базе и работать в одной транзакции.
·>А откуда возникла такая задача "транзакционно обновить состояние корзины с резервов на складе"? По-моему, ты путаешь цель и средство.
Конкретно это из озона. В целом в любой торговле со склада задача актуальна, если надо не продать больше чем есть на складе.

·>Аналогия. Мне это напоминает давнишние споры cvcs vs dvcs. Ну как типа "нам нужно точно пронумеровать каждый changeset, как в svn. А вот git так не умеет, значит отстой". Вот только git точнее описывает реально происходящее при работе с исходниками, а не случайно придуманные "нам нужно". Не бывает в ральном мире точно пронумерованных changesets. Поэтому svn умер.

Я даже не возьмусь комментировать по пунктам этот опус. Я работал с SVN и git, и первый умер совсем по другим причинам, а не связанным с нумерованные ченджсетов.


G>>>>В рамках одного процесса вполне себе бывает.

G>>·>Ну вот плохо получается, если моделировать одним процессом.
G>>Тогда можно атомарность сделать за счет одной бд на одном сервере.
G>>СУБД умеет обеспечивать атомарность изменений в рамках одного сервера\бд. Прям гарантировано умеет, в отличие от наколеночных поделок.
·>Да не важно. СУБД это просто такой готовый процесс.
Угу, процесс который умеет это делать, в отличие от твоего процесса, который по умолчанию не умеет и надо прям поприседать чтобы умел.

G>>·>Участники бизнес-процессов в реальном мире — независимы, географически распределены, работают параллельно.

G>>Как это связано? Если они все приходят в итоге на одни сервер? бд например, то задача решаема.
·>Мы можем попытаться их заставить ходить на один сервер, реализуя такую монолитную архитектуру. И это даже как-то будет работать. Пока этот один сервер не ляжет.
У OpenAI как-то не лег с их количеством клиентов и обращений.
Если ты сделал оптимальные запросы, а сервер бд лег от нагрузки, то у тебя будет только одна проблема — куда потратить миллионы, которые ты заработал.

G>>>>Тут дело не в транзакции выдачи со склада, тут вы транзакционность потеряли парой шагов бизнес-процесса ранее.

G>>·>Это разные транзакции в разных частях системы.
G>>И что? Все равно потеряли.
G>>В рамках бизес-процесса бывают шаги, где нужна транзакционно, а бывает что не нужна. Я же описывал что резервирование товаров на складе и смена статуса заказа на "зарезервирован" должны быть в одной транзакции
·>А как это решит твою проблему "пользователь плюет на это дело и идет в другой магазин"?
Этой проблемы просто не будет, мы никогда не продадим больше чем есть на складе если резервирование сделаем транзакционно.

·>Какую вообще проблему решает эта твоя "одна транзакция"?

Ту самую, которую ты непонятно как хочешь решить.

·>С таким же успехом резервировать можно в одной транзакции, а обновлять статус заказа на "зарезервирован" в другой.


нельзя

·>Вот только в этом случае транзакция резервирования выполняется в БД склада, а транзакция смены статуса в БД заказов. И эти БД могут быть физически распределены и размещены поближе к местам событий.

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


G>>·>В мелкомасштабных проектах работает, да. А так банально даже не в версии дело, а скажем, есть Azul и Oracle. С разной стоимостью, лицензиями и разными свойствами. Да даже просто ключи запуска могут отличаться.

G>>И это повод не пользоваться транзакциями?
·>Это повод не использовать один процесс. И транзакции, если ими пользоваться, будут распределёнными. Что кошмар и ужас.
С распределенными транзакциями у тебя все ляжет сильно раньше, поэтому на практике под нагрузкой их не используют.

G>>>>У меня в проекте сейчас работает C# 10-летней давности. Да, неэффективно по сравнению с текущей версией, но работает.

G>>·>Именно. А начнёшь переезд — заколебаешься для большого проекта.
G>>Так у меня большой проект. За 10 лет огого сколько всего написали.
·>Так и я о том же. В рамках МСА была бы группка мелких проектов, то можно было бы есть слона по частям. А сейчас ты в тупике.

У меня большой проект, который состоит из группы мелких проектов, которые примерно 8 лет усиленно пилили. В 2018 году взяли курс на микросервисность. Сейчас огромное количество проблем с консистентностью данных и быстродействием из-за МСА, что приходится почти целые сервисы переписывать, чтобы обеспечить надежность.
Я думаю за годик смогу запинать все это, чтобы модульный монолит получился.
Re[22]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 08.02.26 14:39
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>>>А в чем смысл "верно моделировать реальность"? тем более что у слов "верно" и "реальность" очень субъективное понимание?

G>>>Я думаю есть единственно верный способ моделирования — отталкиваться от конкретных решаемых задач. Если нам нужно транзакционно обновить состояние корзины с резервов на складе, то они должны быть в одной базе и работать в одной транзакции.
G>·>А откуда возникла такая задача "транзакционно обновить состояние корзины с резервов на складе"? По-моему, ты путаешь цель и средство.
G>Конкретно это из озона.
Это ты описываешь конкретное решение некой задачи. Сама задача-то в чём?

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

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

G>·>Аналогия. Мне это напоминает давнишние споры cvcs vs dvcs. Ну как типа "нам нужно точно пронумеровать каждый changeset, как в svn. А вот git так не умеет, значит отстой". Вот только git точнее описывает реально происходящее при работе с исходниками, а не случайно придуманные "нам нужно". Не бывает в ральном мире точно пронумерованных changesets. Поэтому svn умер.

G>Я даже не возьмусь комментировать по пунктам этот опус. Я работал с SVN и git, и первый умер совсем по другим причинам, а не связанным с нумерованные ченджсетов.
Да не важно. Подставь любую другую очень "нужную" фичу, которая есть в svn но нет в git.
Суть моей аналогии, что твоё "нам нужно [xxx]" ты исходишь не из постановки бизнес-задачи, а из конкретного решения в виде монолитной архитектуры и гигантской всемогущей субд в виде одного процесса.

G>>>Тогда можно атомарность сделать за счет одной бд на одном сервере.

G>>>СУБД умеет обеспечивать атомарность изменений в рамках одного сервера\бд. Прям гарантировано умеет, в отличие от наколеночных поделок.
G>·>Да не важно. СУБД это просто такой готовый процесс.
G>Угу, процесс который умеет это делать, в отличие от твоего процесса, который по умолчанию не умеет и надо прям поприседать чтобы умел.
Речь о другом — процесс не один. Можно иметь две субд, в каждой свои локальные транзакции.

G>>>Как это связано? Если они все приходят в итоге на одни сервер? бд например, то задача решаема.

G>·>Мы можем попытаться их заставить ходить на один сервер, реализуя такую монолитную архитектуру. И это даже как-то будет работать. Пока этот один сервер не ляжет.
G>У OpenAI как-то не лег с их количеством клиентов и обращений.
А там тоже все ходят на один сервер с бд?

G>Если ты сделал оптимальные запросы, а сервер бд лег от нагрузки, то у тебя будет только одна проблема — куда потратить миллионы, которые ты заработал.

Он может просто лечь, и без нагрузки.

G>>>И что? Все равно потеряли.

G>>>В рамках бизес-процесса бывают шаги, где нужна транзакционно, а бывает что не нужна. Я же описывал что резервирование товаров на складе и смена статуса заказа на "зарезервирован" должны быть в одной транзакции
G>·>А как это решит твою проблему "пользователь плюет на это дело и идет в другой магазин"?
G>Этой проблемы просто не будет, мы никогда не продадим больше чем есть на складе если резервирование сделаем транзакционно.
Я не понимаю как "пользователь плюёт" соотносится с "не продадим больше".

G>·>Какую вообще проблему решает эта твоя "одна транзакция"?

G>Ту самую, которую ты непонятно как хочешь решить.
Это какую?

G>·>С таким же успехом резервировать можно в одной транзакции, а обновлять статус заказа на "зарезервирован" в другой.

G>
G>нельзя
Почему? Напомню контекст: МСА.

G>·>Вот только в этом случае транзакция резервирования выполняется в БД склада, а транзакция смены статуса в БД заказов. И эти БД могут быть физически распределены и размещены поближе к местам событий.

G>Ага, и в этом случае статус может стать "зарезервирован", а остаток на складе не уменьшится, и ты будешь разговаривать с недовольными покупателями.
Пока сервис склада не вернёт ответ "резервация прошла успешно" мы не обновляем статус заказа в сервисе заказов.

G>·>Это повод не использовать один процесс. И транзакции, если ими пользоваться, будут распределёнными. Что кошмар и ужас.

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

G>>>Так у меня большой проект. За 10 лет огого сколько всего написали.

G>·>Так и я о том же. В рамках МСА была бы группка мелких проектов, то можно было бы есть слона по частям. А сейчас ты в тупике.
G>У меня большой проект, который состоит из группы мелких проектов, которые примерно 8 лет усиленно пилили. В 2018 году взяли курс на микросервисность. Сейчас огромное количество проблем с консистентностью данных и быстродействием из-за МСА, что приходится почти целые сервисы переписывать, чтобы обеспечить надежность.
G>Я думаю за годик смогу запинать все это, чтобы модульный монолит получился.
Я не понимаю к чему этот опус. Тут я отвечал на твой вопрос: "а как изначально получилось что у двух команд разные версии?". Потому что проекты разные.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[23]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 10.02.26 21:22
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>>>А в чем смысл "верно моделировать реальность"? тем более что у слов "верно" и "реальность" очень субъективное понимание?

G>>>>Я думаю есть единственно верный способ моделирования — отталкиваться от конкретных решаемых задач. Если нам нужно транзакционно обновить состояние корзины с резервов на складе, то они должны быть в одной базе и работать в одной транзакции.
G>>·>А откуда возникла такая задача "транзакционно обновить состояние корзины с резервов на складе"? По-моему, ты путаешь цель и средство.
G>>Конкретно это из озона.
·>Это ты описываешь конкретное решение некой задачи. Сама задача-то в чём?
Не в курсе чем озон занимается? В основном товары со склада продает. Вот и надо продать не больше чем есть.

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

·>Для решения этой задачи нет необходимости обновлять корзину и склад в одной транзакции. Достаточно их обновить последовательно — вначале склад, потом корзину, в разных транзакциях.
Тогда у нас нет никакого выигрыша от того, что это разные транзакции в разных базах. В сумме это будет работать медленнее чем в одной. Более того, возможен сценарий когда первая выполнилась, а вторая отвалилась, просто по таймауту. Тогда товар на складе забронирован, а статус корзины не поменялся. Нужно писать код для отката. Идемпотентной такую операцию уже не сделаешь и автоповтор со стороны клиента тоже.
Короче сплошные недостатки и ноль преимуществ



G>>·>Аналогия. Мне это напоминает давнишние споры cvcs vs dvcs. Ну как типа "нам нужно точно пронумеровать каждый changeset, как в svn. А вот git так не умеет, значит отстой". Вот только git точнее описывает реально происходящее при работе с исходниками, а не случайно придуманные "нам нужно". Не бывает в ральном мире точно пронумерованных changesets. Поэтому svn умер.

G>>Я даже не возьмусь комментировать по пунктам этот опус. Я работал с SVN и git, и первый умер совсем по другим причинам, а не связанным с нумерованные ченджсетов.
·>Да не важно. Подставь любую другую очень "нужную" фичу, которая есть в svn но нет в git.
Так в том и дело, что у СВНа не было преимуществ перед Гит. У Гита был один недостаток — сложность использования, до сих пор два из трех программистов не умеют в гит.

·>Суть моей аналогии, что твоё "нам нужно [xxx]" ты исходишь не из постановки бизнес-задачи, а из конкретного решения в виде монолитной архитектуры и гигантской всемогущей субд в виде одного процесса.

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

G>>>>Тогда можно атомарность сделать за счет одной бд на одном сервере.

G>>>>СУБД умеет обеспечивать атомарность изменений в рамках одного сервера\бд. Прям гарантировано умеет, в отличие от наколеночных поделок.
G>>·>Да не важно. СУБД это просто такой готовый процесс.
G>>Угу, процесс который умеет это делать, в отличие от твоего процесса, который по умолчанию не умеет и надо прям поприседать чтобы умел.
·>Речь о другом — процесс не один. Можно иметь две субд, в каждой свои локальные транзакции.
И что это даст? Неатомарное, несогласованное, неизолированное изменение данных? В чем смысл?

G>>>>Как это связано? Если они все приходят в итоге на одни сервер? бд например, то задача решаема.

G>>·>Мы можем попытаться их заставить ходить на один сервер, реализуя такую монолитную архитектуру. И это даже как-то будет работать. Пока этот один сервер не ляжет.
G>>У OpenAI как-то не лег с их количеством клиентов и обращений.
·>А там тоже все ходят на один сервер с бд?
Один кластер — один мастер для записи и 15 реплик для чтения. Все транзакции меняющие данные ходят на один сервер.

G>>Если ты сделал оптимальные запросы, а сервер бд лег от нагрузки, то у тебя будет только одна проблема — куда потратить миллионы, которые ты заработал.

·>Он может просто лечь, и без нагрузки.
Сам? Просто так? Реплик нет? Админов нет? Какой смысл это рассматривать как реалистичный сценарий?
Ну даже допустим что у вас сервер может лечь, вы его надежность оцениваете как число X в интервале (0;1) — оба конца не включены.
Если рассмотреть сценарий выше с покупкой товаров в ОЗОН и разнесением транзакций на два сервера, то общая надежность будет равна X^2, что строго меньше Х. То есть надежность двух серверов ниже.


G>>>>И что? Все равно потеряли.

G>>>>В рамках бизес-процесса бывают шаги, где нужна транзакционно, а бывает что не нужна. Я же описывал что резервирование товаров на складе и смена статуса заказа на "зарезервирован" должны быть в одной транзакции
G>>·>А как это решит твою проблему "пользователь плюет на это дело и идет в другой магазин"?
G>>Этой проблемы просто не будет, мы никогда не продадим больше чем есть на складе если резервирование сделаем транзакционно.
·>Я не понимаю как "пользователь плюёт" соотносится с "не продадим больше".
Это относится к недостаткам решения. Так как "пользователь плюет" это потеря денег. Потому что дальше пользователь идет на другой маркетплейс и к тебе возможно уже не возвращается никогда

G>>·>Какую вообще проблему решает эта твоя "одна транзакция"?

G>>Ту самую, которую ты непонятно как хочешь решить.
·>Это какую?
Чтобы пользователь придя за своим товаром получил его.

G>>·>С таким же успехом резервировать можно в одной транзакции, а обновлять статус заказа на "зарезервирован" в другой.

G>>
G>>нельзя
·>Почему? Напомню контекст: МСА.
Выше все описал. Без МСА получается лучше.

G>>·>Вот только в этом случае транзакция резервирования выполняется в БД склада, а транзакция смены статуса в БД заказов. И эти БД могут быть физически распределены и размещены поближе к местам событий.

G>>Ага, и в этом случае статус может стать "зарезервирован", а остаток на складе не уменьшится, и ты будешь разговаривать с недовольными покупателями.
·>Пока сервис склада не вернёт ответ "резервация прошла успешно" мы не обновляем статус заказа в сервисе заказов.
Тогда в этом нет никакого смысла, потому что недостатки есть, а преимуществ нет.

G>>·>Это повод не использовать один процесс. И транзакции, если ими пользоваться, будут распределёнными. Что кошмар и ужас.

G>>С распределенными транзакциями у тебя все ляжет сильно раньше, поэтому на практике под нагрузкой их не используют.
·>Я это собственно и сказал. Так какие транзакции ты предлагаешь использовать в случае нескольких процессов?
Напомню что ОпенАИ не использует распределенные транзакции и живет на одном кластере, то есть все записи приходят в один мастер.
Re[24]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 11.02.26 11:07
Оценка: -1
Здравствуйте, gandjustas, Вы писали:

G>>>·>А откуда возникла такая задача "транзакционно обновить состояние корзины с резервов на складе"? По-моему, ты путаешь цель и средство.

G>>>Конкретно это из озона.
G>·>Это ты описываешь конкретное решение некой задачи. Сама задача-то в чём?
G>Не в курсе чем озон занимается?
Судя по твоим заявлениям: транзакционно обновляют состояние корзины с резервов на складе.

G>В основном товары со склада продает. Вот и надо продать не больше чем есть.

Для этого нет необходимости транзакционно обновлять состояние корзины с резервов на складе.

G>·>Для решения этой задачи нет необходимости обновлять корзину и склад в одной транзакции. Достаточно их обновить последовательно — вначале склад, потом корзину, в разных транзакциях.

G>Тогда у нас нет никакого выигрыша от того, что это разные транзакции в разных базах. В сумме это будет работать медленнее чем в одной.
Зато будет работать хотя бы.

G>Более того, возможен сценарий когда первая выполнилась, а вторая отвалилась, просто по таймауту. Тогда товар на складе забронирован, а статус корзины не поменялся. Нужно писать код для отката.

Это эквивалентно ситуации: клиент наполнил корзину и ушел плюнув, потому что левая пятка зачесалась. Код отката брони на складе ты будешь писать в любом случае.

G>Идемпотентной такую операцию уже не сделаешь и автоповтор со стороны клиента тоже.

Почему? Присваиваешь заявке на бронирование uuid и долбишь склад до посинения.

G>Короче сплошные недостатки и ноль преимуществ

Угу-угу.

G>·>Да не важно. Подставь любую другую очень "нужную" фичу, которая есть в svn но нет в git.

G>Так в том и дело, что у СВНа не было преимуществ перед Гит. У Гита был один недостаток — сложность использования, до сих пор два из трех программистов не умеют в гит.
Многие носились с номерами ревизий. Коммит 23414 выглядит на порядок лучше, чем 21f0c36f3cef976789d9c65c16198a7c14f7b272. А ещё отдельные чекауты подветок, локи, явные переименования, и т.п. Многие заявляют "нам нужно".

G>·>Суть моей аналогии, что твоё "нам нужно [xxx]" ты исходишь не из постановки бизнес-задачи, а из конкретного решения в виде монолитной архитектуры и гигантской всемогущей субд в виде одного процесса.

G>СУБД умеет ровно то, что у умеет — Атомарные изолированные транзакции, сохраняющие согласованность данных и гарантирующие надежность.
G>Любые реализации таких гарантий вручную дают менее надежный и менее производительный код, который не имеет преимуществ. Никакая теория и философия еще ни разу никому помогли сделать лучше транзакций в БД там, где транзакции решают проблему.
Теория простая: CAP-теорема.

G>>>·>Да не важно. СУБД это просто такой готовый процесс.

G>>>Угу, процесс который умеет это делать, в отличие от твоего процесса, который по умолчанию не умеет и надо прям поприседать чтобы умел.
G>·>Речь о другом — процесс не один. Можно иметь две субд, в каждой свои локальные транзакции.
G>И что это даст? Неатомарное, несогласованное, неизолированное изменение данных? В чем смысл?
Чтоб работало.

G>>>>>Как это связано? Если они все приходят в итоге на одни сервер? бд например, то задача решаема.

G>Один кластер — один мастер для записи и 15 реплик для чтения. Все транзакции меняющие данные ходят на один сервер.
Ок, почитал. Цитатки:
"continue to migrate, shardable, write-heavy workloads to sharded systems as Azure Cosmos DB".
"no longer allow adding new tables to the current PostgreSQL deployment".
"we're not ruling out sharding PostreSQL in the future"
"move complex join logic to the application layer"
"caching layer"

Хорошо подытожено: "It's Scaling PostgresNoSQL, not Scaling PostgreSQL, as this is full of NoSQL solutions: eventually consistent, cascading read replicas, sharding for write-heavy workloads, offload to purpose-built CosmosDB, lazy writes, cache limiting, no new create/alter table, schemaless, avoid complex multi-table joins..."
Но в главном-то ты прав: "все приходят в итоге на одни сервер".

G>·>Он может просто лечь, и без нагрузки.

G>Сам? Просто так? Реплик нет? Админов нет? Какой смысл это рассматривать как реалистичный сценарий?
G>Ну даже допустим что у вас сервер может лечь, вы его надежность оцениваете как число X в интервале (0;1) — оба конца не включены.
G>Если рассмотреть сценарий выше с покупкой товаров в ОЗОН и разнесением транзакций на два сервера, то общая надежность будет равна X^2, что строго меньше Х. То есть надежность двух серверов ниже.
Ты, видимо, сервер и сервис путаешь. Один сервис может работать как несколько инстансов, нет требования монолитности. И внезапно надёжность сервиса выше, если он работает на нескольких серверах.

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

G>·>Я не понимаю как "пользователь плюёт" соотносится с "не продадим больше".
G>Это относится к недостаткам решения. Так как "пользователь плюет" это потеря денег. Потому что дальше пользователь идет на другой маркетплейс и к тебе возможно уже не возвращается никогда
"резервирование сделаем транзакционно" не решает проблему "пользователь плюет".

G>>>Ту самую, которую ты непонятно как хочешь решить.

G>·>Это какую?
G>Чтобы пользователь придя за своим товаром получил его.
Для этого не требуется обновлять корзину и склад в одной транзакции.

G>·>Почему? Напомню контекст: МСА.

G>Выше все описал. Без МСА получается лучше.
Не получается. Проще — да, но по многим другим параметрам — хуже.

G>>>Ага, и в этом случае статус может стать "зарезервирован", а остаток на складе не уменьшится, и ты будешь разговаривать с недовольными покупателями.

G>·>Пока сервис склада не вернёт ответ "резервация прошла успешно" мы не обновляем статус заказа в сервисе заказов.
G>Тогда в этом нет никакого смысла, потому что недостатки есть, а преимуществ нет.
Можно продолжать работать с корзиной, например, позволять добавлять товары ещё, обновлять адрес доставки, применять скидки и купоны и т.п.

G>Напомню что ОпенАИ не использует распределенные транзакции и живет на одном кластере, то есть все записи приходят в один мастер.

Угу-угу.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 11.02.2026 12:54 · . Предыдущая версия . Еще …
Отредактировано 11.02.2026 12:49 · . Предыдущая версия .
Отредактировано 11.02.2026 12:05 · . Предыдущая версия .
Re[25]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 12.02.26 00:14
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>>>·>А откуда возникла такая задача "транзакционно обновить состояние корзины с резервов на складе"? По-моему, ты путаешь цель и средство.

G>>>>Конкретно это из озона.
G>>·>Это ты описываешь конкретное решение некой задачи. Сама задача-то в чём?
G>>Не в курсе чем озон занимается?
·>Судя по твоим заявлениям: транзакционно обновляют состояние корзины с резервов на складе.
Это часть того, что они делают.

G>>В основном товары со склада продает. Вот и надо продать не больше чем есть.

·>Для этого нет необходимости транзакционно обновлять состояние корзины с резервов на складе.
Ты не понимаешь суть задачи

две сущности Order (id, status, lines(product_id, q)) и Stock (product_id, reserverd, limit). При смене статуса в Order мы меняем состояние резервов в Stock.
При подтверждении заказа — увеличиваем reserved в stock для каждого line в order. А при отмене уменьшаем.

Если мы это не делаем в одной транзакции, то как ты потом уменьшишь, если статус не удалось сменить?
Сохранишь копию order в то же базе в той же тразакции, да?

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


G>>·>Для решения этой задачи нет необходимости обновлять корзину и склад в одной транзакции. Достаточно их обновить последовательно — вначале склад, потом корзину, в разных транзакциях.

G>>Тогда у нас нет никакого выигрыша от того, что это разные транзакции в разных базах. В сумме это будет работать медленнее чем в одной.
·>Зато будет работать хотя бы.
Не лучше, чем в одной базе. Прям строго математически не лучше.

G>>Более того, возможен сценарий когда первая выполнилась, а вторая отвалилась, просто по таймауту. Тогда товар на складе забронирован, а статус корзины не поменялся. Нужно писать код для отката.

·>Это эквивалентно ситуации: клиент наполнил корзину и ушел плюнув, потому что левая пятка зачесалась. Код отката брони на складе ты будешь писать в любом случае.
Не эквивалентно и не придется такое писать. Это твои фантазии


G>>Идемпотентной такую операцию уже не сделаешь и автоповтор со стороны клиента тоже.

·>Почему? Присваиваешь заявке на бронирование uuid и долбишь склад до посинения.
То ест мало того, что для обработки отмены ты вынужден будешь сохранить почти весь order в в той же транзакции, что и обновление остатков, так еще и дополнишь его полем ключа идемпотентности


G>>·>Да не важно. Подставь любую другую очень "нужную" фичу, которая есть в svn но нет в git.

G>>Так в том и дело, что у СВНа не было преимуществ перед Гит. У Гита был один недостаток — сложность использования, до сих пор два из трех программистов не умеют в гит.
·>Многие носились с номерами ревизий. Коммит 23414 выглядит на порядок лучше, чем 21f0c36f3cef976789d9c65c16198a7c14f7b272. А ещё отдельные чекауты подветок, локи, явные переименования, и т.п. Многие заявляют "нам нужно".
Я уверен что и сейчас svn найдет сторонников, потому что он все еще в разы проще git. Аргументы будут самые неразумные.


G>>·>Суть моей аналогии, что твоё "нам нужно [xxx]" ты исходишь не из постановки бизнес-задачи, а из конкретного решения в виде монолитной архитектуры и гигантской всемогущей субд в виде одного процесса.

G>>СУБД умеет ровно то, что у умеет — Атомарные изолированные транзакции, сохраняющие согласованность данных и гарантирующие надежность.
G>>Любые реализации таких гарантий вручную дают менее надежный и менее производительный код, который не имеет преимуществ. Никакая теория и философия еще ни разу никому помогли сделать лучше транзакций в БД там, где транзакции решают проблему.
·>Теория простая: CAP-теорема.
Пока ты пишешь на один сервер тебя cap вообще не интересует.

G>>>>·>Да не важно. СУБД это просто такой готовый процесс.

G>>>>Угу, процесс который умеет это делать, в отличие от твоего процесса, который по умолчанию не умеет и надо прям поприседать чтобы умел.
G>>·>Речь о другом — процесс не один. Можно иметь две субд, в каждой свои локальные транзакции.
G>>И что это даст? Неатомарное, несогласованное, неизолированное изменение данных? В чем смысл?
·>Чтоб работало.
Только оно не рабоает.

G>>>>>>Как это связано? Если они все приходят в итоге на одни сервер? бд например, то задача решаема.

G>>Один кластер — один мастер для записи и 15 реплик для чтения. Все транзакции меняющие данные ходят на один сервер.
·>Ок, почитал. Цитатки:
·>"continue to migrate, shardable, write-heavy workloads to sharded systems as Azure Cosmos DB".
·>"no longer allow adding new tables to the current PostgreSQL deployment".
·>"we're not ruling out sharding PostreSQL in the future"
·>"move complex join logic to the application layer"
·>"caching layer"
И? они все еще на Postgres с одним мастером, и все работает
Как-то работает, и CAP не мешает.

А все что они рассматривают никак не противоречит сказанному выше.

·>Хорошо подытожено: "It's Scaling PostgresNoSQL, not Scaling PostgreSQL, as this is full of NoSQL solutions: eventually consistent, cascading read replicas, sharding for write-heavy workloads, offload to purpose-built CosmosDB, lazy writes, cache limiting, no new create/alter table, schemaless, avoid complex multi-table joins..."

·>Но в главном-то ты прав: "все приходят в итоге на одни сервер".
Там где не нужна целостность всегда можно вынести. Но мы же рассматриваем задачу где она нужна.

G>>·>Он может просто лечь, и без нагрузки.

G>>Сам? Просто так? Реплик нет? Админов нет? Какой смысл это рассматривать как реалистичный сценарий?
G>>Ну даже допустим что у вас сервер может лечь, вы его надежность оцениваете как число X в интервале (0;1) — оба конца не включены.
G>>Если рассмотреть сценарий выше с покупкой товаров в ОЗОН и разнесением транзакций на два сервера, то общая надежность будет равна X^2, что строго меньше Х. То есть надежность двух серверов ниже.
·>Ты, видимо, сервер и сервис путаешь. Один сервис может работать как несколько инстансов, нет требования монолитности. И внезапно надёжность сервиса выше, если он работает на нескольких серверах.
Это ты путаешь stateless и stateful. В stateless сервисе ты можешь поднять несколько инстансов на разных серверах и если один перестанет отвечать другие смогут ответить.
Но когда мы рассматриваем stateful (а база данных это statful сервис), то при нескольких экземплярах мы наталкиваемся на CAP ограничения — мы можем или терять консистентность (что запрещено по условиям задачи) или доступность. Причем потерю доступности можно посчитать. Доступность системы из двух stateful узлов равна произведению доступности серверов, которая меньше доступности одного сервера. Априори считаем все серверы имеют одинаковую доступность.
Поэтому чтобы не заниматься сложной схемой отказоустойчивости и распределенными транзакциями выбирают обычную master-slave репликацию, когда все записи приходят в одну ноду.

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

G>>·>Я не понимаю как "пользователь плюёт" соотносится с "не продадим больше".
G>>Это относится к недостаткам решения. Так как "пользователь плюет" это потеря денег. Потому что дальше пользователь идет на другой маркетплейс и к тебе возможно уже не возвращается никогда
·>"резервирование сделаем транзакционно" не решает проблему "пользователь плюет".
Конечно решает, потому что пользователь после резервации на складе точно получит свой заказ.

G>>>>Ту самую, которую ты непонятно как хочешь решить.

G>>·>Это какую?
G>>Чтобы пользователь придя за своим товаром получил его.
·>Для этого не требуется обновлять корзину и склад в одной транзакции.
Я уже выше описал почему требуется. Не повторяй эту глупость уже

G>>·>Почему? Напомню контекст: МСА.

G>>Выше все описал. Без МСА получается лучше.
·>Не получается. Проще — да, но по многим другим параметрам — хуже.
Ты делаешь утвреждения, но даже не пытаешься из доказывать. Думаю просто потому что у тебя нет объективных аргументов.

G>>>>Ага, и в этом случае статус может стать "зарезервирован", а остаток на складе не уменьшится, и ты будешь разговаривать с недовольными покупателями.

G>>·>Пока сервис склада не вернёт ответ "резервация прошла успешно" мы не обновляем статус заказа в сервисе заказов.
G>>Тогда в этом нет никакого смысла, потому что недостатки есть, а преимуществ нет.
·>Можно продолжать работать с корзиной, например, позволять добавлять товары ещё, обновлять адрес доставки, применять скидки и купоны и т.п.
Лол, а зачем?
Re[26]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 12.02.26 12:48
Оценка: -1
Здравствуйте, gandjustas, Вы писали:


G>·>Судя по твоим заявлениям: транзакционно обновляют состояние корзины с резервов на складе.

G>Это часть того, что они делают.
Ещё раз. Это не задача, а конкретное решение. С таким же смыслом можно заявлять, что они, например, логи пишут и байты по проводам передают.

G>>>В основном товары со склада продает. Вот и надо продать не больше чем есть.

G>·>Для этого нет необходимости транзакционно обновлять состояние корзины с резервов на складе.
G>Ты не понимаешь суть задачи
G>две сущности Order (id, status, lines(product_id, q)) и Stock (product_id, reserverd, limit). При смене статуса в Order мы меняем состояние резервов в Stock.
G>При подтверждении заказа — увеличиваем reserved в stock для каждого line в order. А при отмене уменьшаем.
G>Если мы это не делаем в одной транзакции, то как ты потом уменьшишь, если статус не удалось сменить?
Так же, как если клиент набрал кучу товаров, они зарезервировались, но не смог оплатить. Резерв нужно откатывать.

G>Сохранишь копию order в то же базе в той же тразакции, да?

G>Ты наверное начнешь разговор что так делать не надо. Но озону надо. Потому что разница limit-reserverd отображается в интерфейсе приложения как "сколько осталось" и это отображается прямо в результатах поиска, то есть надо быстро получать это число для любого количества товаров.
Сохранять копии? Конечно, не надо.

G>·>Зато будет работать хотя бы.

G>Не лучше, чем в одной базе. Прям строго математически не лучше.
В крупных магазинах такое вообще работать не будет.

G>·>Это эквивалентно ситуации: клиент наполнил корзину и ушел плюнув, потому что левая пятка зачесалась. Код отката брони на складе ты будешь писать в любом случае.

G>Не эквивалентно и не придется такое писать. Это твои фантазии
Как не придётся? Что делать, когда корзина создана, товары зарезервированы, а пользователь закрыл браузер и ушел с концами?

G>>>Идемпотентной такую операцию уже не сделаешь и автоповтор со стороны клиента тоже.

G>·>Почему? Присваиваешь заявке на бронирование uuid и долбишь склад до посинения.
G>То ест мало того, что для обработки отмены ты вынужден будешь сохранить почти весь order в в той же транзакции, что и обновление остатков, так еще и дополнишь его полем ключа идемпотентности
Зачем его сохранять? Заявка — это сообщение. Ты вообще что-ли не понимаешь как делают идемпотентность?

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

G>·>Многие носились с номерами ревизий. Коммит 23414 выглядит на порядок лучше, чем 21f0c36f3cef976789d9c65c16198a7c14f7b272. А ещё отдельные чекауты подветок, локи, явные переименования, и т.п. Многие заявляют "нам нужно".
G>Я уверен что и сейчас svn найдет сторонников, потому что он все еще в разы проще git. Аргументы будут самые неразумные.
А совсем проще всего — вообще не использовать системы контроля версий. И что? Как и всегда в жизни: у любой, даже самой сложной задачи есть очевидное, простое, понятное, неправильное решение.

G>>>СУБД умеет ровно то, что у умеет — Атомарные изолированные транзакции, сохраняющие согласованность данных и гарантирующие надежность.

G>>>Любые реализации таких гарантий вручную дают менее надежный и менее производительный код, который не имеет преимуществ. Никакая теория и философия еще ни разу никому помогли сделать лучше транзакций в БД там, где транзакции решают проблему.
G>·>Теория простая: CAP-теорема.
G>Пока ты пишешь на один сервер тебя cap вообще не интересует.
Это плохого архитектора вообще cap не интересует. Вот правда теореме пофиг, интересует она кого-то или нет, она работает всегда.

G>>>·>Речь о другом — процесс не один. Можно иметь две субд, в каждой свои локальные транзакции.

G>>>И что это даст? Неатомарное, несогласованное, неизолированное изменение данных? В чем смысл?
G>·>Чтоб работало.
G>Только оно не рабоает.
Ну ты хочешь чтобы я тут описывал как строятся МСА системы? Мне очень лень, но можешь почитать, например, тут: https://medium.com/@CodeWithTech/the-saga-design-pattern-coordinating-long-running-transactions-in-distributed-systems-edbc9b9a9116

G>И? они все еще на Postgres с одним мастером, и все работает

В смысле? У них postgres в режиме deprecated. Т.к. дорого всё распилить, т.к. надо переделывать всё. Сказано же: "no longer allow adding new tables". Кто-то гениальный запилил им монолит, вот теперь страдают.

G>·>Но в главном-то ты прав: "все приходят в итоге на одни сервер".

G>Там где не нужна целостность всегда можно вынести. Но мы же рассматриваем задачу где она нужна.
Ну у них не получилось. Все эти многослойные кеши и реплики — это уже отказ от целостности.

G>·>Ты, видимо, сервер и сервис путаешь. Один сервис может работать как несколько инстансов, нет требования монолитности. И внезапно надёжность сервиса выше, если он работает на нескольких серверах.

G>Это ты путаешь stateless и stateful. В stateless сервисе ты можешь поднять несколько инстансов на разных серверах и если один перестанет отвечать другие смогут ответить.
G>Но когда мы рассматриваем stateful (а база данных это statful сервис), то при нескольких экземплярах мы наталкиваемся на CAP ограничения — мы можем или терять консистентность (что запрещено по условиям задачи) или доступность.
В задаче корзина-склад — достаточно eventual consistency.

G>Причем потерю доступности можно посчитать. Доступность системы из двух stateful узлов равна произведению доступности серверов, которая меньше доступности одного сервера. Априори считаем все серверы имеют одинаковую доступность.

G>Поэтому чтобы не заниматься сложной схемой отказоустойчивости и распределенными транзакциями выбирают обычную master-slave репликацию, когда все записи приходят в одну ноду.
Такое требуется в очень редких задачах. И там обычно используют какой-нибудь event sourcing, а не acid субд.

G>>>Это относится к недостаткам решения. Так как "пользователь плюет" это потеря денег. Потому что дальше пользователь идет на другой маркетплейс и к тебе возможно уже не возвращается никогда

G>·>"резервирование сделаем транзакционно" не решает проблему "пользователь плюет".
G>Конечно решает, потому что пользователь после резервации на складе точно получит свой заказ.
"пользователь плюёт" — это означает что он не ничего хочет оплачивать и уж тем более получать свой заказ. Но зарезервированные товары хочет получить совершенно другой пользователь.

G>>>Чтобы пользователь придя за своим товаром получил его.

G>·>Для этого не требуется обновлять корзину и склад в одной транзакции.
G>Я уже выше описал почему требуется. Не повторяй эту глупость уже
Ещё раз. Если выполнять вначале транзакцию резервации, потом другую транзакцию для смены статуса заказа — то мы гарантируем, что товар будет в наличии после оплаты заказа.
Существует только проблема того, что товар зарезервирован, но не оплачен. Нужен ещё процесс отмены резерва. И твоя транзакция ничем не помогает, никакую проблему она не решает.

G>·>Можно продолжать работать с корзиной, например, позволять добавлять товары ещё, обновлять адрес доставки, применять скидки и купоны и т.п.

G>Лол, а зачем?
Ага. Раз такого в Озоне нет, то это никому не нужно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 12.02.2026 12:55 · . Предыдущая версия .
Re[27]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 12.02.26 22:39
Оценка:
Здравствуйте, ·, Вы писали:



G>>>>В основном товары со склада продает. Вот и надо продать не больше чем есть.

G>>·>Для этого нет необходимости транзакционно обновлять состояние корзины с резервов на складе.
G>>Ты не понимаешь суть задачи
G>>две сущности Order (id, status, lines(product_id, q)) и Stock (product_id, reserverd, limit). При смене статуса в Order мы меняем состояние резервов в Stock.
G>>При подтверждении заказа — увеличиваем reserved в stock для каждого line в order. А при отмене уменьшаем.
G>>Если мы это не делаем в одной транзакции, то как ты потом уменьшишь, если статус не удалось сменить?
·>Так же, как если клиент набрал кучу товаров, они зарезервировались, но не смог оплатить. Резерв нужно откатывать.
Нужно, это другой сценарий. Далеко не то же самое, что откат незавершенной резервации.


G>>Сохранишь копию order в то же базе в той же тразакции, да?

G>>Ты наверное начнешь разговор что так делать не надо. Но озону надо. Потому что разница limit-reserverd отображается в интерфейсе приложения как "сколько осталось" и это отображается прямо в результатах поиска, то есть надо быстро получать это число для любого количества товаров.
·>Сохранять копии? Конечно, не надо.
А что тогда делать для отката незавершенной резервации?

G>>·>Зато будет работать хотя бы.

G>>Не лучше, чем в одной базе. Прям строго математически не лучше.
·>В крупных магазинах такое вообще работать не будет.
Ты можешь как-то обосновать свои слова?

G>>·>Это эквивалентно ситуации: клиент наполнил корзину и ушел плюнув, потому что левая пятка зачесалась. Код отката брони на складе ты будешь писать в любом случае.

G>>Не эквивалентно и не придется такое писать. Это твои фантазии
·>Как не придётся? Что делать, когда корзина создана, товары зарезервированы, а пользователь закрыл браузер и ушел с концами?
Это другой сценарий, мы его сейчас не рассматриваем.
Не надо придумывать новые кейсы, придумай как сделать хорошо один. Потом будем другие рассматривать.
Пока у тебя ничего внятного не получилось.

G>>>>Идемпотентной такую операцию уже не сделаешь и автоповтор со стороны клиента тоже.

G>>·>Почему? Присваиваешь заявке на бронирование uuid и долбишь склад до посинения.
G>>То ест мало того, что для обработки отмены ты вынужден будешь сохранить почти весь order в в той же транзакции, что и обновление остатков, так еще и дополнишь его полем ключа идемпотентности
·>Зачем его сохранять? Заявка — это сообщение. Ты вообще что-ли не понимаешь как делают идемпотентность?
Я понимаю как делают идемпотентность. Я не понимаю как ты её хочешь сделать.
Можешь привести пример кода?


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

G>>·>Многие носились с номерами ревизий. Коммит 23414 выглядит на порядок лучше, чем 21f0c36f3cef976789d9c65c16198a7c14f7b272. А ещё отдельные чекауты подветок, локи, явные переименования, и т.п. Многие заявляют "нам нужно".
G>>Я уверен что и сейчас svn найдет сторонников, потому что он все еще в разы проще git. Аргументы будут самые неразумные.
·>А совсем проще всего — вообще не использовать системы контроля версий.
Альтернатива какая? передавать архивами и мерить вручную?



G>>>>·>Речь о другом — процесс не один. Можно иметь две субд, в каждой свои локальные транзакции.

G>>>>И что это даст? Неатомарное, несогласованное, неизолированное изменение данных? В чем смысл?
G>>·>Чтоб работало.
G>>Только оно не рабоает.
·>Ну ты хочешь чтобы я тут описывал как строятся МСА системы? Мне очень лень, но можешь почитать, например, тут: https://medium.com/@CodeWithTech/the-saga-design-pattern-coordinating-long-running-transactions-in-distributed-systems-edbc9b9a9116
Не надо ссылок, просто приведи пример кода как бы ты сделал резервацию заказа.


G>>И? они все еще на Postgres с одним мастером, и все работает

·>В смысле? У них postgres в режиме deprecated. Т.к. дорого всё распилить, т.к. надо переделывать всё. Сказано же: "no longer allow adding new tables". Кто-то гениальный запилил им монолит, вот теперь страдают.
Да уж, страдают....

G>>·>Но в главном-то ты прав: "все приходят в итоге на одни сервер".

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


G>>·>Ты, видимо, сервер и сервис путаешь. Один сервис может работать как несколько инстансов, нет требования монолитности. И внезапно надёжность сервиса выше, если он работает на нескольких серверах.

G>>Это ты путаешь stateless и stateful. В stateless сервисе ты можешь поднять несколько инстансов на разных серверах и если один перестанет отвечать другие смогут ответить.
G>>Но когда мы рассматриваем stateful (а база данных это statful сервис), то при нескольких экземплярах мы наталкиваемся на CAP ограничения — мы можем или терять консистентность (что запрещено по условиям задачи) или доступность.
·>В задаче корзина-склад — достаточно eventual consistency.
Покажи пример кода


G>>Причем потерю доступности можно посчитать. Доступность системы из двух stateful узлов равна произведению доступности серверов, которая меньше доступности одного сервера. Априори считаем все серверы имеют одинаковую доступность.

G>>Поэтому чтобы не заниматься сложной схемой отказоустойчивости и распределенными транзакциями выбирают обычную master-slave репликацию, когда все записи приходят в одну ноду.
·>Такое требуется в очень редких задачах. И там обычно используют какой-нибудь event sourcing, а не acid субд.
Именно, а в более простых случаях вполне достаточно одного мастера. Вот у OpenAI простой случай. Я правда не представляю у кого он не простой.

G>>>>Чтобы пользователь придя за своим товаром получил его.

G>>·>Для этого не требуется обновлять корзину и склад в одной транзакции.
G>>Я уже выше описал почему требуется. Не повторяй эту глупость уже
·>Ещё раз. Если выполнять вначале транзакцию резервации, потом другую транзакцию для смены статуса заказа — то мы гарантируем, что товар будет в наличии после оплаты заказа.
·>Существует только проблема того, что товар зарезервирован, но не оплачен. Нужен ещё процесс отмены резерва. И твоя транзакция ничем не помогает, никакую проблему она не решает.
Да ты сначала сделай процесс резервирования надежным, потом про оплату поговорим.

G>>·>Можно продолжать работать с корзиной, например, позволять добавлять товары ещё, обновлять адрес доставки, применять скидки и купоны и т.п.

G>>Лол, а зачем?
·>Ага. Раз такого в Озоне нет, то это никому не нужно.
Так мы сейчас про конкретную задачу говорим, как в озоне.
Re[26]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 13.02.26 09:52
Оценка: 1 (1)
Здравствуйте, gandjustas, Вы писали:

G>Сохранишь копию order в то же базе в той же тразакции, да?

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

G>Ты наверное начнешь разговор что так делать не надо. Но озону надо. Потому что разница limit-reserverd отображается в интерфейсе приложения как "сколько осталось" и это отображается прямо в результатах поиска, то есть надо быстро получать это число для любого количества товаров.

Микросервисы никак не мешают быстрому показу remaining — скорее наоборот, т.к. RDBMS склада вообще не обрабатывает никакой нагрузки, кроме вот этого вот get remaining и reserve stock / unreserve stock / top-up stock.
Кроме того, её можно шардить — т.к. сами товары друг на друга никак не завязаны, можно держать первые 10000 позиций в одном сервере, вторые 10000 — в другом, и так далее.

Не, я в курсе, что как только мы начнём джойнить эту информацию с какими-нибудь запросами по другой части домена (типа "робот-пылесос в пределах 20000 рублей с доставкой до послезавтра и средним баллом по отзывам не ниже 4.9"), то МСА со страшной силой сольёт монолиту. Вот только если мы всё же уперлись в потолок монолита, то возможность получить 2х перформанс ценой распиливания его на 8х частей может оказаться единственным выбором.

G>Не лучше, чем в одной базе. Прям строго математически не лучше.

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

G>>>Более того, возможен сценарий когда первая выполнилась, а вторая отвалилась, просто по таймауту. Тогда товар на складе забронирован, а статус корзины не поменялся. Нужно писать код для отката.

G>·>Это эквивалентно ситуации: клиент наполнил корзину и ушел плюнув, потому что левая пятка зачесалась. Код отката брони на складе ты будешь писать в любом случае.
G>Не эквивалентно и не придется такое писать. Это твои фантазии
Нет, откат писать не надо. Надо писать "накат". И это можно сделать один раз в инфраструктуре worflow-engine. Типа вот мы поднялись после сбоя и видим, что корзинка №42342342 была отправлена на резервирование, а результата резервирования нет. Значит, либо мы в прошлый раз не достучались до сервера (получили сonnection timeout), либо достучались да он упал до начала резервирования (отдал нам 5хх), либо упал после окончания резервирования (и отдал нам 5хх или connection reset by peer), либо всё нам отдал, да мы расплескали по пути и упали до коммита в локальную БД.
Во всех случаях мы тупо идём и повторяем резерв. При необходимости — сколько угодно раз, пока нам таки не удастся записать к себе "успех" либо "фейл". На практике, длинные даунтаймы тут бывают не чаще, чем даунтаймы у монолита. Только у монолита лежит вообще всё, а у МСА можно хотя бы в корзинку что-то складывать да отзывы читать.

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

Да, объекты, пересекающие границы сервисов, в МСА должны храниться на обеих сторонах. Это не обязательно одни и те же объекты, но у них должна быть общая проекция. В данном случае сторону склада не интересуют никакие подробности про способы доставки товара, розничные цены, или там демографию покупателя, но вот список артикулов и количеств, снабжённый уникальным ID, ей необходим. Ровно для того, чтобы когда к нему в следующий раз стукнутся с просьбой зарезервировать корзинку №42342342 он мог не делать повторный резерв, а сразу отдать 200 ok.

G>·>Теория простая: CAP-теорема.

G>Пока ты пишешь на один сервер тебя cap вообще не интересует.
Совершенно верно.

G>Только оно не рабоает.

Это слишком сильное утверждение в рамках данной дискуссии

G>Но когда мы рассматриваем stateful (а база данных это statful сервис), то при нескольких экземплярах мы наталкиваемся на CAP ограничения — мы можем или терять консистентность (что запрещено по условиям задачи) или доступность. Причем потерю доступности можно посчитать. Доступность системы из двух stateful узлов равна произведению доступности серверов, которая меньше доступности одного сервера. Априори считаем все серверы имеют одинаковую доступность.

Зато доступность строго согласованной системы из трёх stateful узлов уже выше, одного узла — при условии, что надёжность связей между ними выше определённого предела
CAP-теорема как раз и намекает на то, что если каналы не 100% надёжны (P), то получить 100% доступность невозможно даже для 100% надёжных узлов. Но этот тривиальный факт никак не помогает инженерному дизайну — потому что как вы будете поднимать доступность вашего единственного узла за пределы его нормативных способностей?
Вот картинка для доступности строго согласованного кластера из трёх узлов с доступностью Anode, связанных каналами с доступностью Alink:

Белый контур — это зона, где эта доступность выше доступности каждого из компонентов.

G>·>"резервирование сделаем транзакционно" не решает проблему "пользователь плюет".

G>Конечно решает, потому что пользователь после резервации на складе точно получит свой заказ.
В нашем случае пользователь после резервации на складе тоже точно получит свой заказ. Вся разница — в том, что если будет сбой системы во время заказа, то пользователь монолита до окончания сбоя будет получать 502, а пользователь МСА имеет шанс в это время увидеть спиннер "заказ резервируется....".
В любом случае результат резервирования станет известен только по окончанию сбоя — и точно так же, как в монолитном случае можно получить как подтверждение успеха, так и "извините, но другой покупатель успел зарезервировать товар до вас".

G>·>Для этого не требуется обновлять корзину и склад в одной транзакции.

G>Я уже выше описал почему требуется. Не повторяй эту глупость уже
При всём уважении — это не глупость, а вполне себе математическая реальность. Я выше написал, как именно это работает.
G>Лол, а зачем?
Затем, что в реальном приложении — сотни бизнес-сценариев. Остановка на техобслуживание одного из сервисов задержит только те сценарии, которые проходят через него. И если этот сервис достаточно простой и маленький, то его рестарт не будет занимать десятки минут. Таким образом, perceived availability может оказаться значительно выше, чем у монолита. Ну вот, абстрактно, мы на пять минут отключили банковское ядро, которое собственно проводит платежи. Если всё сделано по уму, то пользователи это увидят только как "странно, я вроде по СБП деньги отправил, а у получателя телефон что-то не вибрирует". Если чуть хуже — то как "переводы пока недоступны, приходите позже". Если ещё хуже — то как "Непредвиденная ошибка. Перезапустите приложение или зайдите позже". И в любом из этих случаев люди, которые смотрят какую-нибудь там аналитику расходов, или остатки по вкладам, или условия кредитов/страховок/етк не заметят вообще ничего. С их точки зрения никакого сбоя не было. А если нам нужно сделать то же самое в монолите, то опускать нужно примерно всё, и всё будет лежать сразу у всех пользователей.

Не факт, что это прямо плохо. Не факт, что это прямо настолько плохо, что стоит многократного роста вычислительной и когнитивной нагрузки, сопровождающего переход от монолита к (микро)-сервисам. Но для полноты картины имеет смысл рассматривать и такие сценарии, не пытаясь искусственно обесценить такие требования или там сочинять недостатки модели, которых в реальности нет.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[27]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 13.02.26 10:12
Оценка:
Здравствуйте, Sinclair, Вы писали:


G>>Не лучше, чем в одной базе. Прям строго математически не лучше.

S>Да, но с учётом нюансов, указанных выше. Пока мы не упёрлись в пределы монолита и при отсутствии административных причин, монолит будет строго математически лучше.
Стоит ещё уточнить, что "математически лучше" — это плохой критерий для выбора решения. Нужно ещё не забывать о физике. Было бы математически лучше, если б я взмахнул руками и полетел, но физику с гравитацией это не устраивает, разрешает только строго вниз летать.
Так и тут, математически лучше чтобы всё работало в одной центральной точке. Но пользователи, склад, корзина, товары, платежи, курьеры — это всё разные физически разделённые сущности.

ЗЫЖ Спасибо за хороший ответ. А то я уже устал ему отвечать
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 13.02.2026 10:24 · . Предыдущая версия .
Re[28]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 13.02.26 12:03
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>>>Если мы это не делаем в одной транзакции, то как ты потом уменьшишь, если статус не удалось сменить?

G>·>Так же, как если клиент набрал кучу товаров, они зарезервировались, но не смог оплатить. Резерв нужно откатывать.
G>Нужно, это другой сценарий. Далеко не то же самое,
В каком месте это не то же самое? Опять же. Ты рассматривай не одно конкретное решение одного конкретного сценария "транзакционно обновлять состояние", а изначальную бизнес-задачу: "продавать товары со склада клиентам". И выясняется что сценариев несколько, и они должны работать все.

G>что откат незавершенной резервации.

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

G>·>Сохранять копии? Конечно, не надо.

G>А что тогда делать для отката незавершенной резервации?
Для отката незавершенной резервации — ровно то же, что и для "если клиент набрал кучу товаров, они зарезервировались, но не смог оплатить."

G>·>В крупных магазинах такое вообще работать не будет.

G>Ты можешь как-то обосновать свои слова?
Могу, но лень. Можешь поискать как устроены всякие Амазоны с Ебеями. Или вообще погляди как какими-нибудь акциями какие-нибудь биржи торгуют.

G>·>Как не придётся? Что делать, когда корзина создана, товары зарезервированы, а пользователь закрыл браузер и ушел с концами?

G>Это другой сценарий, мы его сейчас не рассматриваем.
Этот сценарий является ответом на твой вопрос: "А что тогда делать для отката незавершенной резервации?".

G>Не надо придумывать новые кейсы, придумай как сделать хорошо один. Потом будем другие рассматривать.

G>Пока у тебя ничего внятного не получилось.
Мде. См. ответ от Sinclair.

G>·>Зачем его сохранять? Заявка — это сообщение. Ты вообще что-ли не понимаешь как делают идемпотентность?

G>Я понимаю как делают идемпотентность. Я не понимаю как ты её хочешь сделать.
G>Можешь привести пример кода?
Пример кода чего?

G>>>·>Многие носились с номерами ревизий. Коммит 23414 выглядит на порядок лучше, чем 21f0c36f3cef976789d9c65c16198a7c14f7b272. А ещё отдельные чекауты подветок, локи, явные переименования, и т.п. Многие заявляют "нам нужно".

G>>>Я уверен что и сейчас svn найдет сторонников, потому что он все еще в разы проще git. Аргументы будут самые неразумные.
G>·>А совсем проще всего — вообще не использовать системы контроля версий.
G>Альтернатива какая? передавать архивами и мерить вручную?
Типа того. Ведь это — самое простое. А мержить — совсем слишком сложно, ведь в разы проще договориться, кто какой файл когда меняет.

G>>>·>Чтоб работало.

G>>>Только оно не рабоает.
G>·>Ну ты хочешь чтобы я тут описывал как строятся МСА системы? Мне очень лень, но можешь почитать, например, тут: https://medium.com/@CodeWithTech/the-saga-design-pattern-coordinating-long-running-transactions-in-distributed-systems-edbc9b9a9116
G>Не надо ссылок, просто приведи пример кода как бы ты сделал резервацию заказа.
Ну вот там по ссылке как раз примеры кода.

G>>>И? они все еще на Postgres с одним мастером, и все работает

G>·>В смысле? У них postgres в режиме deprecated. Т.к. дорого всё распилить, т.к. надо переделывать всё. Сказано же: "no longer allow adding new tables". Кто-то гениальный запилил им монолит, вот теперь страдают.
G>Да уж, страдают....
Ну да, ну да. На самом деле они наслаждаются SEV-ами и outages. Ты статью-то читал? https://openai.com/index/scaling-postgresql/

G>>>Там где не нужна целостность всегда можно вынести. Но мы же рассматриваем задачу где она нужна.

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

G>·>В задаче корзина-склад — достаточно eventual consistency.

G>Покажи пример кода
Какого именно кода? Там по ссылке выше есть какие-то примеры.

G>·>Такое требуется в очень редких задачах. И там обычно используют какой-нибудь event sourcing, а не acid субд.

G>Именно, а в более простых случаях вполне достаточно одного мастера. Вот у OpenAI простой случай. Я правда не представляю у кого он не простой.
Если бы им было достаточно, они бы не начали переезжать на Cosmos DB.

G>Да ты сначала сделай процесс резервирования надежным, потом про оплату поговорим.

См. ответ от Sinclair.

G>>>·>Можно продолжать работать с корзиной, например, позволять добавлять товары ещё, обновлять адрес доставки, применять скидки и купоны и т.п.

G>>>Лол, а зачем?
G>·>Ага. Раз такого в Озоне нет, то это никому не нужно.
G>Так мы сейчас про конкретную задачу говорим, как в озоне.
Ты заявил "Тогда в этом нет никакого смысла". Конечно, если весь озон состоял из ровно одного сценария и решал ровно одну задачу, то смысла нет. Но, по-моему, "ранзакционно обновлять состояние корзины с резервов на складе" — это не единственное, чем занимается озон.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 13.02.2026 12:24 · . Предыдущая версия . Еще …
Отредактировано 13.02.2026 12:21 · . Предыдущая версия .
Отредактировано 13.02.2026 12:06 · . Предыдущая версия .
Re[27]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 13.02.26 14:38
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>Сохранишь копию order в то же базе в той же тразакции, да?

S>Техническое решение этой задачи для микросервисов уже обсуждалось. Да, оно в каком-то смысле "хуже", чем единая ACID-транзакция — у заказа появляется некое промежуточное состояние, которого не было в исходной реализации.
S>Но давайте прекратим делать вид, что этого решения нет, т.к. это состояние — эфемерное, и фактически время его жизни равно времени даунтайма сервиса склада с точки зрения сервиса корзинки.

ИМХО ACID-транзакция, изобретенная на другом уровне абстракции — все равно транзакция. Если говорить то том, какие транзакции стоит использовать — самопальные или предоставляемые БД, то любой вменяемый разработчик выберет второй вариант.
Единственная причина применять самопал — когда нет возможности применить транзакции в БД.


Чтобы говорить об одном и том же давай зафиксируем детали о которых шла речь:
— Я предлагаю состояние заказа менять в одной транзакции с обновлением остатков. Заказы и остатки естественно должны быть в одной базе
— МСА предлагает сделать две базы, где заказы лежат в одной, а заказы в другой.
Написать код вида:
1. обнови остатки в базе А
2. обнови статус заказа в базе Б
3. если появилась ошибка, то откати обновление в базе А

Если код падает по между шагами 2 и 3, то в базе остается несогласованное состояние.
Значит вместе самим кодом резервирования заказа надо написать еще фоновую задачу, которая откатывает незавершенные резервы.
Я проделал аналогичное в рамках статьи на хабре, результаты неутешительные — https://habr.com/ru/articles/963120/ см раздел "рукопашные транзакции".


Но у нас транзакции не изолированные, то есть межу 1 и 2 может вклиниться изменение, которое обновит заказ.
Это значит что для корректного кода отказа как в фоне, так и в п3 надо сохранять "слепок" заказа на шаге 1.
Этот слепок — это в чистом виде wal log.



G>>Ты наверное начнешь разговор что так делать не надо. Но озону надо. Потому что разница limit-reserverd отображается в интерфейсе приложения как "сколько осталось" и это отображается прямо в результатах поиска, то есть надо быстро получать это число для любого количества товаров.

S>Микросервисы никак не мешают быстрому показу remaining — скорее наоборот, т.к. RDBMS склада вообще не обрабатывает никакой нагрузки, кроме вот этого вот get remaining и reserve stock / unreserve stock / top-up stock.
Получение остатков это запрос к одной таблице. Ему никакая МСА не помешает.

S>Кроме того, её можно шардить — т.к. сами товары друг на друга никак не завязаны, можно держать первые 10000 позиций в одном сервере, вторые 10000 — в другом, и так далее.

Шардить можно и без МСА. Это вообще ортогональные вещи.

S>Не, я в курсе, что как только мы начнём джойнить эту информацию с какими-нибудь запросами по другой части домена (типа "робот-пылесос в пределах 20000 рублей с доставкой до послезавтра и средним баллом по отзывам не ниже 4.9"), то МСА со страшной силой сольёт монолиту. Вот только если мы всё же уперлись в потолок монолита, то возможность получить 2х перформанс ценой распиливания его на 8х частей может оказаться единственным выбором.

Мы же упираемся не в монолит, а в производительность одного сервера БД. Тут есть несколько решений:
1) Кэши для чтения, чтобы нагрузка на БД не прилетала вообще.
2) Чтение из реплик, чтобы нагрузка на мастер не прилетала там где допустимо отдавать слегка устаревшие данные.
3) Партицирование таблиц — это как шардирование, только в пределах одного сервера БД, чтобы нагрузку на запись распределять по разным дискам.
И только когда все возможности исчерпаны — делать шардирование.
ИМХО в ни у одного веб-сайта или приложения нет такой нагрузки чтобы шардирование было оправдано.

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


G>>Не лучше, чем в одной базе. Прям строго математически не лучше.

S>Да, но с учётом нюансов, указанных выше. Пока мы не упёрлись в пределы монолита и при отсутствии административных причин, монолит будет строго математически лучше.
Этот потолок сильно выше, чем кажется. При правильном подходе к проектированию до него доберутся единицы, а остальные от сложности проиграют только.

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

G>>>>Более того, возможен сценарий когда первая выполнилась, а вторая отвалилась, просто по таймауту. Тогда товар на складе забронирован, а статус корзины не поменялся. Нужно писать код для отката.

G>>·>Это эквивалентно ситуации: клиент наполнил корзину и ушел плюнув, потому что левая пятка зачесалась. Код отката брони на складе ты будешь писать в любом случае.
G>>Не эквивалентно и не придется такое писать. Это твои фантазии
S>Нет, откат писать не надо. Надо писать "накат". И это можно сделать один раз в инфраструктуре worflow-engine.
Допустим

S>Типа вот мы поднялись после сбоя и видим, что корзинка №42342342 была отправлена на резервирование, а результата резервирования нет.

А как мы узнаем что его нет?

S>Значит, либо мы в прошлый раз не достучались до сервера (получили сonnection timeout), либо достучались да он упал до начала резервирования (отдал нам 5хх), либо упал после окончания резервирования (и отдал нам 5хх или connection reset by peer), либо всё нам отдал, да мы расплескали по пути и упали до коммита в локальную БД.

S>Во всех случаях мы тупо идём и повторяем резерв. При необходимости — сколько угодно раз, пока нам таки не удастся записать к себе "успех" либо "фейл". На практике, длинные даунтаймы тут бывают не чаще, чем даунтаймы у монолита. Только у монолита лежит вообще всё, а у МСА можно хотя бы в корзинку что-то складывать да отзывы читать.
А пользователь все это время ждет?
А если он не дождался и ушел?
А если связь между пользователем и приложением пропала?
А если пользователь ушлый и пока в одной вкладе крутится ожидание открыл сайт в другой вкладке и пошел что-то менять?


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

S>Да, объекты, пересекающие границы сервисов, в МСА должны храниться на обеих сторонах. Это не обязательно одни и те же объекты, но у них должна быть общая проекция. В данном случае сторону склада не интересуют никакие подробности про способы доставки товара, розничные цены, или там демографию покупателя, но вот список артикулов и количеств, снабжённый уникальным ID, ей необходим. Ровно для того, чтобы когда к нему в следующий раз стукнутся с просьбой зарезервировать корзинку №42342342 он мог не делать повторный резерв, а сразу отдать 200 ok.
Я понимаю, что при высокоуровневом взгляде кажется что это все просто, написать три-четыре строки в воркфлоу и будет хорошо. А на практике для обеспечения целостности нужны десятки строк кода и дополнительные данные хранить (что увеличивает нагрузку какбы).
И самое главное — ради чего это все? Чтобы не упереться в мифический "потолок монолита". С МСА этот потолок окажется очень низко.


S>Вот картинка для доступности строго согласованного кластера из трёх узлов с доступностью Anode, связанных каналами с доступностью Alink:

S>Белый контур — это зона, где эта доступность выше доступности каждого из компонентов.
Это в каком контексте?
Насколько понимаю картинка эта для случая когда:
1) Есть несколько экземпляров ОДНОЙ И ТОЙ ЖЕ базы
2) Клиент подключается к ЛЮБОМУ экземпляру и может менять ЛЮБЫЕ данные
То есть мультиматер-кластер.

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


G>>·>"резервирование сделаем транзакционно" не решает проблему "пользователь плюет".

G>>Конечно решает, потому что пользователь после резервации на складе точно получит свой заказ.
S>В нашем случае пользователь после резервации на складе тоже точно получит свой заказ. Вся разница — в том, что если будет сбой системы во время заказа, то пользователь монолита до окончания сбоя будет получать 502, а пользователь МСА имеет шанс в это время увидеть спиннер "заказ резервируется....".
Это вопрос реализации фронта. Мы при любой архитектуре можем вынести повтор именно на фронт.

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

Тогда вопрос — если не видно разницы, то зачем платить больше?
Я скинул ссылку на статью выше, там рукопашные транзакции почти в два раза уронили производительность.

G>>·>Для этого не требуется обновлять корзину и склад в одной транзакции.

G>>Я уже выше описал почему требуется. Не повторяй эту глупость уже
S>При всём уважении — это не глупость, а вполне себе математическая реальность. Я выше написал, как именно это работает.
Дьявол как всегда в деталях.

G>>Лол, а зачем?

S>Затем, что в реальном приложении — сотни бизнес-сценариев.
Я предлагаю на одном сконцентрироваться. Это же реальный сценарий.
Когда с ним закончим сможем посмотреть как эти подходы масштабировать.

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

Мы же рассматриваем сценарий когда у нас сценарий зависит от доступности двух серверов. На одно из них, а сразу двух. Их доступность равна произведению доступности обоих. А она будет меньше, чем доступность одного.

S>И если этот сервис достаточно простой и маленький, то его рестарт не будет занимать десятки минут. Таким образом, perceived availability может оказаться значительно выше, чем у монолита. Ну вот, абстрактно, мы на пять минут отключили банковское ядро, которое собственно проводит платежи. Если всё сделано по уму, то пользователи это увидят только как "странно, я вроде по СБП деньги отправил, а у получателя телефон что-то не вибрирует". Если чуть хуже — то как "переводы пока недоступны, приходите позже". Если ещё хуже — то как "Непредвиденная ошибка. Перезапустите приложение или зайдите позже". И в любом из этих случаев люди, которые смотрят какую-нибудь там аналитику расходов, или остатки по вкладам, или условия кредитов/страховок/етк не заметят вообще ничего. С их точки зрения никакого сбоя не было. А если нам нужно сделать то же самое в монолите, то опускать нужно примерно всё, и всё будет лежать сразу у всех пользователей.

Мы о чем говорим? О серверах приложений или о субд? СУБД в продах стоят в HA кластерах и спокойно выдерживают остановку одного из серверов. От силы 15-20 сек ожидания переезда мастера если сервак упал неожиданно.
Приложения можно нарезать на десятки отдельных модулей и запускать отдельно: в отдельных процессах, в модулях одного процесса — как удобно. Все что написано выше для них верно.
Re[28]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 13.02.26 15:56
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Чтобы говорить об одном и том же давай зафиксируем детали о которых шла речь:

G>- Я предлагаю состояние заказа менять в одной транзакции с обновлением остатков. Заказы и остатки естественно должны быть в одной базе
G>- МСА предлагает сделать две базы, где заказы лежат в одной, а заказы в другой.
G>Написать код вида:
G> 1. обнови остатки в базе А
G> 2. обнови статус заказа в базе Б
G> 3. если появилась ошибка, то откати обновление в базе А

G>Если код падает по между шагами 2 и 3, то в базе остается несогласованное состояние.

G>Значит вместе самим кодом резервирования заказа надо написать еще фоновую задачу, которая откатывает незавершенные резервы.
Ты упускаешь мою мысль, что этот код — не особенность МСА-реализации, а реальность бизнес-задачи. Просто в твоём монолитном решении ты игнорируешь эту проблему. С точки зрения бизнеса _изначальная_ задача выглядит так:
1. Пользователь создаёт заказ
2. Склад резервирует заказ
3. Пользователь завершает заказ

И вот когда ты опишешь задачу так, то совершенно ВНЕЗАПНО получается, что "надо написать еще фоновую задачу, которая откатывает незавершенные резервы" в любом случае, неважно — МСА у тебя или монолит.
МСА просто это сделал явным.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[28]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 14.02.26 06:32
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>ИМХО ACID-транзакция, изобретенная на другом уровне абстракции — все равно транзакция. Если говорить то том, какие транзакции стоит использовать — самопальные или предоставляемые БД, то любой вменяемый разработчик выберет второй вариант.

Нет, сделать ACID-транзакцию поверх ненадёжного соединения не получится.
G>Единственная причина применять самопал — когда нет возможности применить транзакции в БД.
Всё верно.

G>- Я предлагаю состояние заказа менять в одной транзакции с обновлением остатков. Заказы и остатки естественно должны быть в одной базе

G>- МСА предлагает сделать две базы, где заказы лежат в одной, а заказы в другой.
G>Написать код вида:
G> 1. обнови остатки в базе А
G> 2. обнови статус заказа в базе Б
G> 3. если появилась ошибка, то откати обновление в базе А
Нет, МСА такого не предлагает.
МСА предлагает использование Representational State Transfer. Работает он так:
1. Заказ в базе Б находится в статусе "черновик".
2. По команде "зарезервировать заказ" заказ атомарно переходит в статус "резервируется". Из этого статуса его вручную вывести нельзя, и изменить состав заказа в этом статусе тоже нельзя.
3. Сервисом заказов делается попытка выполнить идемпотентную операцию "создать резерв" в сервисе А.
3.1. Если сервис А отвечает отказом, то сервис Б атомарно возвращает заказ в статус "черновик" и пишет в историю заказа "резервирование не удалось".
3.2. Если сервис А отвечает подтверждением, то сервис Б атомарно перемещает заказ в статус "зарезервировано", и для него становятся доступны следующие операции стейт-машины.
3.3. Если сервису Б не удается обработать ответ от сервиса А по любой из причин, перечисленных в моём предыдущем посте, то заказ остаётся в статусе "резервируется".

G>Если код падает по между шагами 2 и 3, то в базе остается несогласованное состояние.

Несогласованное состояние в REST возникает только в тот момент, когда мы не получили ответ на запрос "распространить изменение". Причём мы знаем о том, что оно несогласованное — и можем принять меры, чтобы эта несогласованность не вышла нам боком.

G>Значит вместе самим кодом резервирования заказа надо написать еще фоновую задачу, которая откатывает незавершенные резервы.

Нет, никакой "фоновой задачи отката" писать не нужно. А что нужно — так это писать движок стейт-машины, который вполне универсален. Любители бойлерплейта могут развернуться и написать отдельные "фоновые задачи" для каждого такого под-сценария, но современные технологии вполне позволяют раз и навсегда написать код, который в каждом сервисе процессит очередь исходящих запросов и долбит их до тех пор, пока не получит от удалённой стороны внятный ответ. Этот внятный ответ отдаётся в код "продолжения", который и переключает состояние машины в следующий стейт.

G>Я проделал аналогичное в рамках статьи на хабре, результаты неутешительные — https://habr.com/ru/articles/963120/ см раздел "рукопашные транзакции".

Я бы не сказал, что аналогичное. Там вы экспериментируете с рукопашными транзакциями в единой СУБД. Здесь речь идёт о реализации распространения изменений в ненадёжной среде.

G>Но у нас транзакции не изолированные, то есть межу 1 и 2 может вклиниться изменение, которое обновит заказ.

G>Это значит что для корректного кода отказа как в фоне, так и в п3 надо сохранять "слепок" заказа на шаге 1.
G>Этот слепок — это в чистом виде wal log.
Нет, так делать не надо. Основное заблуждение здесь — возможность надёжно определить ситуацию "возникла ошибка". Нет, у вас запросто может так получиться, что "пассивный" сервис в следующее состояние переехал, а "активный" об этом не узнал. Изменения должны распространяться ровно в одном направлении.
Схема начинает становиться более интересной в том случае, когда в транзакции участвует больше 1 пассивного сервиса.
Сам по себе REST даёт только идемпотентность, дающую нам возможность детерминированно двигаться "вперёд" во времени. И если у нас сервис 1 делает успешный вызов сервиса 2, а затем — неуспешный вызов сервиса 3, то мы зависаем в некотором "бесполезном" состоянии, из которого может не оказаться вообще никакого выхода. Ну, как если купить несдаваемые билеты, и обнаружить, что мест в гостиницах в городе назначения уже нет.
К счастью, в большинстве практических случаев всё же сторонние сервисы предоставляют возможности "сторнировать" проведённое изменение. Зарезервированный товар можно снять с резерва, проведённый по карте платёж можно отменить. И даже покупка авиабилетов очень часто всего лишь ставит их в резерв, хоть и на не очень длинное время. Но всё же это время заведомо больше, чем время рестарта типичных сервисов, поэтому нам его хватит для завершения сценария даже в таком сложном случае.
А если мы говорим об МСА, где все участники сценария — наши же собственные сервисы, то мы их сразу так и проектируем, чтобы иметь возможность безболезненной отмены, если какая-то из частей сценария напоролась на неразрешимую ситуацию.

G>Получение остатков это запрос к одной таблице. Ему никакая МСА не помешает.

Именно. А в монолите у вас тот же самый ресурс (CPU и шина) обрабатывают не только запрос к этой таблице, а ещё 2400 таблиц вашей развесистой ентерпрайз-БД.
И пока там кто-то начисляет бонусы сотрудникам, остатки на складах начинают лагать

G>Шардить можно и без МСА. Это вообще ортогональные вещи.

Не совсем ортогональные. Шарды — это и есть "микросервисы для бедных".

G>Мы же упираемся не в монолит, а в производительность одного сервера БД. Тут есть несколько решений:

Всё верно.
G>Правда это пока мы живем в рамках монолитной базы. Как только мы начинаем её разделять на сервисы (подбазы), то у нас появляются дополнительные данные и процессы, необходимые для поддержания целостности. Выше как раз пример такого. И все это жрет ресурсы.
Да, верно, жрёт.
G>При достаточном количестве микросервисов система упирается в "потолок" одного сервера очень быстро.
Вот это утверждение я не понял. Вы собираетесь все микросервисы гонять на одном узле?

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

С этим согласен.

G>По моему опыту "потолок монолита" не в нагрузке, а тупо в размере команды. Когда у тебя 25 человек еще худо-бедно можно пилить монолит с общей кодовой базой. А если команда становится больше, то начинается деление, которое проходит ровно по границам подразделений. А если подразделения между собой не дружат и не имеют хорошего техлида над ними, то микросервисы помогают избежать бардака.

+1. Особенно ярко это проявляется в международных компаниях, где "над ними" не то, что хорошего — вообще никакого техлида может не быть.

G>Допустим


S>>Типа вот мы поднялись после сбоя и видим, что корзинка №42342342 была отправлена на резервирование, а результата резервирования нет.

G>А как мы узнаем что его нет?
Смотрим в свою базу и видим: статус — "резервирование начато".

G>А пользователь все это время ждет?

Конечно.
G>А если он не дождался и ушел?
Произойдёт ровно то же самое, что будет в случае, если у нас "пропал" монолит — ну там, временный сбой, перезагрузка сервиса. Или пропала связь между приложением пользователя и монолитом.
Или даже связь на месте, но одна из позиций на складе сейчас заблокирована другой транзакцией, в рамках которой происходит какое-то замедленное взаимодействие. Так что мы не получаем ни аборта, ни коммита, а просто ждём.
G>А если связь между пользователем и приложением пропала?
То он перезапускает приложение и восстанавливает картину мира.
G>А если пользователь ушлый и пока в одной вкладе крутится ожидание открыл сайт в другой вкладке и пошел что-то менять?
Если разработчики сервиса обладают мало-мальской квалификацией, то ничего плохого не произойдёт. Нельзя же, в самом деле, строить свой код на предположении о том, что с каждым заказом в один момент времени работает ровно одна транзакция.

S>>Да, объекты, пересекающие границы сервисов, в МСА должны храниться на обеих сторонах. Это не обязательно одни и те же объекты, но у них должна быть общая проекция. В данном случае сторону склада не интересуют никакие подробности про способы доставки товара, розничные цены, или там демографию покупателя, но вот список артикулов и количеств, снабжённый уникальным ID, ей необходим. Ровно для того, чтобы когда к нему в следующий раз стукнутся с просьбой зарезервировать корзинку №42342342 он мог не делать повторный резерв, а сразу отдать 200 ok.

G>Я понимаю, что при высокоуровневом взгляде кажется что это все просто, написать три-четыре строки в воркфлоу и будет хорошо. А на практике для обеспечения целостности нужны десятки строк кода и дополнительные данные хранить (что увеличивает нагрузку какбы).
Ну да, около двухсот строк кода. Один раз на всю систему. Не так уж и плохо
G>И самое главное — ради чего это все? Чтобы не упереться в мифический "потолок монолита". С МСА этот потолок окажется очень низко.

G>Это в каком контексте?

G>Насколько понимаю картинка эта для случая когда:
G>1) Есть несколько экземпляров ОДНОЙ И ТОЙ ЖЕ базы
G>2) Клиент подключается к ЛЮБОМУ экземпляру и может менять ЛЮБЫЕ данные
G>То есть мультиматер-кластер.
Да, совершенно верно.

G>В нашем случае вообще никаких кластеров нет. Мы просто в рамках одного процесса обращаемся к двум сервисам на двух разных серверах. Операция может завершиться успешно если обе операции завершатся успешно.

G>Каждая из этих баз может представлять из себя мкльтимастер-кластер, а может одни сервер, а может шардированную базу, это не имеет значения.
Тут картина на самом деле сильно сложнее. Детальный расчёт perceived availability потребует знания соотношения частот сценариев с различным количеством узлов.

G>>>·>"резервирование сделаем транзакционно" не решает проблему "пользователь плюет".

G>>>Конечно решает, потому что пользователь после резервации на складе точно получит свой заказ.
S>>В нашем случае пользователь после резервации на складе тоже точно получит свой заказ. Вся разница — в том, что если будет сбой системы во время заказа, то пользователь монолита до окончания сбоя будет получать 502, а пользователь МСА имеет шанс в это время увидеть спиннер "заказ резервируется....".
G>Это вопрос реализации фронта. Мы при любой архитектуре можем вынести повтор именно на фронт.
Фронт, с т.з. REST — это точно такой же узел сети. По-хорошему, он проектируется по тем же принципам; но благодаря наличию пользователя можно упростить реализацию. Хочешь повтор — жмёшь F5.

G>Тогда вопрос — если не видно разницы, то зачем платить больше?

Затем, что можно делать систему, которая выдерживает много пользователей. Если чо, антропики — не очень хороший пример. У них сценарии все крайне тривиальные, поэтому масштабироваться не трудно.
G>Я скинул ссылку на статью выше, там рукопашные транзакции почти в два раза уронили производительность.
В МСА коэффициент на таких транзакциях будет хуже, чем в два раза.
G>Дьявол как всегда в деталях.
Именно.

G>Я предлагаю на одном сконцентрироваться. Это же реальный сценарий.

G>Когда с ним закончим сможем посмотреть как эти подходы масштабировать.

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

G>Мы же рассматриваем сценарий когда у нас сценарий зависит от доступности двух серверов. На одно из них, а сразу двух. Их доступность равна произведению доступности обоих. А она будет меньше, чем доступность одного.

G>Мы о чем говорим? О серверах приложений или о субд?

Мы говорим о сервисе. Как у него там внутри устроено деление между СУБД и апп-серверами — это дело сервиса; пользователи всего этого не видят.
G>Приложения можно нарезать на десятки отдельных модулей и запускать отдельно: в отдельных процессах, в модулях одного процесса — как удобно. Все что написано выше для них верно.
Чтобы приложения можно было нарезать, между ними должны быть какие-то границы. Как вы передадите одну транзакцию между несколькими модулями в разных процессах? В МСА вы можете накатить апдейт на "модуль ценообразования", не останавливая модули корзинки и склада. В монолите вам придётся делать общий даунтайм. И количество серверов СУБД в HA-кластере вас не спасёт от того, что апдейт схемы плохо совместим с data-level блокировками. То есть "на ходу" его провести возможно далеко не всегда; безопасный способ — переключать базу в монопольный режим.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Отредактировано 14.02.2026 18:37 Sinclair . Предыдущая версия .
Re[29]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.02.26 10:23
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>ИМХО ACID-транзакция, изобретенная на другом уровне абстракции — все равно транзакция. Если говорить то том, какие транзакции стоит использовать — самопальные или предоставляемые БД, то любой вменяемый разработчик выберет второй вариант.

S>Нет, сделать ACID-транзакцию поверх ненадёжного соединения не получится.
У нас все соединения ненадежные. Мы на практике не рассматриваем случай когда фейл происходит между коммитом и ответом о коммите от базы.


S>МСА предлагает использование Representational State Transfer. Работает он так:

S>1. Заказ в базе Б находится в статусе "черновик".
S>2. По команде "зарезервировать заказ" заказ атомарно переходит в статус "резервируется". Из этого статуса его вручную вывести нельзя, и изменить состав заказа в этом статусе тоже нельзя.
S>3. Сервисом заказов делается попытка выполнить идемпотентную операцию "создать резерв" в сервисе А.
S>3.1. Если сервис А отвечает отказом, то сервис Б атомарно возвращает заказ в статус "черновик" и пишет в историю заказа "резервирование не удалось".
S>3.2. Если сервис А отвечает подтверждением, то сервис Б атомарно перемещает заказ в статус "зарезервировано", и для него становятся доступны следующие операции стейт-машины.
S>3.3. Если сервису Б не удается обработать ответ от сервиса А по любой из причин, перечисленных в моём предыдущем посте, то заказ остаётся в статусе "резервируется".
А когда получает ответ пользователь?
Ему же надо дать ответ в моменте и показать окно оплаты.
И что будет если приложение упадет в пункте 3.2 между ответом А и изменением статуса заказа в Б?

Я в принципе знаю правильный ответ: он называется WAL, а на более высоком уровне абстракции — transactional outbox. Данные для выполнения транзакции сначала атомарно пишутся в (одно, иначе атомарности не будет) долговременное хранилище, а потом пытаются примениться к конкретным таблицам.
Это и называется "рукопашные транзакции". Мы рассматриваем сценарий, когда без них можно обойтись.


G>>Если код падает по между шагами 2 и 3, то в базе остается несогласованное состояние.

S>Несогласованное состояние в REST возникает только в тот момент, когда мы не получили ответ на запрос "распространить изменение". Причём мы знаем о том, что оно несогласованное — и можем принять меры, чтобы эта несогласованность не вышла нам боком.
Несогласованное состояние в примере выше возникнет если заказ на складе будет забронирован в сервисе А, а на сервисе Б не изменен статус.


G>>Значит вместе самим кодом резервирования заказа надо написать еще фоновую задачу, которая откатывает незавершенные резервы.

S>Нет, никакой "фоновой задачи отката" писать не нужно.
А что насчет фейла, что я описал выше?


G>>Но у нас транзакции не изолированные, то есть межу 1 и 2 может вклиниться изменение, которое обновит заказ.

G>>Это значит что для корректного кода отказа как в фоне, так и в п3 надо сохранять "слепок" заказа на шаге 1.
G>>Этот слепок — это в чистом виде wal log.
S>Нет, так делать не надо.
А как надо?
В твоем примере выше проблема та же самая. Процесс на шаге 3 может прерваться между обращениями к базам А и Б.
Это будет ошибка — неконсистентное состояние, которое нужно будет или откатить до исходного или каким-то образом докатить до финального. Причем делать это уже фоновым процессом, потому что прерывание, вероятнее всего, произойдет по причине того, что пользователь ушел\связь прервалась. Фоновому процессу надо где-то брать данные о той операции, что надо откатить\докатить, это и будет wal или аналог.

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

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


S>А если мы говорим об МСА, где все участники сценария — наши же собственные сервисы, то мы их сразу так и проектируем, чтобы иметь возможность безболезненной отмены, если какая-то из частей сценария напоролась на неразрешимую ситуацию.

Так давайте на одном простом примере разберемся как оно должно быть? Я вот до сих пор не понимаю как сделать надежное резервирование заказов в рамках МСА с разделением сервисов на "заказ" и "склад", чтобы хотя бы по надежности не уступало одной базе. По трудозатратам однозначно в разы проиграет и по быстродействию\масштабируемости тоже.


G>>Получение остатков это запрос к одной таблице. Ему никакая МСА не помешает.

S>Именно. А в монолите у вас тот же самый ресурс (CPU и шина) обрабатывают не только запрос к этой таблице, а ещё 2400 таблиц вашей развесистой ентерпрайз-БД.
S>И пока там кто-то начисляет бонусы сотрудникам, остатки на складах начинают лагать
Ну нет конечно. readable реплики никто не отменял.

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


G>>Шардить можно и без МСА. Это вообще ортогональные вещи.

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

К шаридованию стоит прибегать тогда, когда нагрузка на CPU при записи (перестроении индексов) уже превышает ресурсы мастера. Но до такой нагрузки 99,999% проектов не доживет никогда.


G>>При достаточном количестве микросервисов система упирается в "потолок" одного сервера очень быстро.

S>Вот это утверждение я не понял. Вы собираетесь все микросервисы гонять на одном узле?
А на скольких узлах надо гонять пока пользователей нет?
Чаще всего так и происходит, что система изначально построенная по МСА гоняется на одном сервере БД в разных базах, а по мере роста нагрузки поднимаются новые серваки. При этом потребность в масштабировании при МСА возникает гораздо раньше, чем в монолитной базе.

S>>>Типа вот мы поднялись после сбоя и видим, что корзинка №42342342 была отправлена на резервирование, а результата резервирования нет.

G>>А как мы узнаем что его нет?
S>Смотрим в свою базу и видим: статус — "резервирование начато".
G>>А если он не дождался и ушел?
S>Произойдёт ровно то же самое, что будет в случае, если у нас "пропал" монолит — ну там, временный сбой, перезагрузка сервиса. Или пропала связь между приложением пользователя и монолитом.
Вот тут неверно.
Если пропала связь в момент между "обновлены остатки на складе" и "обновлен статус заказа", то в МСА получаем неконсистентное состояние, которое еще и непонятно как откатывать\докатывать. В случае монобазы мы получаем отмену транзакции и возврат к исходному состоянию будто ничего не было. Причем бесплатно с точки зрения трудозатрат.

S>Если разработчики сервиса обладают мало-мальской квалификацией, то ничего плохого не произойдёт. Нельзя же, в самом деле, строить свой код на предположении о том, что с каждым заказом в один момент времени работает ровно одна транзакция.

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

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

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


G>>В нашем случае вообще никаких кластеров нет. Мы просто в рамках одного процесса обращаемся к двум сервисам на двух разных серверах. Операция может завершиться успешно если обе операции завершатся успешно.

G>>Каждая из этих баз может представлять из себя мкльтимастер-кластер, а может одни сервер, а может шардированную базу, это не имеет значения.
S>Тут картина на самом деле сильно сложнее. Детальный расчёт perceived availability потребует знания соотношения частот сценариев с различным количеством узлов.
У нас пока один сценарий, давайте с ним разберемся.
Опять типичная софистика — вместо рассмотрения одного кейса сразу начинаем "масштабировать", как-будто недостаток в одном месте чем-то перекроется в других местах. Не перекроется, недостатки будут размножаться во всех остальных сценариях тоже.

S>Фронт, с т.з. REST — это точно такой же узел сети. По-хорошему, он проектируется по тем же принципам; но благодаря наличию пользователя можно упростить реализацию. Хочешь повтор — жмёшь F5.

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

G>>Я предлагаю на одном сконцентрироваться. Это же реальный сценарий.

G>>Когда с ним закончим сможем посмотреть как эти подходы масштабировать.

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

G>>Мы же рассматриваем сценарий когда у нас сценарий зависит от доступности двух серверов. На одно из них, а сразу двух. Их доступность равна произведению доступности обоих. А она будет меньше, чем доступность одного.

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

S>Чтобы приложения можно было нарезать, между ними должны быть какие-то границы.
Границы могут быть разными, в том числе чисто логическими.
Допустим есть прям монолитное приложение, которое весь код запускает в одном процессе. Никакого состояния между запросами в памяти процесса не хранится, все сохраняется в монобазу.
Можем ли мы горизонтально масштабировать приложение, запуская несколько экземпляров на разных серверах? Если нет, то почему?


S>Как вы передадите одну транзакцию между несколькими модулями в разных процессах?

Зачем нам разные процессы?

S>В МСА вы можете накатить апдейт на "модуль ценообразования", не останавливая модули корзинки и склада. В монолите вам придётся делать общий даунтайм.

В монолите я могу сделать rolling update в кластере кубера, обновляя приложение вообще без остановки обслуживания.
С фича-флагами я могу вообще разные версии "модуля ценообразования" запускать даже для разных пользователей.
С хорошей модульностью я могу в части экземпляров запускать часть модулей, а в других этого не делать.
Имхо не надо подстраивать архитектуру под инфраструктурные задачи.

S>И количество серверов СУБД в HA-кластере вас не спасёт от того, что апдейт схемы плохо совместим с data-level блокировками.

А как тут разделение поможет? Если вам нужен access exclusive на таблицу для обновления, то какая разница сколько всего у вас баз?

S>То есть "на ходу" его провести возможно далеко не всегда; безопасный способ — переключать базу в монопольный режим.

не так уж много операций требуют монопольного режима для всей базы, а для отдельных таблиц разделение не поможет.
Re[29]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.02.26 10:51
Оценка:
Здравствуйте, ·, Вы писали:

·>Ты упускаешь мою мысль, что этот код — не особенность МСА-реализации, а реальность бизнес-задачи.

Что означает "реальность бизнес-задачи" ?
Каким образом бизнес-задача приводит к нас к нескольким базам?

·>Просто в твоём монолитном решении ты игнорируешь эту проблему.

О какой проблеме идет речь?

·>С точки зрения бизнеса _изначальная_ задача выглядит так:

·>1. Пользователь создаёт заказ
·>2. Склад резервирует заказ
·>3. Пользователь завершает заказ
И что? Нам поэтому нужно сделать отдельные базы для Заказа, Склада и Пользователя, чтобы соответствовало существительным?
А как в 1С конфигурации УНФ это все в одной базе существует? Оно как-то неправильно работает?
В 1С вообще это все решалось в рамках одной базы задолго до появления термина "микросервисы". В чем они неправы?


·>И вот когда ты опишешь задачу так, то совершенно ВНЕЗАПНО получается, что "надо написать еще фоновую задачу, которая откатывает незавершенные резервы" в любом случае, неважно — МСА у тебя или монолит.

Важно. В монолите это все не нужно. Монобаза тебе обеспечивает атомарность, вообще всегда.

·>МСА просто это сделал явным.

МСА перекладывает на разработчика то, что в монолите делается средствами СУБД.
Re[30]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.02.26 12:47
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>У нас все соединения ненадежные. Мы на практике не рассматриваем случай когда фейл происходит между коммитом и ответом о коммите от базы.

Не совсем так. Если речь идёт о традиционном клиенте, неважно — толстом там или тонком, то стейт такого клиента в любом случае эфемерный.
И если у нас при нажатии кнопки "сабмит" в таком клиенте вылетает какая-нибудь "неожиданная ошибка", то рекомендованное поведение в общем случае сводится к "перезапустите клиента".
То есть мы выбрасываем наше эфемерное состояние и загружаем его обратно из нашей БД.
А уже в ней у нас есть все гарантии ACID-ity. То есть заказ, который мы отправляли на резервирование, в любом случае либо целиком зарезервирован, либо целиком незарезервирован.
И работает это благодаря тому, что вся транзакция исполняется в контексте монолитной СУБД, у которой нет возможности получить partition.
И только когда мы начинаем исполнять эту транзакцию на нескольких узлах, которые связаны ненадёжными каналами и имеют шансы нарваться на partition, в дело вступает Брюер со своей CAP теоремой.

S>>МСА предлагает использование Representational State Transfer. Работает он так:

S>>1. Заказ в базе Б находится в статусе "черновик".
S>>2. По команде "зарезервировать заказ" заказ атомарно переходит в статус "резервируется". Из этого статуса его вручную вывести нельзя, и изменить состав заказа в этом статусе тоже нельзя.
S>>3. Сервисом заказов делается попытка выполнить идемпотентную операцию "создать резерв" в сервисе А.
S>>3.1. Если сервис А отвечает отказом, то сервис Б атомарно возвращает заказ в статус "черновик" и пишет в историю заказа "резервирование не удалось".
S>>3.2. Если сервис А отвечает подтверждением, то сервис Б атомарно перемещает заказ в статус "зарезервировано", и для него становятся доступны следующие операции стейт-машины.
S>>3.3. Если сервису Б не удается обработать ответ от сервиса А по любой из причин, перечисленных в моём предыдущем посте, то заказ остаётся в статусе "резервируется".
G>А когда получает ответ пользователь?
после 3.1/3.2
G>Ему же надо дать ответ в моменте и показать окно оплаты.
Окно оплаты мы показываем только в п. 3.2.
G>И что будет если приложение упадет в пункте 3.2 между ответом А и изменением статуса заказа в Б?
Тут нам придётся немножко погадать о том, что такое "приложение" в этом сценарии. Ну, допустим, это тонкий стейтлесс-клиент, всё "падение" которого — это браузер, который внезапно устал ждать разрешения пользователя на установку апдейта и самопроизвольно перезапустился. Пользователь видит ту же страницу того же заказа, статус которого приложение перезапрашивает у Б через API и видит соответствующий статус "товары в резерве, произведите оплату до ЧЧ:ММ ДД.ММ.ГГГГ".

G>Я в принципе знаю правильный ответ: он называется WAL, а на более высоком уровне абстракции — transactional outbox. Данные для выполнения транзакции сначала атомарно пишутся в (одно, иначе атомарности не будет) долговременное хранилище, а потом пытаются примениться к конкретным таблицам.

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

G>>>Если код падает по между шагами 2 и 3, то в базе остается несогласованное состояние.

S>>Несогласованное состояние в REST возникает только в тот момент, когда мы не получили ответ на запрос "распространить изменение". Причём мы знаем о том, что оно несогласованное — и можем принять меры, чтобы эта несогласованность не вышла нам боком.
G>Несогласованное состояние в примере выше возникнет если заказ на складе будет забронирован в сервисе А, а на сервисе Б не изменен статус.
Вы говорите то же самое, что и я, немного другими словами. Потому, что "в сервисе Б не изменён статус" == "сервис Б не смог получить ответ на запрос о распространении изменения".
Как только он сможет получить этот ответ, статус заказа сразу же и изменится. Без каких-либо рукопашных действий со стороны пользователя или администратора.

G>А что насчет фейла, что я описал выше?

Не уверен, что я правильно понял сценарий фейла, который вы описали.
Я описал ВСЕ сценарии, которые возможны в рассматриваемой ситуации.

G>В твоем примере выше проблема та же самая. Процесс на шаге 3 может прерваться между обращениями к базам А и Б.

G>Это будет ошибка — неконсистентное состояние, которое нужно будет или откатить до исходного или каким-то образом докатить до финального.
Я же написал, как именно докатить. У нас в сервисе Б есть заказ(ы) в состоянии "резервируется". Всё, что нам нужно знать про эти заказы для завершения резервирования, лежит прямо в базе Б и никак не зависит от прихода или ухода клиента. "Фоновый поток" стейт машины берёт каждый такой заказ (на самом деле — remote request task, т.к. этому коду всё равно, идёт ли речь о заказах, или доставках, или рассылках, или ещё о чём-то) из очереди и пытается выполнить заказанную идемпотентную операцию. В нашем случае — "создать резерв для заказа Х" в сервисе А. Если у сервиса А уже есть резерв для этого заказа — он отвечает 200 Ok, давая возможность сервису Б записать в его базу результат операции, то есть переключить заказ Х в состояние "зарезервирован, ожидает оплаты". Если у сервиса А нет резерва для этого заказа — он попробует его выполнить, и ответит либо 200 Ok, либо 4хх если резерв выполнить невозможно. В обоих случаях это даёт сервису Б шанс записать в свой стейт новый статус заказа.
Если у нас каждый 10й запрос от Б к А заканчивается "потерей результата" (неважно по чьей вине — или А в это время перезапускают, или Б, или сеть между ними ложится из-за игр админов), с вероятностью 90% мы получим результат за 1 обращение, с вероятностью 99% — за 2, с вероятностью 99.9% — за три и так далее. В реальности надёжность системы из сети и двух сервисов настолько высока, что в 95-й процентиль укладываются запросы с 1 попытки.

G>Причем делать это уже фоновым процессом, потому что прерывание, вероятнее всего, произойдет по причине того, что пользователь ушел\связь прервалась.

Пользователь здесь играет только роль инициатора операции. Ему не надо "следить" за системой. Вот в вашей системе что будет, если связь с пользователем прервалась между тем, как транзакция в БД закоммитилась, и ему было показано окно оплаты?

G>Фоновому процессу надо где-то брать данные о той операции, что надо откатить\докатить, это и будет wal или аналог.

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

G>Необходимости на уровне кода определять "были ли ошибка" конечно нет. Но код который откатывает\докатывает транзакцию все равно нужен.

Да, и он пишется и отлаживается примерно 1 раз на систему.

G>Так давайте на одном простом примере разберемся как оно должно быть? Я вот до сих пор не понимаю как сделать надежное резервирование заказов в рамках МСА с разделением сервисов на "заказ" и "склад", чтобы хотя бы по надежности не уступало одной базе. По трудозатратам однозначно в разы проиграет и по быстродействию\масштабируемости тоже.

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

S>>И пока там кто-то начисляет бонусы сотрудникам, остатки на складах начинают лагать

G>Ну нет конечно. readable реплики никто не отменял.
Readable реплики быстро превращаются в OLAP, а за ним паровозом едет ETL, и это то, что нынешние эксплуатанты делать не хотят.

G>А нагрузка на запись масштабируется дисками и партицированием.

Если бы всё было так просто, то никто бы не заморачивался никакой архитектурой, кроме монолита. Я согласен с тем, что монолит является предпочтительным решением для старта. И МСА зачастую выступает именно как оверкилл решение, в котором самая рациональная часть — это "если наш сервис не выстрелит, то response time в 100 миллисекунд вместо 5 никого не расстроит. А если выстрелит, то у нас не будет времени на вдумчивое партиционирование и настройку репликации. Народ повалит с реддита так, что нам надо будет за двое суток его отмасштабировать от 200 пользователей до двух миллионов; и если в МСА мы просто навалим туда ресурсов из амазона, то в монолите мы просто ляжем, и второй раз к нам никто не пойдёт".

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

G>Причём если при патицировании можно делить только нагруженные таблицы, то при шардировании придется делить всё. Да еще и придумывать механизм реплицирования изменений в общих таблицах.
Именно. А в микросервисах ничего реплицировать не надо: шардируем нагруженные таблицы, а общие слабонагруженные таблицы так и остаются там, где они остаются.
S>>Вот это утверждение я не понял. Вы собираетесь все микросервисы гонять на одном узле?
G>Чаще всего так и происходит, что система изначально построенная по МСА гоняется на одном сервере БД в разных базах, а по мере роста нагрузки поднимаются новые серваки. При этом потребность в масштабировании при МСА возникает гораздо раньше, чем в монолитной базе.
Да, в этом смысле вы совершенно правы. В целом, конечно, "высокоабстрактные" архитектуры (к коим относится и МСА) как раз позволяют лёгким движением руки поднять количество серверов со 100 до 1000, чтобы обработать нагрузку, с которой монолит бы справился и на 1 сервере

G>Если пропала связь в момент между "обновлены остатки на складе" и "обновлен статус заказа", то в МСА получаем неконсистентное состояние, которое еще и непонятно как откатывать\докатывать.

Нет там ничего непонятного. Мы данные для любой внешней операции берём не из эфемерного контекста, а из своей локальной БД.
Всё, это означает, что у нас цикл do res = try_reserve(...) while (is_transient_error(res)) прекрасно переживает перезапуск нашего приложения.

G>В случае монобазы мы получаем отмену транзакции и возврат к исходному состоянию будто ничего не было. Причем бесплатно с точки зрения трудозатрат.

Да, совершенно верно. А если нам совсем повезло, то вообще вся транзакция, включая пользовательскую переписку и обработку платежей, лежит в пределах этой одной монобазы, и мы можем делать кросс-сервисный ACID.
Жаль, что такое встречается исчезающе редко.

G>Фишка в том, что I в ACID говорит нам, что мы можем написать код, который предполагает что работает одна транзакция, она или выполнится до конца как будто она была одна в этот момент времени или откатится если такое не получилось.

А то. Но вы же помните, что эта буква работает на полную катушку только при уровне изоляции serializable?
Положа руку на сердце: какая часть транзакций в типовой разрабатываемой вами системе работает с таким уровнем?

G>Но когда мы границы транзакции в одной базе пересекаем никаких гарантий больше нет и все надо делать руками.

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

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

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

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

G>Поэтому мы должны на стороне сервера делать откат\докат фоновым процессом.
Он не фоновый, этот процесс — основной. Взаимодействие с клиентом становится асинхронным: то есть я не "резервирую заказ", я ставлю задачу "зарезервируй заказ" в очередь. Очередь разгребается не тем же потоком, который был инициирован в ответ на мой запрос. И делается это в первую очередь для того, чтобы не надо было перезапускать всего клиента каждый раз, как связь с сервером моргнёт.


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

S>>Чтобы приложения можно было нарезать, между ними должны быть какие-то границы.
G>Границы могут быть разными, в том числе чисто логическими.
G>Допустим есть прям монолитное приложение, которое весь код запускает в одном процессе. Никакого состояния между запросами в памяти процесса не хранится, все сохраняется в монобазу.
G>Можем ли мы горизонтально масштабировать приложение, запуская несколько экземпляров на разных серверах? Если нет, то почему?
Это сильно зависит от того, как это приложение было спроектировано. Большинство известных мне монолитных приложений крайне плохо относятся к такой идее. Ну, вот 1С: попробуйте запустить несколько экземпляров на разных серверах, подключив их к одной монобазе. Внезапно оказывается, что нужно прямо как-то заранее бить разработчиков палкой, чтобы они не делали каких-нибудь фоновых процессов, полагающихся на свою эксклюзивность, или client-side locks.
А когда мы таки научили приложение не рассчитывать на монопольный доступ к базе, то оказывается, что узкое место — вовсе не код в App Server (особенно если мы не стали писать его в стиле рич-DDD, а свели к нормальной анемик-модели). Поэтому дополнительные экземпляры начинают дольше простаивать в ожидании БД, и всё.
Дальнейшее распиливание, например на read-реплики и master-реплику потребует либо обучать пользователей "не запускайте этот отчёт в мастер-приложении, идите на специальный адрес, где можно делать отчёты. Но там нельзя резервировать заказы", либо допиливать приложение, чтобы оно умело ходить в разные базы за разными сценариями. То есть опять не получается решить вопрос без трудозатрат.


G>Зачем нам разные процессы?

Вы только что написали:

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

G>В монолите я могу сделать rolling update в кластере кубера, обновляя приложение вообще без остановки обслуживания.
Это интересно. Я так не умею. Есть какой-то авторитетный источник, который описывает механизм этого чуда для невежд вроде меня?
Потому что мне непонятно, как это у нас делаются роллинг апдейты, задействующие схему базы, с учётом того, что она у нас в единственном экземпляре.

G>С фича-флагами я могу вообще разные версии "модуля ценообразования" запускать даже для разных пользователей.

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

S>>И количество серверов СУБД в HA-кластере вас не спасёт от того, что апдейт схемы плохо совместим с data-level блокировками.

G>А как тут разделение поможет? Если вам нужен access exclusive на таблицу для обновления, то какая разница сколько всего у вас баз?
Большая. Эксклюзивность-то мне нужна не на все базы, а только на вот эту конкретную, в которой я меняю схему. А остальные как ехали, так и едут — их вообще это обновление не касается.

S>>То есть "на ходу" его провести возможно далеко не всегда; безопасный способ — переключать базу в монопольный режим.

G>не так уж много операций требуют монопольного режима для всей базы, а для отдельных таблиц разделение не поможет.
Смена схемы — и есть та операция, которая требует этого монопольного режима. К ней же относятся все эти партиционирования и прочие волшебные меры по оптимизации.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[30]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 16.02.26 12:54
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>·>Ты упускаешь мою мысль, что этот код — не особенность МСА-реализации, а реальность бизнес-задачи.

G>Что означает "реальность бизнес-задачи" ?
G>Каким образом бизнес-задача приводит к нас к нескольким базам?
Реальные физические акторы физически разделены и физически невозможно обеспечить 100% надёжную коммуникацию между ними. Поэтому в CAP будет присутствовать компонента P, хочется тебе того или нет.

G>·>Просто в твоём монолитном решении ты игнорируешь эту проблему.

G>О какой проблеме идет речь?
"задачу, которая откатывает незавершенные резервы".

G>·>1. Пользователь создаёт заказ

G>·>2. Склад резервирует заказ
G>·>3. Пользователь завершает заказ
G>И что? Нам поэтому нужно сделать отдельные базы для Заказа, Склада и Пользователя, чтобы соответствовало существительным?
Не чтобы соответствовало существительным, а чтобы сабж.

G>А как в 1С конфигурации УНФ это все в одной базе существует? Оно как-то неправильно работает?

G>В 1С вообще это все решалось в рамках одной базы задолго до появления термина "микросервисы". В чем они неправы?
Хреново 1С работает. Не знаю как оно изменилось с тех пор, но раньше это был просто ад — все ломятся через RDP на один гигасервер и простейшие операции работают минуты. И если что-то где-то упало, то всё и сразу лежит.

G>·>И вот когда ты опишешь задачу так, то совершенно ВНЕЗАПНО получается, что "надо написать еще фоновую задачу, которая откатывает незавершенные резервы" в любом случае, неважно — МСА у тебя или монолит.

G>Важно. В монолите это все не нужно. Монобаза тебе обеспечивает атомарность, вообще всегда.
Если после 2-го пункта произойдёт какая-либо ошибка, как тебе поможет твоя атомаронсть?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[30]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.02.26 12:58
Оценка: +1
Здравствуйте, gandjustas, Вы писали:
G>В 1С вообще это все решалось в рамках одной базы задолго до появления термина "микросервисы". В чем они неправы?
Хм. "Задолго до появления" — это, я так понимаю, лет 10-15 тому? В 1с это решалось очень просто: ферма из 11 серверов с трудом тащила 100 одновременных пользователей.
Это как-то мало похоже на историю успешного масштабирования монолита.
Читаем https://forum.infostart.ru/forum67/topic154761/ и наслаждаемся.

G>·>И вот когда ты опишешь задачу так, то совершенно ВНЕЗАПНО получается, что "надо написать еще фоновую задачу, которая откатывает незавершенные резервы" в любом случае, неважно — МСА у тебя или монолит.

G>Важно. В монолите это все не нужно. Монобаза тебе обеспечивает атомарность, вообще всегда.
Ну давайте всё же ограничимся здравым смыслом. Емейл вам монобаза атомарно отправит? Деньги с карточки атомарно спишет в той же транзакции, что и резерв товара?
В партнёрский сервис заказ на доставку атомарно добавит?
Это я не к тому, что МСА решает все проблемы и делает волосы мягими и шелковистыми, а к тому, что преувеличивать способности монобазы тоже не стоит.
Если разработчик не владеет техникой интеграции упомянутых мной сценариев, то приложение так и не заработает даже в случае монолита.
А если владеет, то у него ровно те же технические решения будут работать и при интеграции со "своими" сервисами, будь они хоть микро-, хоть макро-.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[31]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.02.26 14:56
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>>>МСА предлагает использование Representational State Transfer. Работает он так:

S>>>1. Заказ в базе Б находится в статусе "черновик".
S>>>2. По команде "зарезервировать заказ" заказ атомарно переходит в статус "резервируется". Из этого статуса его вручную вывести нельзя, и изменить состав заказа в этом статусе тоже нельзя.
S>>>3. Сервисом заказов делается попытка выполнить идемпотентную операцию "создать резерв" в сервисе А.
S>>>3.1. Если сервис А отвечает отказом, то сервис Б атомарно возвращает заказ в статус "черновик" и пишет в историю заказа "резервирование не удалось".
S>>>3.2. Если сервис А отвечает подтверждением, то сервис Б атомарно перемещает заказ в статус "зарезервировано", и для него становятся доступны следующие операции стейт-машины.
S>>>3.3. Если сервису Б не удается обработать ответ от сервиса А по любой из причин, перечисленных в моём предыдущем посте, то заказ остаётся в статусе "резервируется".
G>>И что будет если приложение упадет в пункте 3.2 между ответом А и изменением статуса заказа в Б?
S>Тут нам придётся немножко погадать о том, что такое "приложение" в этом сценарии.
У нас мобильное приложение или браузер, обращается через REST API к серверу, а сервер в свою очередь обращается к БД. Типичная трехзвенная архитектура.

S>Ну, допустим, это тонкий стейтлесс-клиент, всё "падение" которого — это браузер, который внезапно устал ждать разрешения пользователя на установку апдейта и самопроизвольно перезапустился.

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


S>Пользователь видит ту же страницу того же заказа, статус которого приложение перезапрашивает у Б через API и видит соответствующий статус "товары в резерве, произведите оплату до ЧЧ:ММ ДД.ММ.ГГГГ".

В описанном выше сценарии пользователь перезагрузивший страницу\приложение увидит что заказ все еще в статусе "резервируется".


G>>Я в принципе знаю правильный ответ: он называется WAL, а на более высоком уровне абстракции — transactional outbox. Данные для выполнения транзакции сначала атомарно пишутся в (одно, иначе атомарности не будет) долговременное хранилище, а потом пытаются примениться к конкретным таблицам.

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

S>Это и даёт нам железобетонную уверенность в том, что после рестарта мы можем откатить неоконченные транзакции и накатить оконченные.

S>Когда мы проектируем в терминах сервисов, такой гарантии у нас в общем случае нет.
Вот мы и выяснили, что в БД есть WAL, что дает нам гарантию, а в МСА для гарантии надо свой WAL изобрести, и то не факт что получится, ибо там другие хранилища.

G>>>>Если код падает по между шагами 2 и 3, то в базе остается несогласованное состояние.

S>>>Несогласованное состояние в REST возникает только в тот момент, когда мы не получили ответ на запрос "распространить изменение". Причём мы знаем о том, что оно несогласованное — и можем принять меры, чтобы эта несогласованность не вышла нам боком.
G>>Несогласованное состояние в примере выше возникнет если заказ на складе будет забронирован в сервисе А, а на сервисе Б не изменен статус.
S>Вы говорите то же самое, что и я, немного другими словами. Потому, что "в сервисе Б не изменён статус" == "сервис Б не смог получить ответ на запрос о распространении изменения".
S>Как только он сможет получить этот ответ, статус заказа сразу же и изменится. Без каких-либо рукопашных действий со стороны пользователя или администратора.
От кого он этот ответ должен получить? Какой процесс должен сработать?


Мне кажется дальнейшее пока рано обсуждать, потому что мы пока не достигли единого понимания сценария фейла для "транзакции" из двух таблиц. Когда поймем эти сценарии и поймем как их можно компенсировать за счет кода можно обсуждать все остальное.
Re[31]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.02.26 15:06
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>·>Ты упускаешь мою мысль, что этот код — не особенность МСА-реализации, а реальность бизнес-задачи.

G>>Что означает "реальность бизнес-задачи" ?
G>>Каким образом бизнес-задача приводит к нас к нескольким базам?
·>Реальные физические акторы физически разделены и физически невозможно обеспечить 100% надёжную коммуникацию между ними.
Как связаны реальны акторы и информационная система?

·>Поэтому в CAP будет присутствовать компонента P, хочется тебе того или нет.

Как связаны реальные акторы и CAP?

G>>·>Просто в твоём монолитном решении ты игнорируешь эту проблему.

G>>О какой проблеме идет речь?
·>"задачу, которая откатывает незавершенные резервы".
Эта проблема решается транзакциями в БД.
Если ты уверен что нет, то наверное можешь показать пример как СУБД с ACID гарантиями не откатывает незавершенные транзакции.

G>>·>1. Пользователь создаёт заказ

G>>·>2. Склад резервирует заказ
G>>·>3. Пользователь завершает заказ
G>>И что? Нам поэтому нужно сделать отдельные базы для Заказа, Склада и Пользователя, чтобы соответствовало существительным?
·>Не чтобы соответствовало существительным, а чтобы сабж.
А зачем это? Самоцель сделать микросервисы? Потребителю пофиг сколько у вас сервисов.

G>>А как в 1С конфигурации УНФ это все в одной базе существует? Оно как-то неправильно работает?

G>>В 1С вообще это все решалось в рамках одной базы задолго до появления термина "микросервисы". В чем они неправы?
·>Хреново 1С работает. Не знаю как оно изменилось с тех пор, но раньше это был просто ад — все ломятся через RDP на один гигасервер и простейшие операции работают минуты. И если что-то где-то упало, то всё и сразу лежит.
Показывает уровень понимания 1С...
Давай проще, я в статье описал пример кода, который в одной базе сохраняет заказы и меняет остатки транзакционно https://habr.com/ru/articles/955714/ Без RDP работает.
Можешь прям в коде показать где там проблемы, которые я игнорирую?


G>>·>И вот когда ты опишешь задачу так, то совершенно ВНЕЗАПНО получается, что "надо написать еще фоновую задачу, которая откатывает незавершенные резервы" в любом случае, неважно — МСА у тебя или монолит.

G>>Важно. В монолите это все не нужно. Монобаза тебе обеспечивает атомарность, вообще всегда.
·>Если после 2-го пункта произойдёт какая-либо ошибка, как тебе поможет твоя атомаронсть?
Про какой второй пункт речь?
Re[31]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.02.26 16:19
Оценка:
Здравствуйте, Sinclair, Вы писали:


S>>>И пока там кто-то начисляет бонусы сотрудникам, остатки на складах начинают лагать

G>>Ну нет конечно. readable реплики никто не отменял.
S>Readable реплики быстро превращаются в OLAP, а за ним паровозом едет ETL, и это то, что нынешние эксплуатанты делать не хотят.
И в чем проблема? Поднимите реплику отдельную и делайте на ней olap.
Если лаг вообще не интересует и расчеты делаются по историческим данным, то можно в отдельный олап переливать данные по ночам (периоды низкой активности), из реплики.
Монобаза этому никак не мешает.

G>>А нагрузка на запись масштабируется дисками и партицированием.

S>Если бы всё было так просто, то никто бы не заморачивался никакой архитектурой, кроме монолита.
Мода влияет на архитектуру гораздо сильнее, чем любые технические факторы. Увы.
А есть еще и фактор управления большими командами.

S>Я согласен с тем, что монолит является предпочтительным решением для старта.

И для нальнейшего развития, пока команда может масштабироваться.

S>И МСА зачастую выступает именно как оверкилл решение, в котором самая рациональная часть — это "если наш сервис не выстрелит, то response time в 100 миллисекунд вместо 5 никого не расстроит. А если выстрелит, то у нас не будет времени на вдумчивое партиционирование и настройку репликации.

То есть ресурсы запилить МСА были, а репликацию и партицирование нет? Это как?
Репликация требует примерно ноль затрат на разработку, надо только написать селектор нужной реплика в зависимости от сценария.
Партицирование требует усилий чуть больше, но слава богу оно нужно далеко не сразу. Поэтому можно будет к нему плавно и осознано подойдти.
По сути для pg есть готовый модуль автопартицирования. Ему даешь в ход таблицу и ключи и он автоматм управляет всеми партициями. Правда входит этот модуль в PostgresPro Enterprise, но если вы дожили до необходимости партицирования, то денег на него хватит. Там еще будет и BiHa прилагаться, и мультмастер кластеры и много чего еще хорошего. И только когда ресурсы в одного мастера уже не будут закрывать все потребности, тогда можно и пробовать шардировать базу.




S>Народ повалит с реддита так, что нам надо будет за двое суток его отмасштабировать от 200 пользователей до двух миллионов; и если в МСА мы просто навалим туда ресурсов из амазона, то в монолите мы просто ляжем, и второй раз к нам никто не пойдёт".

А что мешает навалить ресурсов на монолит?
Я в яндексе могу постгрес поднять с 4 ядер до 96 в 4 клика. И также пропорционально прирастет память на сервере. До 2 млн активных пользователей выдержит я думаю.

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

G>>Причём если при патицировании можно делить только нагруженные таблицы, то при шардировании придется делить всё. Да еще и придумывать механизм реплицирования изменений в общих таблицах.
S>Именно. А в микросервисах ничего реплицировать не надо: шардируем нагруженные таблицы, а общие слабонагруженные таблицы так и остаются там, где они остаются.
Так и без МСА тоже самое будет. Только ресурсов будет меньше жрать на то же самое количество пользователей.
А если дойдет до шардирования, то затраты на репликацию общих таблиц окажутся в разы меньше чем на обеспечение кросс-сервисной коммуникации.

S>>>Вот это утверждение я не понял. Вы собираетесь все микросервисы гонять на одном узле?

G>>Чаще всего так и происходит, что система изначально построенная по МСА гоняется на одном сервере БД в разных базах, а по мере роста нагрузки поднимаются новые серваки. При этом потребность в масштабировании при МСА возникает гораздо раньше, чем в монолитной базе.
S>Да, в этом смысле вы совершенно правы. В целом, конечно, "высокоабстрактные" архитектуры (к коим относится и МСА) как раз позволяют лёгким движением руки поднять количество серверов со 100 до 1000, чтобы обработать нагрузку, с которой монолит бы справился и на 1 сервере

G>>Фишка в том, что I в ACID говорит нам, что мы можем написать код, который предполагает что работает одна транзакция, она или выполнится до конца как будто она была одна в этот момент времени или откатится если такое не получилось.

S>А то. Но вы же помните, что эта буква работает на полную катушку только при уровне изоляции serializable?
S>Положа руку на сердце: какая часть транзакций в типовой разрабатываемой вами системе работает с таким уровнем?
Ровно та часть, которой это требуется. Мне к сожалению сейчас проект достался с МСА и кучей багов неконсистентности. За последние 3 месяца была пара сценариев, которые я непосредственно ревьювил, где нужен был уровень serializable, в остальных справлялись read committed.

G>>Но когда мы границы транзакции в одной базе пересекаем никаких гарантий больше нет и все надо делать руками.

S>Да, что делает ценными специалистов, которые знают, как быть в таком случае
По сути выход один — делать wal. Иногда его называют transactional outbox, что сути не меняет.


G>>Зачем нам разные процессы?

S>Вы только что написали:
S>

S>Приложения можно нарезать на десятки отдельных модулей и запускать отдельно: в отдельных процессах, в модулях одного процесса — как удобно.

Мы нарезаем на процессы вещи на связанные, оставляю слвязанную (транзакционную) логику в рамках одного.
Это же наше решение как делить систему на модули и как запускать их в отдельных процессах.

G>>В монолите я могу сделать rolling update в кластере кубера, обновляя приложение вообще без остановки обслуживания.

S>Это интересно. Я так не умею. Есть какой-то авторитетный источник, который описывает механизм этого чуда для невежд вроде меня?
https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/


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

Достаточно придерживаться простых правил:
1) В схему только добавляем таблицы и колонки (кроме access exclusive на таблицу ничего не надо).
2) Удаляем таблицы колонки только если если они нигде не используются в проде (на момент создания ветки из мастера нет ни одной ссылки из БЛ). такие апдейты накатываем в периоды низкой активности.


S>>>И количество серверов СУБД в HA-кластере вас не спасёт от того, что апдейт схемы плохо совместим с data-level блокировками.

G>>А как тут разделение поможет? Если вам нужен access exclusive на таблицу для обновления, то какая разница сколько всего у вас баз?
S>Большая. Эксклюзивность-то мне нужна не на все базы, а только на вот эту конкретную, в которой я меняю схему. А остальные как ехали, так и едут — их вообще это обновление не касается.
Если миграции ограничить только добавлением колонок, таблиц и индексов, то лочить всю базу им не надо. Им надо лочить отдельные таблицы.

S>>>То есть "на ходу" его провести возможно далеко не всегда; безопасный способ — переключать базу в монопольный режим.

G>>не так уж много операций требуют монопольного режима для всей базы, а для отдельных таблиц разделение не поможет.
S>Смена схемы — и есть та операция, которая требует этого монопольного режима.
Монопольного режима к одной таблице, а не к базе целиком. Не надо путать.


S>К ней же относятся все эти партиционирования и прочие волшебные меры по оптимизации.

Партицирование делается один раз если что.
Re[32]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 16.02.26 16:51
Оценка: +1 :)
Здравствуйте, gandjustas, Вы писали:

G>У нас мобильное приложение или браузер, обращается через REST API к серверу, а сервер в свою очередь обращается к БД. Типичная трехзвенная архитектура.


G>Все проще, пользователь просто закрыл страницу когда не дождался ответа или интернет ему выключили. В любом случае сервер увидел потерю соединения и запустил отмену через CancellationToken, который из контроллера прокидывается во всю БЛ.

Нет конечно, сервер ничего не "увидел", и никакую "отмену" он не запустил.
Сервер может заметить потерю клиента только в тот момент, когда он начинает отправлять клиенту респонс — после того, как все транзакции уже уехали в durable log.
Наоборот делать ни в коем случае нельзя — иначе клиент получит ответ, который может исчезнуть после перезагрузки сервиса.
Более того — сервер может так и не увидеть отвала клиента даже после отправки ответа.
Например, между клиентом и сервером стоит прокси, который в любом случае вычитает ответ до конца и вежливо дождётся закрытия соединения со стороны сервера.

G>И это событие по счастливой случайности происходит ровно в момент между тем как А ответил подтверждением, а на сервер Б еще не отправлена команда на изменение статуса заказа.

Вы по-прежнему пишете какие-то непонятные мне вещи. Никакой "команды на изменение статуса заказа" на сервер Б не отправляется.
В этой архитектуре
  1. Пользователь на странице заказа нажимает кнопку "зарезервировать"
  2. Клиент отправляет в "сервис Б" команду
    PATCH /orders/42 
    
    {"status": "reserved"}

  3. Сервис Б в локальной базе делает
    update orders set status = "reserving" where id = 42

    и немедленно возвращает клиенту 202 Accepted
  4. Клиент, в зависимости от развитости чувства прекрасного у тех лида, либо делает регулярный polling этого ордера через GET /orders/42, либо идёт читать GET /orders/42/events.
  5. Тем временем город засыпает просыпается workflow-процесс, который делает select * from orders where status = "reserving" (с поправкой на engine-specific приседания для того, чтобы сделать аккуратное разгребание этой очереди без лишних блокировок и рейсов)
  6. Обнаружив в базе наш заказ с номером 42, он вычитывает его позиции, и делает PUT на адрес сервиса А:
    PUT /reservations/42/
    
    {
      "items": [ 
       {"product":17, "quantity":10},
       {"product":5, "quantity":1}
      ]
    }

    обратите внимание, что этот процесс вообще ничего не знает о клиенте и пользователе. Его работа — двигать workflow по предписанной траектории, даже если эта траектория пересекает произвольное количество вынужденных и добровольных рестартов любого из участников.
  7. Сервис А в ответ на этот запрос лезет в свою базу и ищет там reservation where id = 42.
    • Если находит, то проверяет, совпадают ли указанные в запросе продукты и количества с reservationItems where orderId = 42. Если совпали — то возвращает 200 Ok с ETag, вычисленным как хеш от всех продуктов и количеств. Если не совпали — возвращает 409 Conflict. (Тут на самом деле чуть сложнее, но в данном конкретном сценарии эту сложность можно пока проигнорировать)
    • Если не находит, то делает
      begin transaction;
      update stock 
        set reserved = reserved + 10,
            available = available - 10 
        where productId = 17;
      update stock 
        set reserved = reserved + 1,
            available = available - 1 
        where productId = 5;
       
      insert into reservations(orderId) values(42);
      insert into reservationItems(orderId, productId, quantity) values(42, 17, 10);
      insert into reservationItems(orderId, productId, quantity) values(42, 5, 1);
      commit transaction

      Эта радость может быть прервана constraint availability check(available >= 0) или ещё чем-нибудь — тогда сервис отдаёт обратно 4хх c понятным клиенту объяснением.
      Конкретный SQL тут не так важен — важна его атомарность. Если мы всё же довели транзакцию до конца и получили в сервисе А положительный ответ от СУБД, то мы в ответ на это отдаём сервису Б
      201 Object Created
      ETag: 0x213123123412312
      
      {
        "items": [ 
         {"product":17, "quantity":10},
         {"product":5, "quantity":1}
        ]
      }
  8. Сервис Б, дождавшись этого ответа, делает в своей базе
    update orders set status = "reserved", reservationETag = 0x213123123412312 where id = 42

  9. Если вдруг так получилось, что сервис Б так и не смог сделать эту запись (питание моргнуло до коммита в базу; сеть моргнула до того, как респонс доехал до сервиса Б), то тот же самый процесс перейдёт к обработке следующего заказа в этом статусе; а потом очередь снова дойдёт до заказа 42 и сервис Б пойдёт по кругу, пока не получит от сервиса А внятного ответа и не сможет запомнить этот ответ.
    Клиент в этом всём никакого участия не принимает: надёжность его связи с сервером на порядок хуже надёжности сети между сервисами А и Б.
  10. В зависимости от вкусов разработчика, упомянутых выше, та же транзакция, которая пишет в orders, может заодно писать и в orderEvents. Тогда клиент, если он ещё не ушёл, узнает об этом по появлению новых данных в GET /orders/42/events.
  11. Иначе, на очередном витке GET /orders/42, он увидит "status":"reserved" и покажет пользователю окошко для реквизитов оплаты.
G>Даже если мы эту отправку на серверы А и Б делаем на сервере, а не клиенте, то проблема никуда не девается. Процесс на сервере тоже также может упасть между.
Нет никаких "между".

G>В описанном выше сценарии пользователь перезагрузивший страницу\приложение увидит что заказ все еще в статусе "резервируется".

Можно и так написать, но зачем делать плохое решение, когда есть хорошее?

G>Что именно будет сохранено в WAL зависит в итоге от реализации хранилища, к которому мы будем этот самый WAL применять.

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

G>От кого он этот ответ должен получить? Какой процесс должен сработать?

От сервиса А, к которому он обращается. Выше — пошаговый алгоритм.

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

Ок, давайте достигнем. Есть какие-то моменты, непонятные в схеме выше?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[33]: Помогите правильно спроектировать микросервисное при
От: TG  
Дата: 16.02.26 19:13
Оценка:
Здравствуйте, Sinclair, Вы писали:

G>>И это событие по счастливой случайности происходит ровно в момент между тем как А ответил подтверждением, а на сервер Б еще не отправлена команда на изменение статуса заказа.

S>Вы по-прежнему пишете какие-то непонятные мне вещи. Никакой "команды на изменение статуса заказа" на сервер Б не отправляется.

Видимо, gandjustas имеет в виду асинхронный способ взаимодействия между сервисами через брокеры.
Re[32]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 16.02.26 20:45
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>·>Реальные физические акторы физически разделены и физически невозможно обеспечить 100% надёжную коммуникацию между ними.

G>Как связаны реальны акторы и информационная система?
Информационная система — это модель реальных акторов. Чем модель точнее, тем лучше работает.

G>·>Поэтому в CAP будет присутствовать компонента P, хочется тебе того или нет.

G>Как связаны реальные акторы и CAP?
Вот там тебе писали пример: "Потому что с утра жахнули при разгрузке два холодоса об асфальт, а списать еще не списали".

G>>>О какой проблеме идет речь?

G>·>"задачу, которая откатывает незавершенные резервы".
G>Эта проблема решается транзакциями в БД.
G>Если ты уверен что нет, то наверное можешь показать пример как СУБД с ACID гарантиями не откатывает незавершенные транзакции.
Читай внимательно: "незавершенные резервы" != "незавершенные транзакции".

G>>>И что? Нам поэтому нужно сделать отдельные базы для Заказа, Склада и Пользователя, чтобы соответствовало существительным?

G>·>Не чтобы соответствовало существительным, а чтобы сабж.
G>А зачем это? Самоцель сделать микросервисы? Потребителю пофиг сколько у вас сервисов.
Цель: правильно спроектировать.

G>>>А как в 1С конфигурации УНФ это все в одной базе существует? Оно как-то неправильно работает?

G>>>В 1С вообще это все решалось в рамках одной базы задолго до появления термина "микросервисы". В чем они неправы?
G>·>Хреново 1С работает. Не знаю как оно изменилось с тех пор, но раньше это был просто ад — все ломятся через RDP на один гигасервер и простейшие операции работают минуты. И если что-то где-то упало, то всё и сразу лежит.
G>Показывает уровень понимания 1С...
Наверное. Но никогда не слышал, что 1С отличалась скоростью работы и стабильностью. Десяток пользователей, как правило, уложат любой крутой 1С сервак.

G>Давай проще, я в статье описал пример кода, который в одной базе сохраняет заказы и меняет остатки транзакционно https://habr.com/ru/articles/955714/ Без RDP работает.

Ну честно говоря в ретейле я давно не работал. А для трейдинга это какие-то смешные показатели перформанса.
Вот недавно как раз завершили decomission старой системы с субд, acid и прочим. Время отклика уменьшилось с 300мс до 3мс.

G>Можешь прям в коде показать где там проблемы, которые я игнорирую?

Честно говоря, всё читать лень, но вот эту, например: "резерв полностью прошел но оплата не прошла".

G>>>Важно. В монолите это все не нужно. Монобаза тебе обеспечивает атомарность, вообще всегда.

G>·>Если после 2-го пункта произойдёт какая-либо ошибка, как тебе поможет твоя атомаронсть?
G>Про какой второй пункт речь?
Второй из этих:
G>>>·>1. Пользователь создаёт заказ
G>>>·>2. Склад резервирует заказ
G>>>·>3. Пользователь завершает заказ
Пользователь завершает заказ после оформления доставки, оплаты и прочего. Обычно может длиться от нескольких минут до нескольких месяцев.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 16.02.2026 21:11 · . Предыдущая версия .
Re[33]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.02.26 22:08
Оценка:
Здравствуйте, Sinclair, Вы писали:

G>>Все проще, пользователь просто закрыл страницу когда не дождался ответа или интернет ему выключили. В любом случае сервер увидел потерю соединения и запустил отмену через CancellationToken, который из контроллера прокидывается во всю БЛ.

S>Нет конечно, сервер ничего не "увидел", и никакую "отмену" он не запустил.
S>Сервер может заметить потерю клиента только в тот момент, когда он начинает отправлять клиенту респонс — после того, как все транзакции уже уехали в durable log.
Даже как-то странно это читать.
Есть tcp keepalives (вряд ли применимо в нашем сценарии), есть HTTP2 Ping, есть просто таймаут браузера примерно 60-90 сек если не получен ответ.

S>Наоборот делать ни в коем случае нельзя — иначе клиент получит ответ, который может исчезнуть после перезагрузки сервиса.

S>Более того — сервер может так и не увидеть отвала клиента даже после отправки ответа.
Тем не менее рекомендуемый способ в asp.net — использовать CancellationToken в запросе.

S>Например, между клиентом и сервером стоит прокси, который в любом случае вычитает ответ до конца и вежливо дождётся закрытия соединения со стороны сервера.

Вот тут я вообще не уверен.
Потому что буквально на днях мне показали багу, которая возникала при отключении клиента. Как в раз в сценарии, когда надо было в базу записать в и очередь отправить. И обе операции должны были быть выполнены атомарно.
Клиент отключался, соединение разрывалось, CancellationToken взводился, запрос прерывался там где должен был отправить в очередь, но отправка произойти не успела.
Продовый сервер в кубере за nginx ingress.

Но это в целом неважно. Веб-сервер тоже может упасть между двумя операциями. Просто по OOM будет убит по какой-то причине.



G>>И это событие по счастливой случайности происходит ровно в момент между тем как А ответил подтверждением, а на сервер Б еще не отправлена команда на изменение статуса заказа.

S>Вы по-прежнему пишете какие-то непонятные мне вещи. Никакой "команды на изменение статуса заказа" на сервер Б не отправляется.
S>В этой архитектуре ....
То есть в П7 на сервере А мы сохраняем полную копию заказа, как на сервере Б. В одной атомарной транзакции.

На этом можно заканчивать. без МСА было бы достаточно одного действия в П7. Insert вполне можно заменить на update без потери остальных свойств.

G>>В описанном выше сценарии пользователь перезагрузивший страницу\приложение увидит что заказ все еще в статусе "резервируется".

S>Можно и так написать, но зачем делать плохое решение, когда есть хорошее?
Полностью согласен.
Хорошее решение я увидел из детального описания сценария, оно полностью содержится в п7. Все остальное не нужно, как и разделение баз.

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

S>Ок, давайте достигнем. Есть какие-то моменты, непонятные в схеме выше?
Спасибо, теперь стало понятно о чем речь.

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

Надо понимать что далеко не все мы делаем Озон. По сути кроме озона никто не делает Озон
Re[33]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 16.02.26 22:26
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>·>Реальные физические акторы физически разделены и физически невозможно обеспечить 100% надёжную коммуникацию между ними.

G>>Как связаны реальны акторы и информационная система?
·>Информационная система — это модель реальных акторов.
кто это придумал вообще?


·>Чем модель точнее, тем лучше работает.

Есть какие-то доказательства этом тезису?

G>>·>Поэтому в CAP будет присутствовать компонента P, хочется тебе того или нет.

G>>Как связаны реальные акторы и CAP?
·>Вот там тебе писали пример: "Потому что с утра жахнули при разгрузке два холодоса об асфальт, а списать еще не списали".
Как это связано с обсуждаемым вопросом?



G>>>>И что? Нам поэтому нужно сделать отдельные базы для Заказа, Склада и Пользователя, чтобы соответствовало существительным?

G>>·>Не чтобы соответствовало существительным, а чтобы сабж.
G>>А зачем это? Самоцель сделать микросервисы? Потребителю пофиг сколько у вас сервисов.
·>Цель: правильно спроектировать.
Если "правильно" это "точная модель реальных акторов", то вы в чем-то правы.
В реальности "правильно", это "удовлетворяя функциональные и нефункциональные требования с минимальными затратами"

не видел я доказательств что "точная модель реальных акторов" как-то уменьшает затраты.

G>>>>А как в 1С конфигурации УНФ это все в одной базе существует? Оно как-то неправильно работает?

G>>>>В 1С вообще это все решалось в рамках одной базы задолго до появления термина "микросервисы". В чем они неправы?
G>>·>Хреново 1С работает. Не знаю как оно изменилось с тех пор, но раньше это был просто ад — все ломятся через RDP на один гигасервер и простейшие операции работают минуты. И если что-то где-то упало, то всё и сразу лежит.
G>>Показывает уровень понимания 1С...
·>Наверное. Но никогда не слышал, что 1С отличалась скоростью работы и стабильностью. Десяток пользователей, как правило, уложат любой крутой 1С сервак.
1С сервак может и уложат, а вот базе будет пофиг.
Глазами видел как посгрес держал 10 эквивалентных по ресурсам серверов 1С и еще запас был. Примерно 2000 tps приходило, на быстродействии 1С это незаметно сказывалось.
При этом 1С умудряется работать в монобазах.


G>>Давай проще, я в статье описал пример кода, который в одной базе сохраняет заказы и меняет остатки транзакционно https://habr.com/ru/articles/955714/ Без RDP работает.

·>Ну честно говоря в ретейле я давно не работал. А для трейдинга это какие-то смешные показатели перформанса.
·>Вот недавно как раз завершили decomission старой системы с субд, acid и прочим. Время отклика уменьшилось с 300мс до 3мс.
Писать в dev/null можно еще быстрее.


G>>Можешь прям в коде показать где там проблемы, которые я игнорирую?

·>Честно говоря, всё читать лень, но вот эту, например: "резерв полностью прошел но оплата не прошла".
Да мы еще резерв не прошли
Давай вернемся к нему.
напиши как сделать резерв заказа, чтобы:
1) не продать больше чем есть на складе
2) если остатка недостаточно для резерве, то состояние заказа не меняется
3) все это должно переживать внезапные отключения клиента, падения серверов и прочие катаклизмы
4) так чтобы это было эффективнее монобазы (в любом смысле)
Re[34]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.02.26 04:26
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Даже как-то странно это читать.

G>Есть tcp keepalives (вряд ли применимо в нашем сценарии), есть HTTP2 Ping, есть просто таймаут браузера примерно 60-90 сек если не получен ответ.
А можете привести пример кода REST-сервиса, который использует что-то из перечисленного для проверки "наличия клиента"?

G>Тем не менее рекомендуемый способ в asp.net — использовать CancellationToken в запросе.

Возможно, мы с вами по-разному читаем эти рекомендации. Можете набросать схему использования CancellationToken в запросе резервирования товаров?

G>Потому что буквально на днях мне показали багу, которая возникала при отключении клиента. Как в раз в сценарии, когда надо было в базу записать в и очередь отправить. И обе операции должны были быть выполнены атомарно.

G>Клиент отключался, соединение разрывалось, CancellationToken взводился, запрос прерывался там где должен был отправить в очередь, но отправка произойти не успела.
Скорее всего причина баги — как раз в том, что кто-то неверно трактовал рекомендации ASP.NET. Насколько я знаю, ClientDisconnectedToken предназначен для сценариев потоковой отправки — например, тех самых /orders/42/events. Применение его внутри атомарных запросов — лучший способ обеспечить себя багами для починки.

G>Но это в целом неважно. Веб-сервер тоже может упасть между двумя операциями. Просто по OOM будет убит по какой-то причине.

Именно. Поэтому мы делаем так, как я написал.

G>То есть в П7 на сервере А мы сохраняем полную копию заказа, как на сервере Б. В одной атомарной транзакции.

Процитирую то, что я написал три поста назад:

Да, объекты, пересекающие границы сервисов, в МСА должны храниться на обеих сторонах. Это не обязательно одни и те же объекты, но у них должна быть общая проекция. В данном случае сторону склада не интересуют никакие подробности про способы доставки товара, розничные цены, или там демографию покупателя, но вот список артикулов и количеств, снабжённый уникальным ID, ей необходим. Ровно для того, чтобы когда к нему в следующий раз стукнутся с просьбой зарезервировать корзинку №42342342 он мог не делать повторный резерв, а сразу отдать 200 ok.

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

G>На этом можно заканчивать. без МСА было бы достаточно одного действия в П7. Insert вполне можно заменить на update без потери остальных свойств.

Конечно. Если всё лежит в одной базе, можно обойтись без хранения "лишних" проекций.

G>Хорошее решение я увидел из детального описания сценария, оно полностью содержится в п7. Все остальное не нужно, как и разделение баз.



G>Надо понимать что далеко не все мы делаем Озон. По сути кроме озона никто не делает Озон

Ну почему же. Кроме озона, Озон делает вайлдберриз, яндекс, сбер, т-системы, и ещё с десяток игроков поменьше.
И да, административные границы тут первичны. Не обязательно из-за отсутствия техлида или ещё каких-то прямо фатальных дефектов.
Страшнее всего — совмещение разных уровней разработчиков в одном продукте. Криворукий джун, который неровно добавил баннеры в наш монолит, может положить всю систему. У нас конечно CI/CD, но перетестирование монолита — это прогон всех интеграционных тестов перед релизом. Что в мало-мальски большой системе занимает столько времени, что очень много усилий тратится на изобретение способов обойтись без них.
Понятно, что можно пытаться изолировать компоненты — но всё это упирается в разработчицкую дисциплину, которую, опять же, легко поддерживать в команде из 10 человек, и трудно — в команде из 3000 человек.
И даже при самой полной изоляции кода нет уверенности в том, что дурацкая баннерокрутилка не засрёт логами диск так, что весь постгрес ляжет с диагностикой insufficient disk space, парализовав вообще всё.
Недостатки монолита хорошо видны в той же самой винде, когда explorer.exe до сих пор иногда зависает в самый неподходящий момент из-за очередного криво написанного COM Extension, а обновление Tortoise Git требует перезагрузки всей машины.

В остальном — повторюсь: да, я согласен. YAGNI в полный рост — при нормальной архитектуре монолита хватает очень надолго. А к моменту, когда он перестаёт справляться, уже есть более-менее реальная статистика использования, благодаря которой можно пилить систему на сервисы осознанным образом. А не так, что "нам надо трудоустроить 39 начальников отделов — давайте напилим 39 микросервисов и дадим всем сестрам по серьгам".
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[32]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.02.26 04:52
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>По сути для pg есть готовый модуль автопартицирования. Ему даешь в ход таблицу и ключи и он автоматм управляет всеми партициями. Правда входит этот модуль в PostgresPro Enterprise, но если вы дожили до необходимости партицирования, то денег на него хватит. Там еще будет и BiHa прилагаться, и мультмастер кластеры и много чего еще хорошего. И только когда ресурсы в одного мастера уже не будут закрывать все потребности, тогда можно и пробовать шардировать базу.

У меня личного опыта администрирования продакшн постгреса нет, поэтому не могу с вами поддержать беседу на должном уровне.
Но ПостгресПро работают географически близко от меня, поэтому есть возможность смотреть "через плечо" на их ресёрч.
Так вот, судя по ресёрчу, все эти BiHa и прочие адвансед вещи (начиная от репликации) там, скажем так, далеки от идеала. Рекламные проспекты стараются тактично умалчивать о всяких интересных особенностях, которые приходится активно пилить. Возможно, если бы у меня был доступ к R&D MS SQL, то у меня было бы меньше иллюзий про откатанность аналогичных сценариев у них.
В целом у меня нет ощущения, что подобные решения делаются в реляционках по щелчку пальцев.
Основная разница, имхо — в том, что MSA заставляет инвестировать в распиливание заранее, а не в том, что она какие-то усилия экономит или какой-то перформанс улучшает в моменте.
G>А что мешает навалить ресурсов на монолит?
G>Я в яндексе могу постгрес поднять с 4 ядер до 96 в 4 клика. И также пропорционально прирастет память на сервере. До 2 млн активных пользователей выдержит я думаю.
А IOPS вы тоже в 20 раз в 4 клика ему подымете?

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

Лично мне механика кросс-сервисной коммуникации понятна от и до. Что создаёт иллюзию контроля.
А репликация в том же постгре для меня выглядит так: "Иногда, если слейв слишком сильно отстал от мастера, то ему не удаётся нащупать нужный LSN при репликации". Или "в некоторых сценариях TSN на мастере и слейве может разьехаться так, что у них нет общей предыстории". Или там "в процессе восстановления после сбоя постгрес не принимает никаких соединений; в некоторых сценариях он может подолгу ничего не писать в лог. Поэтому проблема надёжного детектирования сбоя рекавери является актуальной".
Это я по памяти пересказываю темы ВКР стажирующихся в постгресе студентов. На защитах про это слушать прикольно; но когда я думаю про возможность нарваться на такое в проде, у меня шевелятся остатки волос на голове.


G>Ровно та часть, которой это требуется.

Ну, лично в вас я уверен. К сожалению, как только мы переходим с разработчицкой стороны на менеджерскую, то выясняется, что даже те немногие бэкендеры, которые вообще владеют SQL, в большинстве не понимают смысла уровней изоляции. Ещё меньше таких, кто умеет их корректно применять
G>Мне к сожалению сейчас проект достался с МСА и кучей багов неконсистентности. За последние 3 месяца была пара сценариев, которые я непосредственно ревьювил, где нужен был уровень serializable, в остальных справлялись read committed.
Ок, согласен. Сравнивая "компетентные разработчики в МСА" с "некомпететнными разработчиками в монолите" выиграет МСА. Но "некомпетентные разработчики в МСА" напорют, наверное, больше, чем "компетентные разработчики в монолите"
S>>Это интересно. Я так не умею. Есть какой-то авторитетный источник, который описывает механизм этого чуда для невежд вроде меня?
G>https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/
Не-не-не. Меня не команды Кубера интересуют. Как вы сделаете роллинг апдейт так, что у вас половина подов в версии 3, половина в версии 2, и все при этом работают, при том что схема базы у нас где-то между версиями 2 и 3?

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

G>Достаточно придерживаться простых правил:
G>1) В схему только добавляем таблицы и колонки (кроме access exclusive на таблицу ничего не надо).
G>2) Удаляем таблицы колонки только если если они нигде не используются в проде (на момент создания ветки из мастера нет ни одной ссылки из БЛ). такие апдейты накатываем в периоды низкой активности.

Не вижу, как бы этого было достаточно.

S>>>>И количество серверов СУБД в HA-кластере вас не спасёт от того, что апдейт схемы плохо совместим с data-level блокировками.

G>>>А как тут разделение поможет? Если вам нужен access exclusive на таблицу для обновления, то какая разница сколько всего у вас баз?
S>>Большая. Эксклюзивность-то мне нужна не на все базы, а только на вот эту конкретную, в которой я меняю схему. А остальные как ехали, так и едут — их вообще это обновление не касается.
G>Если миграции ограничить только добавлением колонок, таблиц и индексов, то лочить всю базу им не надо. Им надо лочить отдельные таблицы.
Ок, поясняю свое беспокойство. Вот мы идём и апгрейдим базу.
Начали добавлять колонки в таблицу Т1. Получили на неё access exclusive, добавили, перешли к таблице T2. А в это время прикладная транзакция делает что-то невинное, например — пишет в T2, и собирается писать в T1.
Значит, она не даёт нам получить access exclusive на T2, а мы не даём ей получить блокировку на T1.
Кто-то из нас будет выбран жертвой дедлока.

G>Монопольного режима к одной таблице, а не к базе целиком. Не надо путать.

Почему к одной-то? Ко всем задействованным, а лучше — ко всем, которые косвенно связаны с изменяемыми таблицами (могут быть изменены в одной транзакции с нашей).

G>Партицирование делается один раз если что.

Сразу всей базы?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[33]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 17.02.26 10:36
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Если не находит, то делает

S>begin transaction;
update stock 
...
update stock 
...
S>insert into reservationItems(orderId, productId, quantity) values(42, 17, 10);
S>insert into reservationItems(orderId, productId, quantity) values(42, 5, 1);
...
S>commit transaction

Кстати, вовсе необязательно делать все строки заказа в одной БД-транзакции. Их можно делать поштучно или разбивать на батчи, шарды и т.п. с целью чтобы транзакции были меньшего размера. Это позволит сильнее параллелить и меньше крутиться на локах.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[34]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 17.02.26 10:39
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>кто это придумал вообще?

G>Есть какие-то доказательства этом тезису?
Лень доказывать. Можешь вычеркнуть, на результат не влияет. Я надеялся, что так проще объяснить.

G>>>Как связаны реальные акторы и CAP?

G>·>Вот там тебе писали пример: "Потому что с утра жахнули при разгрузке два холодоса об асфальт, а списать еще не списали".
G>Как это связано с обсуждаемым вопросом?
Чтобы спроектировать правильно — надо учитывать и такие сценарии.

G>·>Цель: правильно спроектировать.

G>Если "правильно" это "точная модель реальных акторов", то вы в чем-то правы.
G>В реальности "правильно", это "удовлетворяя функциональные и нефункциональные требования с минимальными затратами"
G>не видел я доказательств что "точная модель реальных акторов" как-то уменьшает затраты.
Чем точнее модель, тем бОльшее число сценариев она поддерживает в штатном режиме. Это когда тебе не требуется вручную апдейтить таблицы на проде, т.к. "жахнули два холодоса", а всё работает само.

G>>>·>Хреново 1С работает. Не знаю как оно изменилось с тех пор, но раньше это был просто ад — все ломятся через RDP на один гигасервер и простейшие операции работают минуты. И если что-то где-то упало, то всё и сразу лежит.

G>>>Показывает уровень понимания 1С...
G>·>Наверное. Но никогда не слышал, что 1С отличалась скоростью работы и стабильностью. Десяток пользователей, как правило, уложат любой крутой 1С сервак.
G>1С сервак может и уложат, а вот базе будет пофиг.
А мне пофиг на базу. Юзерам важнее работу работать, а не о самочувствии базы беспокоиться.

G>·>Ну честно говоря в ретейле я давно не работал. А для трейдинга это какие-то смешные показатели перформанса.

G>·>Вот недавно как раз завершили decomission старой системы с субд, acid и прочим. Время отклика уменьшилось с 300мс до 3мс.
G>Писать в dev/null можно еще быстрее.
А в лежачий сервер можно хоть за 0мс писать!

G>·>Честно говоря, всё читать лень, но вот эту, например: "резерв полностью прошел но оплата не прошла".

G>Да мы еще резерв не прошли
G>Давай вернемся к нему.
На десятый круг заходим?

G>напиши как сделать резерв заказа, чтобы:

G>1) не продать больше чем есть на складе
Уже раз 10 тебе написали несколько разных людей.

G>2) если остатка недостаточно для резерве, то состояние заказа не меняется

Это ещё зачем? Если всё зарезервировано, надо поменять состояние заказа на "зарезервировано". Если что-то не смогли, то действуем по обстоятельствам, например, просто начинаем отмену заказа.

G>3) все это должно переживать внезапные отключения клиента, падения серверов и прочие катаклизмы

G>4) так чтобы это было эффективнее монобазы (в любом смысле)
Разделять на мелкие порции и иметь workflow. Писали же тебе уже.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[34]: Помогите правильно спроектировать микросервисное при
От: TG  
Дата: 17.02.26 11:27
Оценка: +1
Здравствуйте, gandjustas, Вы писали:

G>напиши как сделать резерв заказа, чтобы:

G>1) не продать больше чем есть на складе
G>2) если остатка недостаточно для резерве, то состояние заказа не меняется
G>3) все это должно переживать внезапные отключения клиента, падения серверов и прочие катаклизмы

С такими формулировками системы не проектируют.
Надо перечислить какие именно катаклизмы и как Система должна переживать, прикинуть стоимость и сроки разработки, и уже потом принимать решения.

G>4) так чтобы это было эффективнее монобазы (в любом смысле)


Это бессмысленное и бесполезное занятие.
Переход к МСА имеет смысл только если есть существенные нефункциональные требования, которые в монолите невозможно или трудно реализовать.
Поэтому имеет смысл только сравнение конкретных методов реализации конкретных требований.
Re[34]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.02.26 11:46
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Sinclair, Вы писали:


S>>Если не находит, то делает

·>
S>>begin transaction;
·>update stock 
·>...
·>update stock 
·>...
S>>insert into reservationItems(orderId, productId, quantity) values(42, 17, 10);
S>>insert into reservationItems(orderId, productId, quantity) values(42, 5, 1);
·>...
S>>commit transaction
·>

·>Кстати, вовсе необязательно делать все строки заказа в одной БД-транзакции. Их можно делать поштучно или разбивать на батчи, шарды и т.п. с целью чтобы транзакции были меньшего размера. Это позволит сильнее параллелить и меньше крутиться на локах.

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

Нулевое приближение: всё зависит от бизнес-задачи. Если партия сказала "есть контакт", то народ будет есть контакт. А если бизнес сказал "нам нужна атомарность", то разработчики будут есть атомарность. И вот как мы её обеспечиваем. "Я же не советую вам, сколько табов нужно ставить перед фигурной скобкой. Так и вы не учите меня торговать неликвидом".

И в обратную сторону: разработке похрену, сколько там покупателей отваливаются по таймауту в час пик, потому как "мы сделали всё, как в техзадании. Последствия — на вас".

В рамках этого подхода мы не имеем права произвольно менять постановку задачи. "Мы сделали всё, как вы сказали, только не миллион, а сто рублей, и не выиграл, а проиграл, и не атомарно, а частично, и не зарезервировал, а оплатил" и так далее. Надо уметь делать то, что требуется, а не то, что удобно.

Недостатки подхода понятны.

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

Поэтому,
Второе приближение: лучше всего, когда понимание нужд бизнеса и особенностей архитектуры сосредоточено в одной голове. Это сильно ускоряет переход к продуктивным решениям
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[35]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 17.02.26 12:17
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Кстати, вовсе необязательно делать все строки заказа в одной БД-транзакции. Их можно делать поштучно или разбивать на батчи, шарды и т.п. с целью чтобы транзакции были меньшего размера. Это позволит сильнее параллелить и меньше крутиться на локах.

S>Как бы так ответить на простой тезис в сложном контексте...
S>Нулевое приближение: всё зависит от бизнес-задачи. Если партия сказала "есть контакт", то народ будет есть контакт. А если бизнес сказал "нам нужна атомарность", то разработчики будут есть атомарность. И вот как мы её обеспечиваем. "Я же не советую вам, сколько табов нужно ставить перед фигурной скобкой. Так и вы не учите меня торговать неликвидом".
S>И в обратную сторону: разработке похрену, сколько там покупателей отваливаются по таймауту в час пик, потому как "мы сделали всё, как в техзадании. Последствия — на вас".
S>В рамках этого подхода мы не имеем права произвольно менять постановку задачи. "Мы сделали всё, как вы сказали, только не миллион, а сто рублей, и не выиграл, а проиграл, и не атомарно, а частично, и не зарезервировал, а оплатил" и так далее. Надо уметь делать то, что требуется, а не то, что удобно.
Я не предлагаю менять постановку задачи, а менять реализацию. Если нам нужен AoN режим (all-or-nothing) мы просто реализуем откат зарезервированных позиций при отмене зарезервированного (частично или нет, неважно) заказа.
Суть в том, что размер транзакции разумно ограничен, а не зависит от размера заказа.
Я согласен, что это требует бОльших усилий. Но если изначально закладываться на МСА-подход и понятие workflow уже есть, это практически бесплатно.
А в случае если у нас был изначально монолит, то дело плохо, конечно. Распилить код, рассчитывающий на acid-транзакции ничем неограниченной сложности — очень трудозатратно.

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

S>Например, частичное резервирование позволяет не послать покупателя нахрен потому, что какая-то не особо нужная ему фитюлина закончилась на складе, а пока он разбирался с тем, что именно и как надо выкинуть из корзины для завершения заказа, уже разобрали и то, что он точно хотел взять. И заодно это уменьшение гранулярности транзакций снижает lock contention и увеличивает общий throughput на том же железе.
S>Недостаток подхода — нужно тратить много усилий на торг и согласование требований.
Это уже шаг второй — как именно workflow будет реагировать на событие "item resevation failed" — инициировать отмену AoN или переходить в Partial Fill Negotiation.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 17.02.2026 12:20 · . Предыдущая версия .
Re[36]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.02.26 13:49
Оценка:
Здравствуйте, ·, Вы писали:

·>Я не предлагаю менять постановку задачи, а менять реализацию. Если нам нужен AoN режим (all-or-nothing) мы просто реализуем откат зарезервированных позиций при отмене зарезервированного (частично или нет, неважно) заказа.

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

·>Я согласен, что это требует бОльших усилий. Но если изначально закладываться на МСА-подход и понятие workflow уже есть, это практически бесплатно.

·>А в случае если у нас был изначально монолит, то дело плохо, конечно. Распилить код, рассчитывающий на acid-транзакции ничем неограниченной сложности — очень трудозатратно.

·>Это уже шаг второй — как именно workflow будет реагировать на событие "item resevation failed" — инициировать отмену AoN или переходить в Partial Fill Negotiation.

Ну с моей точки зрения делить транзакцию на ещё более мелкие гранулы, чем по границам (микро)сервисов — уже оверкилл, если только это не обосновывается бизнес-необходимостью.
Кстати, современные маркетплейсы типа Озона, емнип, при оформлении заказа запросто делят его на части типа "это уже зарезервировали, а вот это внезапно кончилось — мы переложили его обратно в корзину. Сообщить вам, когда оно появится в продаже"?
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[37]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 17.02.26 14:28
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Я не предлагаю менять постановку задачи, а менять реализацию. Если нам нужен AoN режим (all-or-nothing) мы просто реализуем откат зарезервированных позиций при отмене зарезервированного (частично или нет, неважно) заказа.

S>·>Суть в том, что размер транзакции разумно ограничен, а не зависит от размера заказа.
S>К сожалению, у этого подхода есть семантические последствия.
S>Например, если два пользователя заказали товары А и Б, причём первый успел зарезервировать последний А, а второй успел зарезервировать последний Б, то в ACID один из них получит отказ, а другой заберёт всё.
S>А в вашем подходе оба заказа будут откачены, и мы получим двух недовольных кастомеров плюс непроданный товар на складе.
Так обычная сортировка по идентификатору товара — типичный приём обхода взаимоблокировок. Это, кстати, must have и при формировании твоей мегатранзакции.
Ну или retry логика.

S>·>Я согласен, что это требует бОльших усилий. Но если изначально закладываться на МСА-подход и понятие workflow уже есть, это практически бесплатно.

S>·>А в случае если у нас был изначально монолит, то дело плохо, конечно. Распилить код, рассчитывающий на acid-транзакции ничем неограниченной сложности — очень трудозатратно.
S>·>Это уже шаг второй — как именно workflow будет реагировать на событие "item resevation failed" — инициировать отмену AoN или переходить в Partial Fill Negotiation.
S>Ну с моей точки зрения делить транзакцию на ещё более мелкие гранулы, чем по границам (микро)сервисов — уже оверкилл, если только это не обосновывается бизнес-необходимостью.
Я, наверное, испорчен hft и прочим. Как-то само собой разумеющееся — атомарная транзакция должна быть минимальной.

S>Кстати, современные маркетплейсы типа Озона, емнип, при оформлении заказа запросто делят его на части типа "это уже зарезервировали, а вот это внезапно кончилось — мы переложили его обратно в корзину. Сообщить вам, когда оно появится в продаже"?

Угу. Оно. Partial Negotiation. Как такое уложить в мегатразнакцию
begin transaction;
update stock
update stock
commit transaction;
мне неясно.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 17.02.2026 14:33 · . Предыдущая версия . Еще …
Отредактировано 17.02.2026 14:30 · . Предыдущая версия .
Re[38]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.02.26 15:18
Оценка:
Здравствуйте, ·, Вы писали:


S>>Кстати, современные маркетплейсы типа Озона, емнип, при оформлении заказа запросто делят его на части типа "это уже зарезервировали, а вот это внезапно кончилось — мы переложили его обратно в корзину. Сообщить вам, когда оно появится в продаже"?

·>Угу. Оно. Partial Negotiation. Как такое уложить в мегатразнакцию
·>begin transaction;
·>update stock
·>update stock
·>commit transaction;
·>мне неясно.
Там ничего неясного нет — просто добавляется ещё один предикат во where, а заказ распадается на reserved и residual.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[39]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 17.02.26 16:55
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>мне неясно.

S>Там ничего неясного нет — просто добавляется ещё один предикат во where, а заказ распадается на reserved и residual.
Т.е. в одной транзакции будут roundtrips между субд и приложением для каждого товара?
Мне кажется, что
begin transaction;
update stock ...;
insert into reservationItems(orderId, productId, quantity) values(42, 17, 10);
commit transaction;//constraint checked

begin transaction;
update stock ...;
insert into reservationItems(orderId, productId, quantity) values(42, 5, 1);
commit transaction;//constraint checked

будет выполняться быстрее, чем
begin transaction;
update stock ... where available>0;
//check UpdateCount?
update stock ... where available>0;
//check UpdateCount?
insert into reservationItems(orderId, productId, quantity) values(42, 17, 10);
insert into reservationItems(orderId, productId, quantity) values(42, 5, 1);
commit transaction
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 17.02.2026 16:57 · . Предыдущая версия .
Re[40]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 17.02.26 17:41
Оценка:
Здравствуйте, ·, Вы писали:
S>>Там ничего неясного нет — просто добавляется ещё один предикат во where, а заказ распадается на reserved и residual.
·>Т.е. в одной транзакции будут roundtrips между субд и приложением для каждого товара?
Нет, это всё можно записать двумя стейтментами на весь заказ, вообще без раундтрипов.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[35]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.02.26 17:54
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>Даже как-то странно это читать.

G>>Есть tcp keepalives (вряд ли применимо в нашем сценарии), есть HTTP2 Ping, есть просто таймаут браузера примерно 60-90 сек если не получен ответ.
S>А можете привести пример кода REST-сервиса, который использует что-то из перечисленного для проверки "наличия клиента"?
Любой сервис asp.net, использующий cancellation token

G>>Тем не менее рекомендуемый способ в asp.net — использовать CancellationToken в запросе.

S>Возможно, мы с вами по-разному читаем эти рекомендации. Можете набросать схему использования CancellationToken в запросе резервирования товаров?
Тут все есть https://github.com/gandjustas/habr-post-stock-api
Везде в методы сохранения передается CancellationToken, не мешает сделать acid в одной базе, даже если клиенты отваливаются.


G>>То есть в П7 на сервере А мы сохраняем полную копию заказа, как на сервере Б. В одной атомарной транзакции.

S>Процитирую то, что я написал три поста назад:
S>

S>Да, объекты, пересекающие границы сервисов, в МСА должны храниться на обеих сторонах. Это не обязательно одни и те же объекты, но у них должна быть общая проекция. В данном случае сторону склада не интересуют никакие подробности про способы доставки товара, розничные цены, или там демографию покупателя, но вот список артикулов и количеств, снабжённый уникальным ID, ей необходим. Ровно для того, чтобы когда к нему в следующий раз стукнутся с просьбой зарезервировать корзинку №42342342 он мог не делать повторный резерв, а сразу отдать 200 ok.

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

S>Ничего этого складскому сервису не надо, поэтому речь идёт не о "полной копии". Заодно это изолирует наш складской сервис от изменений в сервисе заказов — например, добавление опции "упаковать как подарок" изменит схему БД сервиса Б, никак не трогая сервис А. Что позволяет нам, к примеру, накатывать изменение на сервис Б, не приостанавливая операции в сервисе А (скажем, отгрузки уже оплаченных заказов).

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

G>>На этом можно заканчивать. без МСА было бы достаточно одного действия в П7. Insert вполне можно заменить на update без потери остальных свойств.

S>Конечно. Если всё лежит в одной базе, можно обойтись без хранения "лишних" проекций.


Мы уже несколько раз вернулись к ситуации, то без МСА мы можем получить такое же поведение, только дешевле.
Я повторю тезис: МСА не решает никаких технических проблем, которые нельзя решить другим способом дешевле. Пока ни у кого не нашлось обратного примера.


G>>Надо понимать что далеко не все мы делаем Озон. По сути кроме озона никто не делает Озон

S>Ну почему же. Кроме озона, Озон делает вайлдберриз, яндекс, сбер, т-системы, и ещё с десяток игроков поменьше.
"десяток" это перебор.
Ozon и WB это почти уникальные случаи: у них относительно небольшой фронт, очень тяжелый бек, сервисы связаны друг с другом (там именно такой каскад как в примере), высокие требования консистнетности, при этом могут быть громадные нагрузки в периоды праздников и распродаж. И у них тысячи разработчиков, которых надо чем-то занять.

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


S>В остальном — повторюсь: да, я согласен. YAGNI в полный рост — при нормальной архитектуре монолита хватает очень надолго. А к моменту, когда он перестаёт справляться, уже есть более-менее реальная статистика использования, благодаря которой можно пилить систему на сервисы осознанным образом. А не так, что "нам надо трудоустроить 39 начальников отделов — давайте напилим 39 микросервисов и дадим всем сестрам по серьгам".

Re[41]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 17.02.26 18:24
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>>Там ничего неясного нет — просто добавляется ещё один предикат во where, а заказ распадается на reserved и residual.

S>·>Т.е. в одной транзакции будут roundtrips между субд и приложением для каждого товара?
S>Нет, это всё можно записать двумя стейтментами на весь заказ, вообще без раундтрипов.
Интересно, как это выглядит?
И что если кто-то закинет заказ на тысячу позиций?
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[33]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.02.26 18:28
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>Здравствуйте, gandjustas, Вы писали:


G>>По сути для pg есть готовый модуль автопартицирования. Ему даешь в ход таблицу и ключи и он автоматм управляет всеми партициями. Правда входит этот модуль в PostgresPro Enterprise, но если вы дожили до необходимости партицирования, то денег на него хватит. Там еще будет и BiHa прилагаться, и мультмастер кластеры и много чего еще хорошего. И только когда ресурсы в одного мастера уже не будут закрывать все потребности, тогда можно и пробовать шардировать базу.

S>У меня личного опыта администрирования продакшн постгреса нет, поэтому не могу с вами поддержать беседу на должном уровне.
S>Но ПостгресПро работают географически близко от меня, поэтому есть возможность смотреть "через плечо" на их ресёрч.
S>Так вот, судя по ресёрчу, все эти BiHa и прочие адвансед вещи (начиная от репликации) там, скажем так, далеки от идеала.
Они, к сожалению, все далеки, но все равно это работает лучше чем любой самопал на МСА.

S>Рекламные проспекты стараются тактично умалчивать о всяких интересных особенностях, которые приходится активно пилить. Возможно, если бы у меня был доступ к R&D MS SQL, то у меня было бы меньше иллюзий про откатанность аналогичных сценариев у них.

У меня доступа к R&D не было, но был и есть опыт пром эксплуатации отказоустойчивых кластеров на MS SQL, то же самое все.

То есть при внезапном отказе мастера мастер может просто не переехать на реплику по тысяче причини. Но внезапный отказ это редкое явление. 99% остановок мастера — запланированные, тут обычно переезд мастера происходит ровно даже в бесплатных решениях. Только 1% — проблемы в ДЦ, тут результат может быть любым.

S>В целом у меня нет ощущения, что подобные решения делаются в реляционках по щелчку пальцев.

у меня один инженер с нуля (до этого не знал ничего, в том числе PG) настроил PG + etcd + Patroni кластер за одну ночь с пятницы на понедельник. Кластер этот выдерживал переезд мастера по команде прямо во время нагрузочного теста с нулем потерянных данных.
Это несопоставимо с затратами команды разработки на создание и поддержку МСА.

S>Основная разница, имхо — в том, что MSA заставляет инвестировать в распиливание заранее, а не в том, что она какие-то усилия экономит или какой-то перформанс улучшает в моменте.

G>>А что мешает навалить ресурсов на монолит?
G>>Я в яндексе могу постгрес поднять с 4 ядер до 96 в 4 клика. И также пропорционально прирастет память на сервере. До 2 млн активных пользователей выдержит я думаю.
S>А IOPS вы тоже в 20 раз в 4 клика ему подымете?
Да Яндекс.Облаке потолок IOPS зависит от размера хранилища в Managed Postgres.


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

S>Лично мне механика кросс-сервисной коммуникации понятна от и до. Что создаёт иллюзию контроля.
Некоторые до сих пор джоинов и индексов в базе избегают, так как не понимают их.

S>А репликация в том же постгре для меня выглядит так: "Иногда, если слейв слишком сильно отстал от мастера, то ему не удаётся нащупать нужный LSN при репликации". Или "в некоторых сценариях TSN на мастере и слейве может разьехаться так, что у них нет общей предыстории". Или там "в процессе восстановления после сбоя постгрес не принимает никаких соединений; в некоторых сценариях он может подолгу ничего не писать в лог. Поэтому проблема надёжного детектирования сбоя рекавери является актуальной".

S>Это я по памяти пересказываю темы ВКР стажирующихся в постгресе студентов. На защитах про это слушать прикольно; но когда я думаю про возможность нарваться на такое в проде, у меня шевелятся остатки волос на голове.
Как-то же managed postgres в ЯО работает? Да и OpenAI умудряются до 800млн пользователей обслуживать на одном кластере.


G>>Ровно та часть, которой это требуется.

S>Ну, лично в вас я уверен. К сожалению, как только мы переходим с разработчицкой стороны на менеджерскую, то выясняется, что даже те немногие бэкендеры, которые вообще владеют SQL, в большинстве не понимают смысла уровней изоляции. Ещё меньше таких, кто умеет их корректно применять
Я их учу, постоянно.
По сути последние 9 лет на всех проектах только и занимаюсь тем, чтобы сделать нормальную работу с БД: запросы, фильтры, проекции, индексы, транзакции, уровни изоляции. Постоянно приходится обучать людей этому.
И каждый раз (не утрирую) оказывается, что если с базой работа построена нормально (как предполагается разработчиками БД), то и сложная архитектура для обеспечения надежности и быстродействия не нужна, даже кэши иногда выкидываем за ненадобностью.
Причем все это не зависит от языка: был и дотнет, и typescript, и python, и 1С.

G>>Мне к сожалению сейчас проект достался с МСА и кучей багов неконсистентности. За последние 3 месяца была пара сценариев, которые я непосредственно ревьювил, где нужен был уровень serializable, в остальных справлялись read committed.

S>Ок, согласен. Сравнивая "компетентные разработчики в МСА" с "некомпететнными разработчиками в монолите" выиграет МСА. Но "некомпетентные разработчики в МСА" напорют, наверное, больше, чем "компетентные разработчики в монолите"
При этом МСА имеет больше сложности, особенно при взаимодействии между сервисами.

S>>>Это интересно. Я так не умею. Есть какой-то авторитетный источник, который описывает механизм этого чуда для невежд вроде меня?

G>>https://kubernetes.io/docs/tutorials/kubernetes-basics/update/update-intro/
S>Не-не-не. Меня не команды Кубера интересуют. Как вы сделаете роллинг апдейт так, что у вас половина подов в версии 3, половина в версии 2, и все при этом работают, при том что схема базы у нас где-то между версиями 2 и 3?
Варианта два:
1) при накате первого же пода обновляется база тупо миграциями. Все остальные поды после этого быстро перезапускаются и работают уже на новой схеме.
2) если нужна миграция долгая, то запускается команда миграции, а потом kubectl apply. Как завершится первая, так пойдет вторая. Может часы занимать, нам без разницы, это unattended процесс.
rolling update не обновляет более одного пода за раз

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

G>>Достаточно придерживаться простых правил:
G>>1) В схему только добавляем таблицы и колонки (кроме access exclusive на таблицу ничего не надо).
G>>2) Удаляем таблицы колонки только если если они нигде не используются в проде (на момент создания ветки из мастера нет ни одной ссылки из БЛ). такие апдейты накатываем в периоды низкой активности.
S>Не вижу, как бы этого было достаточно.
Эти правила дают нам гарантии, что под версии N заработает с базой версии N+1
Включение новых фич на проде управляется через фич-флаги.

S>Кто-то из нас будет выбран жертвой дедлока.

Я выше описал почему так не получится.

G>>Монопольного режима к одной таблице, а не к базе целиком. Не надо путать.

S>Почему к одной-то? Ко всем задействованным, а лучше — ко всем, которые косвенно связаны с изменяемыми таблицами (могут быть изменены в одной транзакции с нашей).
Так не надо писать миграции, которые сразу все таблицы хотят лочить.

G>>Партицирование делается один раз если что.

S>Сразу всей базы?
Только тех таблиц, для которых это актуально.
Re[35]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 17.02.26 18:31
Оценка:
Здравствуйте, TG, Вы писали:

TG>С такими формулировками системы не проектируют.

TG>Надо перечислить какие именно катаклизмы и как Система должна переживать, прикинуть стоимость и сроки разработки, и уже потом принимать решения.

Стандартная софистика — если не знаем как реализовать требования, то надо доказать что требования плохие.

G>>4) так чтобы это было эффективнее монобазы (в любом смысле)

TG>Это бессмысленное и бесполезное занятие.
TG>Переход к МСА имеет смысл только если есть существенные нефункциональные требования, которые в монолите невозможно или трудно реализовать.
Каких например?

TG>Поэтому имеет смысл только сравнение конкретных методов реализации конкретных требований.

Приведите пример конкретных требований, при которых МСА имеет смысл. Так чтобы это касалось больше 10 компаний в РФ и больше 100 в мире.
Re: Помогите правильно спроектировать микросервисное приложение
От: Qulac Россия  
Дата: 18.02.26 07:03
Оценка:
Здравствуйте, busk, Вы писали:

B>Привет всем.

B>Как-то только в теории читал про микросервисы, но вот новый проект планируется небольшой и хотел как раз попробовать микросервисы тут.

C ci/cd нужно начинать иначе быстро отподет охота этим заниматься. Можно сделать как у меня: репо и пейпланы в на гитлаб в облаке, раннер установлен на рабочем компьютере. Делаю мерж — и у меня на компьютере поднимаются сервисы в докере.
Программа – это мысли спрессованные в код
Re[42]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 20.02.26 04:30
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Sinclair, Вы писали:


S>>>>Там ничего неясного нет — просто добавляется ещё один предикат во where, а заказ распадается на reserved и residual.

S>>·>Т.е. в одной транзакции будут roundtrips между субд и приложением для каждого товара?
S>>Нет, это всё можно записать двумя стейтментами на весь заказ, вообще без раундтрипов.
·>Интересно, как это выглядит?
Примерно так:
BEGIN TRANSACTION;

WITH
-- 1) Лочим заказ и одновременно проверяем, что он в draft
ord AS (
  SELECT o.orderId
  FROM orders o
  WHERE o.orderId = @orderId
    AND o.state = 'draft'
  FOR UPDATE
),

-- Позиции заказа (orderId уже фиксируем через join на ord)
req AS (
  SELECT oi.productId, oi.quantity
  FROM orderItem oi
  JOIN ord ON ord.orderId = oi.orderId
),

-- 2) Лочим строки stock в фиксированном порядке (anti-deadlock)
locked AS (
  SELECT s.productId, s.available
  FROM stock s
  JOIN req r ON r.productId = s.productId
  ORDER BY s.productId
  FOR UPDATE
),

-- 2b) Проверка all-or-nothing:
--  - заказ был draft (ord существует)
--  - для каждой позиции есть строка stock
--  - available хватает для каждой позиции
chk AS (
  SELECT
    (SELECT COUNT(*) FROM ord) AS ord_cnt,
    (SELECT COUNT(*) FROM req) AS need_cnt,
    (SELECT COUNT(*) FROM locked) AS locked_cnt,
    (SELECT COUNT(*)
       FROM locked l
       JOIN req r USING (productId)
      WHERE l.available >= r.quantity
    ) AS ok_cnt
),

-- 3) Обновляем stock только если ВСЁ ОК
upd_stock AS (
  UPDATE stock s
  SET available = s.available - r.quantity,
      reserved  = s.reserved  + r.quantity
  FROM req r, chk
  WHERE s.productId = r.productId
    AND chk.ord_cnt = 1
    AND chk.need_cnt > 0
    AND chk.need_cnt = chk.locked_cnt
    AND chk.need_cnt = chk.ok_cnt
  RETURNING 1
),

-- 4) Меняем статус заказа только если stock реально обновился
upd_order AS (
  UPDATE orders o
  SET state = 'reserved'
  WHERE o.orderId = @orderId
    AND EXISTS (SELECT 1 FROM upd_stock)
  RETURNING o.orderId, o.state
)

SELECT * FROM upd_order;

COMMIT;

·>И что если кто-то закинет заказ на тысячу позиций?
Будет попытка зарезервировать заказ на тысячу позиций.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[43]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 20.02.26 09:41
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>>>>Там ничего неясного нет — просто добавляется ещё один предикат во where, а заказ распадается на reserved и residual.

S>>>·>Т.е. в одной транзакции будут roundtrips между субд и приложением для каждого товара?
S>>>Нет, это всё можно записать двумя стейтментами на весь заказ, вообще без раундтрипов.
S>·>Интересно, как это выглядит?
S>Примерно так:
Ок. Выглядит совсем не так, как в предыдущем примере И вроде нету "заказ распадается на reserved и residual".

S>ORDER BY s.productId
S>FOR UPDATE

S>  UPDATE orders o
S>  RETURNING o.orderId, o.state

Понятно, я безнадёжно отстал от современного sql. Последний раз с sql серьёзно работал лет 10 назад...

S>·>И что если кто-то закинет заказ на тысячу позиций?

S>Будет попытка зарезервировать заказ на тысячу позиций.
Ну вроде речь о МСА шла, а у тебя тут orders и stock в одной транзакции. В МСА же будет некий условный json на резервацию. И тут начнутся проблемы с передачей массивных объектов целиком. Скажем, в kafka размер сообщения жестко ограничивается.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 20.02.2026 10:57 · . Предыдущая версия . Еще …
Отредактировано 20.02.2026 9:52 · . Предыдущая версия .
Re[44]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 20.02.26 15:36
Оценка:
Здравствуйте, ·, Вы писали:

·>Ок. Выглядит совсем не так, как в предыдущем примере И вроде нету "заказ распадается на reserved и residual".

Простите. Это я что-то начал подтупливать — тяжёлая неделя.
Это у нас код all or nothing и в монолите. Распиливание пополам выглядит в деталях по-другому, но принцип тот же.
·>Понятно, я безнадёжно отстал от современного sql. Последний раз с sql серьёзно работал лет 10 назад...
Это postgres-specific расширение SQL. У MS есть аналог, но с другим синтаксисом.

·>Ну вроде речь о МСА шла, а у тебя тут orders и stock в одной транзакции. В МСА же будет некий условный json на резервацию. И тут начнутся проблемы с передачей массивных объектов целиком. Скажем, в kafka размер сообщения жестко ограничивается.

А, если в MCA то всё ещё проще. Просто отдаём JSON и получаем обратно не такой же объект, как был — и уже при обработке респонса пилим свой order на reserved часть и всё остальное.
Опять же, это распиливание мы можем сделать безо всяких раундтрипов, передав в СУБД список успешно зарезервированных позиций в удобном для неё виде.

Размеры сообщений — это какая-то надуманная проблема. Если кафка не может передать сообщение нужного размера, то нужно выкинуть кафку. Описанный мной способ не полагается на внешние exactly-once системы доставки.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[45]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 20.02.26 15:55
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Ок. Выглядит совсем не так, как в предыдущем примере И вроде нету "заказ распадается на reserved и residual".

S>Простите. Это я что-то начал подтупливать — тяжёлая неделя.
S>Это у нас код all or nothing и в монолите. Распиливание пополам выглядит в деталях по-другому, но принцип тот же.
Да, ты похоже, не на тот вопрос ответил
Я тут речь завёл о том, чтобы распилить транзакцию произвольного размера на множество мелких транзакций фиксированного размера. Тут: https://rsdn.org/forum/design/9057463
Автор: ·
Дата: 17.02 19:55


S>·>Ну вроде речь о МСА шла, а у тебя тут orders и stock в одной транзакции. В МСА же будет некий условный json на резервацию. И тут начнутся проблемы с передачей массивных объектов целиком. Скажем, в kafka размер сообщения жестко ограничивается.

S>А, если в MCA то всё ещё проще. Просто отдаём JSON и получаем обратно не такой же объект, как был — и уже при обработке респонса пилим свой order на reserved часть и всё остальное.
Вопрос о том, как сервис резервации будет взаимодейтсвовать с субд. Получил сервис резервации гигантский объект и пока его жуёт — все остальные должны ждать на блокировках.
Представь себе, есть куча заказов одной популярной сковородки. И большинство заказов из одной позиции именно на эту сковородку. И тут кто-то запулливает гигантский ордер, среди позиций есть эта сковородка. Придётся ждать всем!
Да и вообще неясно в чём преимущество заталкивать всё в одну транзакцию.

S>Опять же, это распиливание мы можем сделать безо всяких раундтрипов, передав в СУБД список успешно зарезервированных позиций в удобном для неё виде.

Как без раундрипов резервировать с успехом/неуспехом каждую позицию?

S>Размеры сообщений — это какая-то надуманная проблема. Если кафка не может передать сообщение нужного размера, то нужно выкинуть кафку.

Тут дело принципиальное — чтобы система была responsive, надо гонять мелкие сообщения. Пока кафка передаёт гигантское сообщение — другим сообщениям придётся ждать своей очереди. Ждут все! А если большое сообщение разбить на мелкие части, то ждать будет только тот, кому надо много.

S> Описанный мной способ не полагается на внешние exactly-once системы доставки.

Тут нужен at-least-once. Да, и при redelivery тоже придётся гигантское сообщение дублировать.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[46]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 21.02.26 03:41
Оценка:
Здравствуйте, ·, Вы писали:

S>>А, если в MCA то всё ещё проще. Просто отдаём JSON и получаем обратно не такой же объект, как был — и уже при обработке респонса пилим свой order на reserved часть и всё остальное.

·>Вопрос о том, как сервис резервации будет взаимодейтсвовать с субд. Получил сервис резервации гигантский объект и пока его жуёт — все остальные должны ждать на блокировках.
Представления о гигантскости объектов сильно искажены идиотскими реализациями hibernate-based приложений.
Вот в кровавом рич DDD это действительно может стать проблемой: мы начали транзакцию, в которой мееееееееееееееееееедленно такаем складские остатки из базы в апп-сервер, потом их обновляем и меееееееееееееееееееееееееедленно записываем обратно.
Когда код пишется прямыми запросами к СУБД, заказ в тыщу позиций обработается примерно мгновенно. Особенно если данные лежат на современном SSD диске.
Я уже неоднократно сталкивался с тем, что у людей сдвинута интуиция относительно быстродействия СУБД на современном железе. Лет несколько тому кто-то меня уверял, что реляционка захлебнётся на обходе графа из миллиона элементов. Оказалось — нет, даже у лаптопа пред-предыдущего поколения вполне хватает дури обрабатывать такое незаметно для пользователя.

·>Представь себе, есть куча заказов одной популярной сковородки. И большинство заказов из одной позиции именно на эту сковородку. И тут кто-то запулливает гигантский ордер, среди позиций есть эта сковородка. Придётся ждать всем!

О да. Двадцать миллисекунд придётся подождать.
·>Да и вообще неясно в чём преимущество заталкивать всё в одну транзакцию.
Преимущества транзакций — в согласованности. Мы вот взяли и придумали какие-то инварианты, отражающие ограничения бизнеса. Транзакции — единственный способ обеспечить распределённые инварианты (то есть несводимые к ограничениям на 1 элемент данных. Проверки типа "возраст не может быть отрицательным" можно сделать и без транзакций).
Вот есть, к примеру, инвариант "статус относится ко всему заказу — черновик, зарезервирован, оплачен, отгружен, получен и т.п." На этот инвариант полагается масса кода, включая отчёты.
Если мы отменяем этот инвариант, придётся переделывать примерно всё.

S>>Опять же, это распиливание мы можем сделать безо всяких раундтрипов, передав в СУБД список успешно зарезервированных позиций в удобном для неё виде.

·>Как без раундрипов резервировать с успехом/неуспехом каждую позицию?
Язык SQL много чем плох, а вот прекрасен он батч-операциями. Можно отдать ему стейтмент, который поменяет сразу много позиций.
В моём предыдущем примере позиции либо менялись, либо не менялись все разом; небольшое изменение предиката приведёт к тому, что позиции будут резервироваться частично.

·>Тут дело принципиальное — чтобы система была responsive, надо гонять мелкие сообщения. Пока кафка передаёт гигантское сообщение — другим сообщениям придётся ждать своей очереди. Ждут все! А если большое сообщение разбить на мелкие части, то ждать будет только тот, кому надо много.

Повторюсь: если Кафка становится узким местом, нужно выкинуть кафку. В рассматриваемом мной примере никакой Кафки нет, и ждут только те, кто ждёт. Ну и, опять же, в современных сетях всё происходит достаточно шустро. Главное — минимизировать количество раунд-трипов.
·>Тут нужен at-least-once. Да, и при redelivery тоже придётся гигантское сообщение дублировать.
Редчайший случай, заказ на тысячу позиций. Какого характерного размера это "гигантское" сообщение? В непожатом JSON оно будет занимать полсотни килобайт. Семечки.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[47]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 22.02.26 11:52
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Вопрос о том, как сервис резервации будет взаимодейтсвовать с субд. Получил сервис резервации гигантский объект и пока его жуёт — все остальные должны ждать на блокировках.

S>Представления о гигантскости объектов сильно искажены идиотскими реализациями hibernate-based приложений.
S>Вот в кровавом рич DDD это действительно может стать проблемой: мы начали транзакцию, в которой мееееееееееееееееееедленно такаем складские остатки из базы в апп-сервер, потом их обновляем и меееееееееееееееееееееееееедленно записываем обратно.
S>Когда код пишется прямыми запросами к СУБД, заказ в тыщу позиций обработается примерно мгновенно. Особенно если данные лежат на современном SSD диске.
S>Я уже неоднократно сталкивался с тем, что у людей сдвинута интуиция относительно быстродействия СУБД на современном железе. Лет несколько тому кто-то меня уверял, что реляционка захлебнётся на обходе графа из миллиона элементов. Оказалось — нет, даже у лаптопа пред-предыдущего поколения вполне хватает дури обрабатывать такое незаметно для пользователя.
Возможно, что мои знания устарели...

S>·>Представь себе, есть куча заказов одной популярной сковородки. И большинство заказов из одной позиции именно на эту сковородку. И тут кто-то запулливает гигантский ордер, среди позиций есть эта сковородка. Придётся ждать всем!

S>О да. Двадцать миллисекунд придётся подождать.
Для FX-трейдинга — задержка 20мс — это production issue. Типичная latency — 0.3мс.

S>·>Да и вообще неясно в чём преимущество заталкивать всё в одну транзакцию.

S>Преимущества транзакций — в согласованности. Мы вот взяли и придумали какие-то инварианты, отражающие ограничения бизнеса. Транзакции — единственный способ обеспечить распределённые инварианты (то есть несводимые к ограничениям на 1 элемент данных. Проверки типа "возраст не может быть отрицательным" можно сделать и без транзакций).
S>Вот есть, к примеру, инвариант "статус относится ко всему заказу — черновик, зарезервирован, оплачен, отгружен, получен и т.п." На этот инвариант полагается масса кода, включая отчёты.
S>Если мы отменяем этот инвариант, придётся переделывать примерно всё.
Так ведь у нас уже есть бизнес-сущность "частично зарезервированный заказ". Поэтому уже неважно — на 99.9% или на 0.1% зарезервированный. Алгоритмы будут те же.

S>>>Опять же, это распиливание мы можем сделать безо всяких раундтрипов, передав в СУБД список успешно зарезервированных позиций в удобном для неё виде.

S>·>Как без раундрипов резервировать с успехом/неуспехом каждую позицию?
S>Язык SQL много чем плох, а вот прекрасен он батч-операциями. Можно отдать ему стейтмент, который поменяет сразу много позиций.
S>В моём предыдущем примере позиции либо менялись, либо не менялись все разом; небольшое изменение предиката приведёт к тому, что позиции будут резервироваться частично.
Батчинг — это уже ортогонально бизнес-требованиям, это нефункциональное требование. Можно батчами обрабатывать входные заявки на резервацию. Они мелкие, зафигачивай сразу кусками по 100 штук (или сколько намерим оптимально, параметр в конфиге). Опять же — суть в том, что размер транзакции фиксированный, а не зависит от данных идущих от юзеров. Это, кстати, сразу бесплатно даёт более эффективную обработку мелких заказов. Ломанулась куча народа покупать по скидке одну сковородку — фигня, будут обработаны пачками максимально быстро.

S>·>Тут дело принципиальное — чтобы система была responsive, надо гонять мелкие сообщения. Пока кафка передаёт гигантское сообщение — другим сообщениям придётся ждать своей очереди. Ждут все! А если большое сообщение разбить на мелкие части, то ждать будет только тот, кому надо много.

S>Повторюсь: если Кафка становится узким местом, нужно выкинуть кафку. В рассматриваемом мной примере никакой Кафки нет, и ждут только те, кто ждёт. Ну и, опять же, в современных сетях всё происходит достаточно шустро. Главное — минимизировать количество раунд-трипов.
Это скорее про поточную, конвеерную обработку. Когда идут потоки сообщений туда-сюда, друг друга не мешая, с минимальными блокировками.

S>·>Тут нужен at-least-once. Да, и при redelivery тоже придётся гигантское сообщение дублировать.

S>Редчайший случай, заказ на тысячу позиций. Какого характерного размера это "гигантское" сообщение? В непожатом JSON оно будет занимать полсотни килобайт. Семечки.
В высокопроизводительных системах — порядка сотен байт.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 22.02.2026 11:53 · . Предыдущая версия .
Re[48]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 22.02.26 13:13
Оценка:
Здравствуйте, ·, Вы писали:

S>>О да. Двадцать миллисекунд придётся подождать.

·>Для FX-трейдинга — задержка 20мс — это production issue. Типичная latency — 0.3мс.
К счастью, FX-трейдинг — нишевая штука. Нафиг не нужен никому, кроме спекулянтов.
Поэтому рассматривать его требования как основу архитектуры типичного бизнес-приложения не нужно.

S>>Если мы отменяем этот инвариант, придётся переделывать примерно всё.

·>Так ведь у нас уже есть бизнес-сущность "частично зарезервированный заказ". Поэтому уже неважно — на 99.9% или на 0.1% зарезервированный. Алгоритмы будут те же.
Не факт. У нас рассуждения дошли до того, что мы можем разделить заказ на два заказа, одновременно сохраняя базовый инвариант, и обходя ограничения строго-атомарного резервирования.

·>Батчинг — это уже ортогонально бизнес-требованиям, это нефункциональное требование. Можно батчами обрабатывать входные заявки на резервацию.

Нет, вы не поняли. Батчинг тут не про несколько заявок, а про то, что одним стейтментом можно поменять произвольное количество "строк".

·>Это скорее про поточную, конвеерную обработку. Когда идут потоки сообщений туда-сюда, друг друга не мешая, с минимальными блокировками.

Я не умею работать с потоками сообщений. Что такое state мне понятно; вместе со всеми производными понятиями — когерентность, распространение, согласованность, несогласованность.
Что можно сказать про систему на потоках сообщений — да хз. Какие-то сообщения куда-то едут. Соответствует ли такая система каким-нибудь требованиям? Может и соответствует. Как это доказать — непонятно.

S>>Редчайший случай, заказ на тысячу позиций. Какого характерного размера это "гигантское" сообщение? В непожатом JSON оно будет занимать полсотни килобайт. Семечки.

·>В высокопроизводительных системах — порядка сотен байт.
Вы говорите о системах, в которых производительность возведена в кумиры, и ей в жертву приносят всё, включая здравый смысл.
Нет, я не против — мне самому очень нравится архитектура LMAX Disruptor. Но не как задача, которую решают пацаны, а как способ посмотреть на задачу под другим углом.

А в традиционном бизнесе нет нужды опередить соперника на долю наносекунды. Зато важно, чтобы инварианты не разъехались.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[49]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 22.02.26 15:59
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>>>О да. Двадцать миллисекунд придётся подождать.

S>·>Для FX-трейдинга — задержка 20мс — это production issue. Типичная latency — 0.3мс.
S>К счастью, FX-трейдинг — нишевая штука. Нафиг не нужен никому, кроме спекулянтов.
S>Поэтому рассматривать его требования как основу архитектуры типичного бизнес-приложения не нужно.
Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс.

S>>>Если мы отменяем этот инвариант, придётся переделывать примерно всё.

S>·>Так ведь у нас уже есть бизнес-сущность "частично зарезервированный заказ". Поэтому уже неважно — на 99.9% или на 0.1% зарезервированный. Алгоритмы будут те же.
S>Не факт. У нас рассуждения дошли до того, что мы можем разделить заказ на два заказа, одновременно сохраняя базовый инвариант, и обходя ограничения строго-атомарного резервирования.
Не понял, что именно "не факт".

S>·>Батчинг — это уже ортогонально бизнес-требованиям, это нефункциональное требование. Можно батчами обрабатывать входные заявки на резервацию.

S>Нет, вы не поняли. Батчинг тут не про несколько заявок, а про то, что одним стейтментом можно поменять произвольное количество "строк".
Это я плохо объяснил. Батчинг — это не требование бизнеса, а механизм повышения перформанса. Это когда запрос в субд на резервацию одной строки и ста строк занимает примерно одно и то же время, т.к. бОльшая часть времени уходит на всякие накладные расходы взаимодействия сервиса резервации и субд.
Следовательно, выгоднее отправлять стейтменты всегда по 100 штук, вне зависимости от того, разные это заказы или один большой.
Т.е. 1000 заказов с 1й позицией и 1 заказ на 1000 позиций потребует 10 батчей с запросами в субд и будет выполняться одинаковое время.
Т.е. вначале разеделяем заказы на мелкие сообщения резервации, сообщения (возможно батчами) засылаем в очередь. Эта очередь потом разгребается (возможно батчами) и ответы отсылаются таким же способом. Ответы резервации аггрерируются в суммарный статус заказа.

S>·>Это скорее про поточную, конвеерную обработку. Когда идут потоки сообщений туда-сюда, друг друга не мешая, с минимальными блокировками.

S>Я не умею работать с потоками сообщений. Что такое state мне понятно; вместе со всеми производными понятиями — когерентность, распространение, согласованность, несогласованность.
S>Что можно сказать про систему на потоках сообщений — да хз. Какие-то сообщения куда-то едут. Соответствует ли такая система каким-нибудь требованиям? Может и соответствует. Как это доказать — непонятно.
Что-то вроде многопоточки, асинхронное программирование?.. Акторы?..

S>>>Редчайший случай, заказ на тысячу позиций. Какого характерного размера это "гигантское" сообщение? В непожатом JSON оно будет занимать полсотни килобайт. Семечки.

S>·>В высокопроизводительных системах — порядка сотен байт.
S>Вы говорите о системах, в которых производительность возведена в кумиры, и ей в жертву приносят всё, включая здравый смысл.
S>Нет, я не против — мне самому очень нравится архитектура LMAX Disruptor. Но не как задача, которую решают пацаны, а как способ посмотреть на задачу под другим углом.
Это по сути как реальность работает на низком уровне. REST-запрос — это два коррелированных сообщения запрос-ответ. А если не ограничиваться этим, то можно слать сообщения куда-то, и как-то реагировать на ответы откуда-то.

S>А в традиционном бизнесе нет нужды опередить соперника на долю наносекунды. Зато важно, чтобы инварианты не разъехались.

Ну в трейдинге тоже ничего разъезжаться не должно. Если разъедится — дело дрянь и серьёзные убытки.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[50]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 23.02.26 07:01
Оценка:
Здравствуйте, ·, Вы писали:

·>Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс.

Разработка — она и в африке разработка. А вот проектирование...

·>Не понял, что именно "не факт".

Не факт, что мы смирились с заказами в "смешанном статусе" как с первоклассным состоянием системы.

·>Это я плохо объяснил. Батчинг — это не требование бизнеса, а механизм повышения перформанса. Это когда запрос в субд на резервацию одной строки и ста строк занимает примерно одно и то же время, т.к. бОльшая часть времени уходит на всякие накладные расходы взаимодействия сервиса резервации и субд.

Про этот батчинг я ничего не говорил. Я говорил про семантику объединения операций в ACID-группу и техническую реализацию такой семантики.

·>Следовательно, выгоднее отправлять стейтменты всегда по 100 штук, вне зависимости от того, разные это заказы или один большой.

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

·>Т.е. вначале разеделяем заказы на мелкие сообщения резервации, сообщения (возможно батчами) засылаем в очередь. Эта очередь потом разгребается (возможно батчами) и ответы отсылаются таким же способом. Ответы резервации аггрерируются в суммарный статус заказа.

Увеличиваем латентность, уменьшаем throughput.

·>Что-то вроде многопоточки, асинхронное программирование?.. Акторы?..

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

·>Это по сути как реальность работает на низком уровне. REST-запрос — это два коррелированных сообщения запрос-ответ. А если не ограничиваться этим, то можно слать сообщения куда-то, и как-то реагировать на ответы откуда-то.

Ну так как только мы перестаём "ограничиваться", так сразу теряем всю привычную REST-семантику с её гарантиями.
·>Ну в трейдинге тоже ничего разъезжаться не должно. Если разъедится — дело дрянь и серьёзные убытки.
В трейдинге инварианты значительно более простые.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[50]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.02.26 08:19
Оценка: +1
Здравствуйте, ·, Вы писали:

·>Здравствуйте, Sinclair, Вы писали:


S>>>>О да. Двадцать миллисекунд придётся подождать.

S>>·>Для FX-трейдинга — задержка 20мс — это production issue. Типичная latency — 0.3мс.
S>>К счастью, FX-трейдинг — нишевая штука. Нафиг не нужен никому, кроме спекулянтов.
S>>Поэтому рассматривать его требования как основу архитектуры типичного бизнес-приложения не нужно.
·>Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс.

Разница огромная.
В бизнес-приложениях:
— ключевую роль играет стоимость разработки. Никому не нужно бизнес-приложение, которое стоит дороже самого бизнеса, какое бы крутое это приложение не было. Это линейный фактор.
— не надо соревноваться по скорости с другими системами. Поэтому перформанс в них — гигиенический фактор. Достигая определенного уровня он перестает давать ценность. Зато стоимость каждого следующего улучшения перформанса растет геометрически. Если разницу между 100мс и 20мс никто глазами не заметит, то нет смысла делать даже 20 мс.
— высокие требования к консистентности данных. потерять изменения в бизнес-приложении можно один максимум раз, после второго раза будет уже другой исполнитель. Это must-фактор. Нельзя иметь надежность ниже определенного уровня, в сценариях, которые встречаются на практике.

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

Ну и количество сценариев в бизнес-приложении примерно на порядок или на два меньше, чем в трейдинге. В трейинге по сути один сценарий — приход информации от биржи, а в результате надо выдать пачку ордеров
Re[51]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 23.02.26 10:52
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс.

S>Разработка — она и в африке разработка. А вот проектирование...
... часть разработки.

S>·>Не понял, что именно "не факт".

S>Не факт, что мы смирились с заказами в "смешанном статусе" как с первоклассным состоянием системы.
Что за смешанный статус? Есть "начали резервацию", "закончили резервацию".

S>·>Это я плохо объяснил. Батчинг — это не требование бизнеса, а механизм повышения перформанса. Это когда запрос в субд на резервацию одной строки и ста строк занимает примерно одно и то же время, т.к. бОльшая часть времени уходит на всякие накладные расходы взаимодействия сервиса резервации и субд.

S>Про этот батчинг я ничего не говорил. Я говорил про семантику объединения операций в ACID-группу и техническую реализацию такой семантики.
Не понятно тогда зачем тебе именно надо в данной задаче объединять операции в одну ACID-группу. Из обоснований я пока заметил лишь: "SQL прекрасен батч-операциями".

S>·>Следовательно, выгоднее отправлять стейтменты всегда по 100 штук, вне зависимости от того, разные это заказы или один большой.

S>Не совсем. Если вы попробуете сделать 1 коммит на пачку заказов, то в случае неудачи откатится вся транзакция, включая и те заказы, которым всего хватает.
Мы вроде рассматриваем вариант "просто добавляется ещё один предикат во where". Неудача и откат тут будет лишь в случае системной ошибки.

S>·>Т.е. вначале разеделяем заказы на мелкие сообщения резервации, сообщения (возможно батчами) засылаем в очередь. Эта очередь потом разгребается (возможно батчами) и ответы отсылаются таким же способом. Ответы резервации аггрерируются в суммарный статус заказа.

S>Увеличиваем латентность, уменьшаем throughput.
Почему уменьшаем?? Для мелких ордеров throughput увеличится засчёт уменьшения числа раундтрипов.

S>·>Что-то вроде многопоточки, асинхронное программирование?.. Акторы?..

S>Ага. Вот мы сейчас изобретаем некоторый способ проектировать подобные системы так, чтобы потенциальные лайвлоки ловить не в продакшне, а в дизайн-тайме.
S>Что-то мне подсказывает, что у вас такого способа нет
Ах, вспомнил. Ты же говорил — workflow, который у тебя уже есть всё равно. Вот его и достаточно. Машина состояний, реагирует на сообщения, меняет внутреннее состояние, посылает сообщения.

S>·>Это по сути как реальность работает на низком уровне. REST-запрос — это два коррелированных сообщения запрос-ответ. А если не ограничиваться этим, то можно слать сообщения куда-то, и как-то реагировать на ответы откуда-то.

S>Ну так как только мы перестаём "ограничиваться", так сразу теряем всю привычную REST-семантику с её гарантиями.
"привычную" — ключевое слово.

S>·>Ну в трейдинге тоже ничего разъезжаться не должно. Если разъедится — дело дрянь и серьёзные убытки.

S>В трейдинге инварианты значительно более простые.
Трейдинг он разный..
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[51]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 23.02.26 11:02
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>·>Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс.

G>Разница огромная.
G>В бизнес-приложениях:
G>- ключевую роль играет стоимость разработки. Никому не нужно бизнес-приложение, которое стоит дороже самого бизнеса, какое бы крутое это приложение не было. Это линейный фактор.
G>- не надо соревноваться по скорости с другими системами. Поэтому перформанс в них — гигиенический фактор. Достигая определенного уровня он перестает давать ценность. Зато стоимость каждого следующего улучшения перформанса растет геометрически. Если разницу между 100мс и 20мс никто глазами не заметит, то нет смысла делать даже 20 мс.
Пока ВНЕЗАПНО не наступает какая-нибдуь чёрная пятница или новогодние распродажи.

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

В трейдинге плюс к этому ещё и штрафы влепят от всяких FCA.

G>В трейдинге другие факторы:

G>- перформанс это must фактор, так как если ты работаешь медленее конкурентов, то ты просто не достигаешь целей.
G>- консистентность вообще можно не учитывать, так как все денные в программе уже устарели
G>- стоимость разработки программы на фоне размера счета — погрешность округления
Ээ, так ещё есть трейдинг по другую сторону баррикады. Например, сама биржа по сути те же заказы (orders) и склад (биржевой стакан). И продать больше — никак нельзя.

G>Ну и количество сценариев в бизнес-приложении примерно на порядок или на два меньше, чем в трейдинге.

"меньше"? Ты хотел сказать "больше"?

G> В трейинге по сути один сценарий — приход информации от биржи, а в результате надо выдать пачку ордеров

Ну вот по обсуждаемой теме есть ещё например portfolio/index трейдинг, где ордера бывают на десяток тысяч позиций. И сложные сценарии price negotiation, споттинг, букинг и т.п.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 23.02.2026 11:04 · . Предыдущая версия .
Re[52]: Помогите правильно спроектировать микросервисное при
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 23.02.26 16:11
Оценка:
Здравствуйте, ·, Вы писали:

·>Здравствуйте, gandjustas, Вы писали:


G>>·>Так ведь по большому счёту разница невелика с т.з. усилий на разработку. А перформанс, он и в африке перформанс.

G>>Разница огромная.
G>>В бизнес-приложениях:
G>>- ключевую роль играет стоимость разработки. Никому не нужно бизнес-приложение, которое стоит дороже самого бизнеса, какое бы крутое это приложение не было. Это линейный фактор.
G>>- не надо соревноваться по скорости с другими системами. Поэтому перформанс в них — гигиенический фактор. Достигая определенного уровня он перестает давать ценность. Зато стоимость каждого следующего улучшения перформанса растет геометрически. Если разницу между 100мс и 20мс никто глазами не заметит, то нет смысла делать даже 20 мс.
·>Пока ВНЕЗАПНО не наступает какая-нибдуь чёрная пятница или новогодние распродажи.
ВНЕЗАПНО длительность транзакции напрямую не влияет на масштабируемость систем. По сути все распределенные транзакции длительность увеличивают, но также увеличивают и пропускную способность.


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

·>В трейдинге плюс к этому ещё и штрафы влепят от всяких FCA.
За что?

G>>В трейдинге другие факторы:

G>>- перформанс это must фактор, так как если ты работаешь медленее конкурентов, то ты просто не достигаешь целей.
G>>- консистентность вообще можно не учитывать, так как все денные в программе уже устарели
G>>- стоимость разработки программы на фоне размера счета — погрешность округления
·>Ээ, так ещё есть трейдинг по другую сторону баррикады. Например, сама биржа по сути те же заказы (orders) и склад (биржевой стакан). И продать больше — никак нельзя.
В на бирже чуть проще, там же ордеры по одному инструменту по сути уже сериализованы этим самым "стаканом". остается только инженерная задача распределения "стаканов" по сервакам так, чтобы максимизировать надежность и скорость записи (шардирование)

G>>Ну и количество сценариев в бизнес-приложении примерно на порядок или на два меньше, чем в трейдинге.

·>"меньше"? Ты хотел сказать "больше"?
Да, не исправил при переписывании

G>> В трейинге по сути один сценарий — приход информации от биржи, а в результате надо выдать пачку ордеров

·>Ну вот по обсуждаемой теме есть ещё например portfolio/index трейдинг, где ордера бывают на десяток тысяч позиций. И сложные сценарии price negotiation, споттинг, букинг и т.п.
Что из этого не укладывается в сценарий: получить данные от биржи и выдать ордеры?
Re[53]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 23.02.26 16:48
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>·>Пока ВНЕЗАПНО не наступает какая-нибдуь чёрная пятница или новогодние распродажи.

G>ВНЕЗАПНО длительность транзакции напрямую не влияет на масштабируемость систем. По сути все распределенные транзакции длительность увеличивают, но также увеличивают и пропускную способность.
А я и не предлагал распределённые транзакции.

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

G>·>В трейдинге плюс к этому ещё и штрафы влепят от всяких FCA.
G>За что?
За неимение надёжности определённого уровня при предоставлении финансовых услуг.

G>·>Ээ, так ещё есть трейдинг по другую сторону баррикады. Например, сама биржа по сути те же заказы (orders) и склад (биржевой стакан). И продать больше — никак нельзя.

G>В на бирже чуть проще, там же ордеры по одному инструменту по сути уже сериализованы этим самым "стаканом".
Есть ещё mass order cancel/replace которые валят от market makers.

G>остается только инженерная задача распределения "стаканов" по сервакам так, чтобы максимизировать надежность и скорость записи (шардирование)

Круто наверное это твоё шардирование, особенно когда 95% трафика сыплет на какую-нибудь одну особо популярную сковородку типа EURUSD в первые секунды какого-нибудь очередного market event.

G>>> В трейинге по сути один сценарий — приход информации от биржи, а в результате надо выдать пачку ордеров

G>·>Ну вот по обсуждаемой теме есть ещё например portfolio/index трейдинг, где ордера бывают на десяток тысяч позиций. И сложные сценарии price negotiation, споттинг, букинг и т.п.
G>Что из этого не укладывается в сценарий: получить данные от биржи и выдать ордеры?
Получаем RFQ, прогоняем разные pre-trade проверки, check fail approvals, шлём запрос прайсерам, выбираем лучшую цену, запускаем проверки цены, публикуем цену, обрабатываем client counter, dealer counter, manual intervention, выполняем last-look, выполняем вычисление spot-цены, отсылаем heding, отсылаем booking.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Re[52]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.02.26 05:35
Оценка:
Здравствуйте, ·, Вы писали:

·>... часть разработки.

Ну, ок
·>Что за смешанный статус? Есть "начали резервацию", "закончили резервацию".
А что у нас между первым и вторым? Когда половина позиций зарезервирована, а половина — нет.
Оплату мы тоже берём по частям?

·>Не понятно тогда зачем тебе именно надо в данной задаче объединять операции в одну ACID-группу. Из обоснований я пока заметил лишь: "SQL прекрасен батч-операциями".

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

·>Мы вроде рассматриваем вариант "просто добавляется ещё один предикат во where". Неудача и откат тут будет лишь в случае системной ошибки.

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

S>>Увеличиваем латентность, уменьшаем throughput.

·>Почему уменьшаем?? Для мелких ордеров throughput увеличится засчёт уменьшения числа раундтрипов.
За счёт накладных расходов на "переоркестрацию" заказов.

·>Ах, вспомнил. Ты же говорил — workflow, который у тебя уже есть всё равно. Вот его и достаточно. Машина состояний, реагирует на сообщения, меняет внутреннее состояние, посылает сообщения.

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

S>>Ну так как только мы перестаём "ограничиваться", так сразу теряем всю привычную REST-семантику с её гарантиями.

·>"привычную" — ключевое слово.
Именно.

·>Трейдинг он разный..

Ну, когда в руках молоток — всё кажется гвоздями. И это, к сожалению, работает для любого подхода.
Когда в руках reader_writer_lock, то кажется, что невозможно обойтись без захвата/отпускания N*M блокировок, где N — количество позиций заказов в системе, а M — количество стадий конвеера обработки заказа.
Когда в руках — LMAX Disruptor, то кажется, что любую деятельность можно представить как цепочку очередей событий.
И гарантии, которые в одном подходе легко и элегантно обеспечиваются "по построению", в другом подходе представляют собой нетривиально доказываемую (если вообще доказуемую) теорему.
И, соответственно, наоборот.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[53]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 24.02.26 12:16
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Что за смешанный статус? Есть "начали резервацию", "закончили резервацию".

S>А что у нас между первым и вторым? Когда половина позиций зарезервирована, а половина — нет.
S>Оплату мы тоже берём по частям?
Продолжаем резервацию до победного конца. Ты ж сам тут всё описывал: https://rsdn.org/forum/design/9057018
Автор: Sinclair
Дата: 16.02 19:51
. Мой поинт был лишь в том, что нет никакой нужды при таком подходе пытаться всё заталкивать в одну транзакцию произвольного размера.

S>·>Не понятно тогда зачем тебе именно надо в данной задаче объединять операции в одну ACID-группу. Из обоснований я пока заметил лишь: "SQL прекрасен батч-операциями".

S>Прекрасность SQL играет тогда, когда мы приняли решение объединять операции. Во время обработки этой ACID-группы нам противопоказаны любые коммуникации, т.к. они ведут к неограниченным задержкам.
А зачем мы приняли такое решение?

S>·>Мы вроде рассматриваем вариант "просто добавляется ещё один предикат во where". Неудача и откат тут будет лишь в случае системной ошибки.

S>Да, это способ развернуть задачу задом наперёд — когда мы, к примеру, резервируем за одно обращение группу позиций по одному и тому же товару для ряда заказов.
Именно. Когда мы не принимаем такие решения на пустом месте, у нас появляется свобода выбора и простор для различных оптимизаций.

S>>>Увеличиваем латентность, уменьшаем throughput.

S>·>Почему уменьшаем?? Для мелких ордеров throughput увеличится засчёт уменьшения числа раундтрипов.
S>За счёт накладных расходов на "переоркестрацию" заказов.
Эти накладные расходы не требуют сетевых операций или блокировок. А значит практически нулевые, наносекундного диапазона.

S>·>Ах, вспомнил. Ты же говорил — workflow, который у тебя уже есть всё равно. Вот его и достаточно. Машина состояний, реагирует на сообщения, меняет внутреннее состояние, посылает сообщения.

S>Набор гарантий для такой машины очень небольшой. Доказать, что в такой системе любой заказ рано или поздно станет либо "отказанным", либо "зарезервированным" — то ещё упражнение.
S>Даже если отказаться от гарантий liveness и сосредоточиться на safety — что мы никогда не получим дедлок — это представляет собой крайне сложную задачу, для которой нет общего решения.
Почему? Чем это будет отличаться от того, что ты там выше по пунктам расписывал?

S>·>"привычную" — ключевое слово.

S>Именно.

S>·>Трейдинг он разный..

S>Ну, когда в руках молоток — всё кажется гвоздями. И это, к сожалению, работает для любого подхода.
S>Когда в руках reader_writer_lock, то кажется, что невозможно обойтись без захвата/отпускания N*M блокировок, где N — количество позиций заказов в системе, а M — количество стадий конвеера обработки заказа.
S>Когда в руках — LMAX Disruptor, то кажется, что любую деятельность можно представить как цепочку очередей событий.
S>И гарантии, которые в одном подходе легко и элегантно обеспечиваются "по построению", в другом подходе представляют собой нетривиально доказываемую (если вообще доказуемую) теорему.
S>И, соответственно, наоборот.
Как раз не молоток. "Это другое". Если вспомнить историю, то этот подход популяризировался на базе Mechanical Sympathy. То есть не построили красивую абстрактность из мира идей того что "нам привычно" и пошли размахивать как молотком, мол, все наши пользователи пойдут в один сервер, или пусть идут нафик. Вот и HTTP, REST у нас, а потом совершенно ВНЕЗАПНО появились Keep Alive, pipelining, WebSocket, QUIC.
А Mechanical Sympathy — это как раз шаг к тому, как реально работают реальные системы в физическом мире. Потому это и есть дорога к сабж.
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 24.02.2026 12:17 · . Предыдущая версия .
Re[54]: Помогите правильно спроектировать микросервисное при
От: Sinclair Россия https://github.com/evilguest/
Дата: 25.02.26 01:53
Оценка:
Здравствуйте, ·, Вы писали:

·>Продолжаем резервацию до победного конца.

Это как раз и означает, что у заказа появляется статус "частично зарезервирован", который нужно учитывать во всех смежных системах.

·>А зачем мы приняли такое решение?

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

·>Эти накладные расходы не требуют сетевых операций или блокировок. А значит практически нулевые, наносекундного диапазона.

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

·>Почему? Чем это будет отличаться от того, что ты там выше по пунктам расписывал?

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

·>Как раз не молоток. "Это другое". Если вспомнить историю, то этот подход популяризировался на базе Mechanical Sympathy. То есть не построили красивую абстрактность из мира идей того что "нам привычно" и пошли размахивать как молотком, мол, все наши пользователи пойдут в один сервер, или пусть идут нафик. Вот и HTTP, REST у нас, а потом совершенно ВНЕЗАПНО появились Keep Alive, pipelining, WebSocket, QUIC.

·>А Mechanical Sympathy — это как раз шаг к тому, как реально работают реальные системы в физическом мире. Потому это и есть дорога к сабж.
Всякий раз, как я вижу упоминание "как реально работают реальные системы в реальном мире", я чую булшит. Это и про ООП говорили, и даже немножко про фп.
Нам не особо нужно моделировать физический мир. Нам нужно моделировать решение задачи И какой формализм выбрать — state propagation или communicating automata — зависит от того, какие конкретно цели мы преследуем.
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[55]: Помогите правильно спроектировать микросервисное при
От: · Великобритания  
Дата: 25.02.26 10:26
Оценка:
Здравствуйте, Sinclair, Вы писали:

S>·>Продолжаем резервацию до победного конца.

S>Это как раз и означает, что у заказа появляется статус "частично зарезервирован", который нужно учитывать во всех смежных системах.
Не понял где и откуда и зачем.

S>·>А зачем мы приняли такое решение?

S>Затем, чтобы обеспечить инварианты, принятые в бизнес-домене.
Давай конкретно. Вот у тебя там был процесс из 11 пунктов. Что конкретно не будет обеспечиваться, если мы резервацию будем делать построчно?
Вот там у тебя было: "Конкретный SQL тут не так важен — важна его атомарность. Если мы всё же довели транзакцию до конца и получили в сервисе А положительный ответ от СУБД". С этим я не спорю. Я лишь уточнил, что тебе важна атомарность изменения одной строки stock и соответствующей ей строки reservationItems. Но нам не важна атомарность между всеми позициями заказа. Мы просто загоняем каждую позицию в отдельную независимую транзакцию в тупом for-цикле.

S>·>Именно. Когда мы не принимаем такие решения на пустом месте, у нас появляется свобода выбора и простор для различных оптимизаций.

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

S>·>Эти накладные расходы не требуют сетевых операций или блокировок. А значит практически нулевые, наносекундного диапазона.

S>Вообще-то требуют, т.к. нам нужно обеспечить какие-то простейшие вещи — например, что кто-то не пытается изменить состав заказа одновременно с тем, как отрабатывает наш код резервирования.
В твоём решении с одной транзакцией это тоже надо делать. Впрочем, это делается просто — изменять состав заказа можно только в определённых статусах заказа. Если статус "резервируется" — менять нельзя, ни в моём случае, ни в твоём. Та же песня.

S>И если для одиночного заказа это "точечная" блокировка, которая не влияет на обработку остальных заказов, то "перегруппировка позиций" уже затрагивает пачку заказов.

Не вижу как именно затрагивает.

S>·>Почему? Чем это будет отличаться от того, что ты там выше по пунктам расписывал?

S>Потому что один процесс у вас копает ямы, другой — закапывает. А тот, который должен был сажать деревья, сообщение не получил. Каждый процесс пашет в соответствии со своим ТЗ, и все юнит-тесты проходят, а результата нет.
По ТЗ процесс закапывающий ямы получает сообщения от процесса-сажателя. С какого бодуна закапыватель чего-то начнёт делать до отсылки сообщения ему?

S>·>Как раз не молоток. "Это другое". Если вспомнить историю, то этот подход популяризировался на базе Mechanical Sympathy. То есть не построили красивую абстрактность из мира идей того что "нам привычно" и пошли размахивать как молотком, мол, все наши пользователи пойдут в один сервер, или пусть идут нафик. Вот и HTTP, REST у нас, а потом совершенно ВНЕЗАПНО появились Keep Alive, pipelining, WebSocket, QUIC.

S>·>А Mechanical Sympathy — это как раз шаг к тому, как реально работают реальные системы в физическом мире. Потому это и есть дорога к сабж.
S>Всякий раз, как я вижу упоминание "как реально работают реальные системы в реальном мире", я чую булшит. Это и про ООП говорили, и даже немножко про фп.
Говорили то, что говоришь и ты: "можно представить". Тут ведь как... можно представить, а ведь можно и не представить!
Взаимодействие в физическом мире происходит посредством посылки сигналов-сообщений. Открой учебник по ТО — там будут signals-events. Произошло событие — полетел фотон. Долетел куда-то — произошло новое событие, фотоны поглотились, излучились новые.

S>Нам не особо нужно моделировать физический мир. Нам нужно моделировать решение задачи И какой формализм выбрать — state propagation или communicating automata — зависит от того, какие конкретно цели мы преследуем.

Вот только реальности всё равно какой формализм ты выбрал — по проводу пойдёт именно коммуникационный сигнал, а не состояние. Тебе придётся построить кривенькую модель организации передачи состояния посредством сигналов. И удивиться потом, чегой-то оно тормозит и состояния разъезжаются...
но это не зря, хотя, может быть, невзначай
гÅрмония мира не знает границ — сейчас мы будем пить чай
Отредактировано 25.02.2026 11:12 · . Предыдущая версия . Еще …
Отредактировано 25.02.2026 10:57 · . Предыдущая версия .
Отредактировано 25.02.2026 10:50 · . Предыдущая версия .
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.