Comments 48
В данном случае проблема не в разделении ответственности, а в database integration antipattern — доступ двух разных приложений к одной таблице в БД. Почему это проблема? Потому, что, в отличие от сетевого RPC, интерфейс взаимодействия между системами через таблицу в БД сложен и избыточен, поэтому его трудно специфицировать и отладить.
Разделение ответственности — это хорошо, но самый важный принцип в микросервисной системе — изоляция мутабельного состояния (базы данных и кеши) и максимально жесткая спецификация интерфейсов между микросервисами.
Странно, что это за база и удивлен про hybernate.+1024
Вдвойне странно, что столько людей не видят в этом большой проблемы. Не говоря уже о том, что правильное использование сиквенсов будет нагружать БД ничуть не больше, чем такой «оптимизированный» Hibernate.
Эммм, всё немного сложнее. Базы бывают разные и с разным набором фич, при массовой вставке дополнительный roundtrip для инкремента сиквенса может значительно увеличить время вставки. Особенно если пинг до базы ненулевой.
И да, хибернейт по дефолту не рассчитан на параллельную модификацию базы чужими приложениями. Это необходимое условие для работы его знаменитых кешей первого и второго уровня.
Базы бывают разные и с разным набором фичЯ сразу скажу: я в ОРМах не разбираюсь, никогда ими не пользовался, так что знаю только то, что Мойша напел. Типа как в этой статье. А поёт Мойша гнусаво, и в ноты не попадает. И знает только три блатных аккорда.
Вот тут главное, что мне не нравится в ОРМах. Есть некоторое количество разных СУБД. С разными фичами. И тут мы такие берем ОРМ, который работает со всеми универсально, и в итоге получается, что доступны только те фичи, которые есть у всех. То есть самая крутая и навороченная СУБД за сотни нефти низводится до самой дешманской, потому что «у нас же
В том же оракле, например, есть кеширование сиквенсов. Я сам лично сталкивался с проблемой, когда (слава богу, никаких ОРМов не бло) поднятие кэша с дефолтных 20 до 1000 прекрасно убирало все проблемы с доступом к сиквенсу. А тут получается так: сначала платим 100500 денег за СУБД, потом берем ОРМ, а потом оказывается, что чукча обманул таксиста.
И да, хибернейт по дефолту не рассчитан на параллельную модификацию базы чужими приложениями. Это необходимое условие для работы его знаменитых кешей первого и второго уровня.Ваши слова меня огорчают. 40 лет развития многопользовательского доступа в СУБД закончились тем, что «а давайте работать с БД в однопользовательском режиме».
Я всё еще надеюсь, что это просто я что-то не понимаю, и мне кто-нибудь объяснит.
А самая главное — статья не о этом. Она — о Феномене Размазывания Ответственности. Постарайтесь в ней увидеть именно этот посыл.
Ну а дальше вопрос архитектуры — где держим эту логику использующую какие-то уникальные фичи БД? В слое бизнесовой логики? В data access layer? Запихиваем еще глубже в storage procedure в бд?
Не претендую на истину, все на своем опыте работы с постгресом, ef.core и неумением последнего в jsonb.
Сами того не ведая, они поработали на Феномен Размывания Ответственности.
Стали копать глубже. Детальный анализ и сопоставление лог-файлов показали
ИМХО, при ошибках на проде анализ логов это первое, с чего стоит начинать.
Но это мои предположения.
Как-раз в нулевых моим основным занятием были базы данных, всякие Ораклы и энтерпрайз юзающий их.
И тогда народ не отдавал БД на откуп фреймворкам, а отдельно ее проектировали всякие архитекторы и работали с базой напрямую, без каких-либо ORM, а за подоход этого хибернейта: угадывание праймари кея, могли забить канделябром по голове до смерти :)
Хороший тон было всегда рассчитывать на то, что параллельно с тобой с базой работает куча клиентов через что угодно и если две операции делают что-то, что должно быть атомарным, надо обеспечить атомарность средствами БД, рассчитывать на то, что между операциями пройдут микросекунды, никто другой влезть не успеет могли только новички.
"И когда во второй компоненте возникла необходимость записи в таблицу, из-за незначительности операции было принято прагматическое решение реализовать это прямо в данной компоненте самым простейшим способом, а не трогать стабильно работающую первую компоненту."
Прагматичное? Серьёзно?
Писать напрямую в БД, да ещё зная что есть другие части системы, которые пишут в эту же БД. Очевидно же, что проблема возникнет рано или поздно
Писать напрямую в БД, да ещё зная что есть другие части системы, которые пишут в эту же БД. Очевидно же, что проблема возникнет рано или поздно
Я не отстаиваю подход с прямой записью а банк данных из разных компонент, но это объективная реальность многих Enterprise систем. И если Вы углубитесь в документацию Oracle, то увидите, какие они предпринимают усилия, чтобы именно так их ДБ и использовали.
Сама по себе параллельная запись в БД из нескольких мест — это ещё не проблема. Но при этом совершенно очевидно, что любое ПО, с такой БД работающее, обязано учитывать тот факт, что БД может быть изменена чем-то помимо него. Так что это не проблема "размазывания ответственности", это проблема плохого проектирования. И ответственные за проблему вполне себе четко определяются в зависимости от конкретной ситуации, на мой взгляд. В вашем случае:
И когда во второй компоненте возникла необходимость записи в таблицу, из-за незначительности операции было принято прагматическое решение реализовать это прямо в данной компоненте самым простейшим способом, а не трогать стабильно работающую первую компоненту.
- Если человек, который принял такое решение, ЗНАЯ, что есть другие пишущие в БД компоненты, не согласовал свое решение с ответственными за работу тех компонент — ответственнен он.
- Если он не знал и не мог знать, что есть другие пишущие в БД компоненты — ответственнен тот, кто организует рабочий процесс и не дал ему нужную информацию.
- Если он свое решение согласовал с нужными людьми, а те прощелкали возможные последствия — ответственны они.
А если два или все три пункта — это одни и те же люди, то вообще не о чем говорить. :)
Например — текучка кадров, ответственных в данный момент просто нет или новые ответственные еще не вработались.
Или: компонента не меняется, ответственные переводятся на другие фронты.
Или: таблиц так много, что ответственный проглядел коварно заданное значение.
Задним умом мы все сильны. (Шутка).
Вы бы лучше не виноватых искали, а думали как не допустить подобных проблем. То, что все рано или поздно косячат — ни для кого не секрет.
Сейчас все меньше систем строится с нуля, на зелёной лужайке, когда всё можно заранее спокойно продумать.
Как раз монолит проще разбить, чем с нуля продумывать, т.к. видна вся картина целиком. Часто советуют пока не понятно как делить, писать монолит.
А вот при внесении дополнений можно наделать ошибок и «размазать» ответственность.
Эти ошибки это и есть ошибки проектирования, архитектуры и т.д. Если не можете развести микросервисы так чтобы они не лезли в чужие БД (а это иногда и правда непросто сделать), так может лучше оставаться на монолите и не играть в модные тренды?
И хорошо бы попробовать в этом спокойно разобраться.
Свести все к «раздолбайству» ничего не даст. Вряд ли все участники процитированной дискуссии на StackOverflow и других похожих дискуссий были «раздолбаями».
Не все спроектированные самолеты взлетают, и многие из них долго доводят до кондиции уже после начала эксплуатации. У сложных технических систем свои законы. В том числе и у ИТ-систем.
Речь не идёт о способах разбиения монолита на части
Я лишь о том, что ваша проблема «размазывания ответственности» на самом деле проблема неправильного разбиения, которая не нова и давно известна.
Свести все к «раздолбайству» ничего не даст.
Я вроде и не пытался. При чем тут раздолбайство? Я хотел сказать, что декомпозиция на микросервисы — это сложная техническая задача.
Не все спроектированные самолеты взлетают, и многие из них долго доводят до кондиции уже после начала эксплуатации.
Доводят, но не приматыванием деталей скотчем. Ваш вариант с чтением двумя микросервисами из одной БД это именно костыль, примотанный скотчем. Можно было перепроектировать сервисы и, например, выделить еще один, который будет один работать с проблемной таблицей. И тогда даже странный кейс с предсказыванием id на hibernate будет работать.
Но и про скотч Вы зря. Во-первых, как проблема была решена, я не писал вообще. И не об этом я хотел рассказать, а о феномене, который можно замечать, а можно не замечать и все сводить к конкретным проблемам и решениям.
Я хотел, чтобы читатели рассмотрели описанную проблему и схожие проблемы на другом уровне абстракции.
Про раздолбайство писал я, но не применительно к дискуссии на StackOverflow (вообще не понимаю, как вы ухитрились мой комментарий привязать к ней), а применительно к конкретно тем проблемам, которые описывали именно вы, сначала в посте, потом в диалоге со мной. Они, на мой взгляд, прекрасно сводятся к конкретным ошибкам конкретных людей, либо непосредственно на уровне разработки, либо на управленческом, и ситуации "в принципе никто не виноват (т.е. все всё сделали правильно), но ничего не работает" тут нет даже близко.
К сожалению, по некоторым комментариям можно предположить, что этот посыл либо не всеми понят, либо с ним не все согласны.
как проблема была решена, я не писал вообще.
Так я, собственно, не о решении, а о самой проблеме, о том почему она появилась.
а о феномене, который можно замечать, а можно не замечать
Для меня описанное в статье — проблема, но не феномен. Далее, видимо, уже начнется философия, поэтому заканчиваю дискуссию.
И когда во второй компоненте возникла необходимость записи в таблицу, из-за незначительности операции было принято прагматическое решение реализовать это прямо в данной компоненте самым простейшим способом, а не трогать стабильно работающую первую компоненту.
Сурово, это как на полном ходу поезда дёрнуть стоп-кран, если мне резко понадобилось выйти именно тут.
Впрочем, любые аналогии имеют свои границы.
Можно назвать это как угодно, но на мой взгляд, самое культурное определение этого решения — костыль, но никак не «прагматическое решение».
A Hibernate работает по умолчанию при записи следующим образом. Чтобы оптимизировать процесс записи, она запрашивает в индексе значение следующего primary key один раз, и после этого записывает несколько раз просто увеличивая значение key (по умолчанию — 10 раз).
О — оптимизация. :)
Поразительная корреляция с вчерашней статьёй про ORM.
Чтобы так не было, используйте, пожалуйста, следующие простые правила:
Если работаете с Oracle, то повесьте на таблицу триггер, который будет генерировать значение синтетического первичного ключа, используя выделенную последовательность (sequence)
Если работаете с MS SQL, используйте свойство IDENTITY на столбце с синтетическим первичным ключом прямо во время создания таблицы.
Всегда, когда нету очень веских причин против, используйте в каждой таблице синтетический первичный ключ целочисленного типа.
А если использовать конструкции вроде newId = SELECT MAX(ID) FROM MyTable + 1, рано или поздно проблемы возникнут. Потому что генерация первичного ключа - слишком важная часть логики, чтобы отдавать её любому постороннему коду. Этим должна заниматься сама СУБД.
Об ошибках, возникающих ниоткуда и в которых некого винить: Феномен Размазывания Ответственности