Об ошибках, возникающих ниоткуда и в которых некого винить: Феномен Размазывания Ответственности

    Мультимедиа михер

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

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

    Работала компонента хорошо, и несколько лет к её коду никто не притрагивался.

    Но вот однажды, ни с того ни с сего, начали твориться с компонентой странные дела.

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

    Начали анализировать ситуацию.Проверили компоненту под большой нагрузкой. Работает хорошо. Повторили достаточно объёмные интеграционные тесты. В интеграционных тестах наша компонента сработала нормально.

    Одним словом, ошибка приходила непонятно когда и непонятно откуда.

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

    Компонента писала данные в таблицу с помощью Hibernate, и иногда Hibernate при попытке записать очередную строчку заявляла о constraint violation.

    Не стану утомлять читателей дальнейшими техническими деталями и сразу расскажу о сути ошибки. Оказалось что в вышеупомянутую таблицу пишет не только наша компонента, но иногда (крайне редко) некая другая компонента. И делает она это совсем просто, с помощью простого SQL INSERT statement. A Hibernate работает по умолчанию при записи следующим образом. Чтобы оптимизировать процесс записи, она запрашивает в индексе значение следующего primary key один раз, и после этого записывает несколько раз просто увеличивая значение key (по умолчанию — 10 раз). И если случилось так, что после запроса вторая компонента «встревала» в процесс и записывала данные в таблицу, используя при этом следующее значение primary key, то следовавшая за ней попытка записи со стороны Hibernate приводила к constraint violation.
    Если вас интересуют технические детали, посмотрите их внизу

    Технические детали
    .
    Код класса начинался примерно так:

    @Entity
    @Table(name="PRODUCT_XXX")
    public class ProductXXX {
                   
                    @Id
                    @Basic(optional=false)
                    @Column(
                                    name="PROD_ID",
                                    columnDefinition="integer not null",
                                    insertable=true,
                                    updatable=false)
                    @SequenceGenerator(
                                    name="GEN_PROD_ID",
                                    sequenceName="SEQ_PROD_ID",
                                    allocationSize=10)
                    @GeneratedValue(
                                    strategy=GenerationType.SEQUENCE,
                                    generator="GEN_PROD_ID")
                    private long prodId;
    

    Одно из обсуждений схожей проблемы на Stackoverflow:
    https://stackoverflow.com/questions/12745751/hibernate-sequencegenerator-and-allocationsize

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

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

    В известном смысле можно сказать, что в появлении ошибки действительно никто не был виноват.

    Или всё же это не так?

    Наблюдения и размышления


    После обнаружения истинной причины ошибки она была исправлена.

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

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

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

    Распределенные системы, особенно концепция микросервисов, эффективно помогли решить ряд проблем, присущих монолитным системам. Однако, как это не парадоксально, разнесение ответственности по отдельным сервисам провоцирует обратный эффект. Компоненты теперь «живут» по возможности независимо друг от друга. И неизбежно возникает соблазн, внося большие изменения в одну компоненту, «привинтить прямо здесь» немного функциональности, которую лучше бы реализовать в другой компоненте. Этим быстрее достигается конечный эффект, сокращается объем согласований и тестирования. Так, от изменения к изменению, компоненты обрастают несвойственными им фичами, одни и те же внутренние алгоритмы и функции дублируются, возникает многовариантность решения задач (а иногда — и их недетерминированность). Другими словами, распределенная система деградирует со временем, но иначе, чем монолитная.

    «Размазывание» ответственности по компонентам в больших системах, состоящих из многих сервисов — одна из из типичных и болезненных проблем современных распределенных систем. Ещё больше ситуацию усложняют и запутывают совместно используемые подсистемы оптимизации типа кэширования, прогнозирования следующих операций (prediction), а также оркестрирования сервисов и т.д.

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

    «Помогают» размазыванию ответственности и ORM фреймворки и библиотеки типа Hibernate. Используя их, многим разработчикам сервисов доступа к базам данных невольно хочется отдавать в качестве результата запроса по возможности полноценные объекты. Типичным примером является запрос пользовательских данных с целью показать их в приветствии или в поле с результатом аутентификации. Вместо возврата имени пользователя в виде трёх текстовых переменных (first_name, mid_name, last_name) по такому запросу нередко возвращается полноценный пользовательский объект с десятками атрибутов и связных объектов, типа списка ролей запрошенного пользователя. Это в свою очередь усложняет логику обработки результата запроса, порождает ненужные зависимости обработчика от типа возвращенного объекта и… — провоцирует размазывание ответственности за счет возможности реализации связанной с объектом логики извне ответственного за этот объект сервиса.

    А что делать? (Рекомендации)


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

    Тем не менее, если возможно, надо стараться соблюдать принцип распределения ответственности между компонентами. Одна компонента — одна ответственность.

    Ну а если уж невозможно сосредоточить операции над определёнными объектами строго в одной системе, такое размазывание необходимо очень тщательно регистрировать в общесистемной («надкомпонентной») документации, как специфическую зависимость компонент от элемента данных, от доменного объекта или друг от друга.

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

    Благодарю вас за то, что вы дочитали статью до конца.

    Иллюстрация «Мультимедиальный михер» автора статьи.

    Средняя зарплата в IT

    110 000 ₽/мес.
    Средняя зарплата по всем IT-специализациям на основании 8 580 анкет, за 2-ое пол. 2020 года Узнать свою зарплату
    Реклама
    AdBlock похитил этот баннер, но баннеры не зубы — отрастут

    Подробнее

    Комментарии 46

      +3

      В данном случае проблема не в разделении ответственности, а в database integration antipattern — доступ двух разных приложений к одной таблице в БД. Почему это проблема? Потому, что, в отличие от сетевого RPC, интерфейс взаимодействия между системами через таблицу в БД сложен и избыточен, поэтому его трудно специфицировать и отладить.


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

        –1
        Я употребил термин «размазывание» ответственности как альтернатива разделению ответственности. И мне кажется, это более общее и частое явление, чем database integration antipattern. Так что я с Вами и согласен и не согласен одновременно.
          0
          Жуть, а по сути это разумно, Hibernate для одной таблицы?
            –1
            Ну разумеется нет. Таблиц было много.
              +7

              Странно, что это за база и удивлен про hybernate. Во всех СУБД, с которыми работал есть sequences или какой-либо их аналог. И ORM, с которыми работал (не java/hybernate), не пытаются угадать, какой будет PRIMARY KEY, а явно или неявно используют sequences, при этом любая возможность переиспользования первичного ключа совершенно исключена и на производительность это не влияет (или в пределах погрешности измерения). Попытки угадывания первичного ключа — это ужас-ужас. Не могу поверить, что hybernate делает это по умолчанию.

                0
                Странно, что это за база и удивлен про hybernate.
                +1024
                Вдвойне странно, что столько людей не видят в этом большой проблемы. Не говоря уже о том, что правильное использование сиквенсов будет нагружать БД ничуть не больше, чем такой «оптимизированный» Hibernate.
                  +1

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


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

                    0
                    Базы бывают разные и с разным набором фич
                    Я сразу скажу: я в ОРМах не разбираюсь, никогда ими не пользовался, так что знаю только то, что Мойша напел. Типа как в этой статье. А поёт Мойша гнусаво, и в ноты не попадает. И знает только три блатных аккорда.
                    Вот тут главное, что мне не нравится в ОРМах. Есть некоторое количество разных СУБД. С разными фичами. И тут мы такие берем ОРМ, который работает со всеми универсально, и в итоге получается, что доступны только те фичи, которые есть у всех. То есть самая крутая и навороченная СУБД за сотни нефти низводится до самой дешманской, потому что «у нас же лапки ОРМ».
                    В том же оракле, например, есть кеширование сиквенсов. Я сам лично сталкивался с проблемой, когда (слава богу, никаких ОРМов не бло) поднятие кэша с дефолтных 20 до 1000 прекрасно убирало все проблемы с доступом к сиквенсу. А тут получается так: сначала платим 100500 денег за СУБД, потом берем ОРМ, а потом оказывается, что чукча обманул таксиста.

                    И да, хибернейт по дефолту не рассчитан на параллельную модификацию базы чужими приложениями. Это необходимое условие для работы его знаменитых кешей первого и второго уровня.
                    Ваши слова меня огорчают. 40 лет развития многопользовательского доступа в СУБД закончились тем, что «а давайте работать с БД в однопользовательском режиме».

                    Я всё еще надеюсь, что это просто я что-то не понимаю, и мне кто-нибудь объяснит.
                      0
                      Все не так плохо. Большинство проектов работают с Hibernate в многопользовательском режиме. Никто Вас назад в 20-й век не зовет.
                      А самая главное — статья не о этом. Она — о Феномене Размазывания Ответственности. Постарайтесь в ней увидеть именно этот посыл.
                        0
                        DEL
                        +1
                        ORM — дешевый способ накидать большую часть логики работы с базой, будто это какое-то хранилище привычных объектов. Зачастую всякой бд-специфичной крутизны в них нет, но вот тот же EF позволяет сделать raw sql запрос к бд и спроецировать его результаты куда следует. Запрос можно даже параметризовать! Это вполне себе способ воспользоваться какой-либо фичей СУБД, которая не реализована в ее драйвере для этой ORM.
                        Ну а дальше вопрос архитектуры — где держим эту логику использующую какие-то уникальные фичи БД? В слое бизнесовой логики? В data access layer? Запихиваем еще глубже в storage procedure в бд?
                        Не претендую на истину, все на своем опыте работы с постгресом, ef.core и неумением последнего в jsonb.
                        0
                        Базы бывают разные и с разным набором фич, при массовой вставке дополнительный roundtrip для инкремента сиквенса может значительно увеличить время вставки. Особенно если пинг до базы ненулевой.

                        На самых распространенных базах не нужен дополнительный roundtrip для инкремента последовательности. Последовательность обновляется при вставке, INSERT возвращает вставленный номер.

                    0
                    Поверить в это действительно трудно, пока не прочитаешь внимательно документацию (или указанную в ссылке в спойлере дискуссию на StackOverflow). Но у разработчиков Hibernate были на то свои разумные соображения.
                    Сами того не ведая, они поработали на Феномен Размывания Ответственности.
                      0
                      А где можно почитать эти разумные соображения? Очень интересно какие могут быть причины, ибо сам я не могу вообразить ни одного аргумента за, по-моему опыту разумностью в конкретном случае вообще не пахнет.

                      Стали копать глубже. Детальный анализ и сопоставление лог-файлов показали

                      ИМХО, при ошибках на проде анализ логов это первое, с чего стоит начинать.
                      • НЛО прилетело и опубликовало эту надпись здесь
                          0
                          Понятно, спасибо. Они хоть тестировали где-нибудь свою микрооптимизацию? Выигрыша никакого не будет, т.к. запросить последнее используемое значение счетчика в базе ничего не стоит и в некоторых субд (а может и во всех) его можно сразу получить тем-же запросом на вставку записи.
                          • НЛО прилетело и опубликовало эту надпись здесь
                        0

                        Вы почему-то вместо ответа на комментарий начинаете новую ветку. Просто это неудобно — уведомление об ответе естественно не приходит.


                        Почитал по диагонали ветку на SO, там нет ответа, почему Hybernate так делает. Исходя из моего опыта — это единственный ORM, который так делает, и по-моему это совершенно очевидная стрельба по ногам разработчиков. Совершенно не могу понять, зачем так делать...

                          0
                          С новой веткой Вы правы. Извините. А соображения разработчиков описаны в комментарии/ответе koluka вверху.
                            +1

                            В ответе выше нет ответа на вопрос. Никто никогда не пытался «угадывать» последовательности первичного ключа, кроме разработчиков Hybernate, потому что:


                            1. Это совершенно очевидная бомба замедленного действия, так как СУБД в принципе подразумевает параллельную работу с ней, и никогда нельзя полагаться на то, что никто не переиспользует id. Это может быть, например, администратор, который вручную запустил консоль СУБД и выполнил SELECT/UPDATE.
                            2. Это не нужно, так как во всех известных мне SQL базах данных есть последовательности или их аналоги.
                            3. Это не нужно, так как во многих (если не во всех) БД, INSERT умеет возвращать значение нового первичного ключа.

                            То есть из всех возможных решений, разработчики Hybernate выбрали наименее оптимальное и наиболее глючное.

                              0
                              Я не хочу выступать в роли адвоката Hibernate, да и не это фокус моей статьи. Но могу себе представить, что в нулевые годы, когда Hibernate создавался, были распространены десктоповые системы и распределенные системы с одним сервером и многими клиентами. Тогда было логично предположить, что класс Hibernate является единственным «властителем» таблицы в системе и может позволить себе такого рода микрооптимизацию.
                              Но это мои предположения.
                                +1
                                Угу, ну а счетчики в БД, если не ошибаюсь, на несколько десятков лет старше нулевых.
                                Как-раз в нулевых моим основным занятием были базы данных, всякие Ораклы и энтерпрайз юзающий их.
                                И тогда народ не отдавал БД на откуп фреймворкам, а отдельно ее проектировали всякие архитекторы и работали с базой напрямую, без каких-либо ORM, а за подоход этого хибернейта: угадывание праймари кея, могли забить канделябром по голове до смерти :)
                                Хороший тон было всегда рассчитывать на то, что параллельно с тобой с базой работает куча клиентов через что угодно и если две операции делают что-то, что должно быть атомарным, надо обеспечить атомарность средствами БД, рассчитывать на то, что между операциями пройдут микросекунды, никто другой влезть не успеет могли только новички.
                        +1

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


                        Прагматичное? Серьёзно?
                        Писать напрямую в БД, да ещё зная что есть другие части системы, которые пишут в эту же БД. Очевидно же, что проблема возникнет рано или поздно

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

                          Я не отстаиваю подход с прямой записью а банк данных из разных компонент, но это объективная реальность многих Enterprise систем. И если Вы углубитесь в документацию Oracle, то увидите, какие они предпринимают усилия, чтобы именно так их ДБ и использовали.
                            0

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


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

                            1. Если человек, который принял такое решение, ЗНАЯ, что есть другие пишущие в БД компоненты, не согласовал свое решение с ответственными за работу тех компонент — ответственнен он.
                            2. Если он не знал и не мог знать, что есть другие пишущие в БД компоненты — ответственнен тот, кто организует рабочий процесс и не дал ему нужную информацию.
                            3. Если он свое решение согласовал с нужными людьми, а те прощелкали возможные последствия — ответственны они.

                            А если два или все три пункта — это одни и те же люди, то вообще не о чем говорить. :)

                              0
                              В жизни всё сложнее.
                              Например — текучка кадров, ответственных в данный момент просто нет или новые ответственные еще не вработались.
                              Или: компонента не меняется, ответственные переводятся на другие фронты.
                              Или: таблиц так много, что ответственный проглядел коварно заданное значение.
                              Задним умом мы все сильны. (Шутка).
                                –1

                                Всё описанное вами называется словом "раздолбайство", а не "размазывание ответственности". И да, в жизни этого раздолбайства навалом. На мой взгляд, нет смысла вводить новые сущности для описания старых давным-давно известных проблем. :)

                                +1

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

                            +1
                            Собственно вопрос, почему в микросервисном подходе сама таблица не реализована как микросервис? Да и «угадайка» с primary-key в распределенной системе это конечно сильно.
                              0
                              Моим ответом на Ваш вопрос можно считать мой комментарий к комментарию maxzh83 внизу.
                              0
                              Дело не в размазывании ответственности, а в неправильном разбиении на микросервисы. Кроме этого, у каждого микросервиса должна быть своя БД и в БД других микросервисов он лезть не должен. Если у вас так не получается, то у вас что-то не так с проектированием/разбиением. На самом деле, правильно разбить монолит на сервисы очень сложно и влечет за собой избыточные взаимодействия и т.д., но, увы, такова плата за микросервисы.
                                0
                                Сейчас все меньше систем строится с нуля, на зелёной лужайке, когда всё можно заранее спокойно продумать. Чаще приходится в существующей системе что-то исправлять или что-то добавлять. Функциональность на микросервисы уже разбита. Этого не переделать. А вот при внесении дополнений можно наделать ошибок и «размазать» ответственность.
                                  0
                                  Сейчас все меньше систем строится с нуля, на зелёной лужайке, когда всё можно заранее спокойно продумать.

                                  Как раз монолит проще разбить, чем с нуля продумывать, т.к. видна вся картина целиком. Часто советуют пока не понятно как делить, писать монолит.
                                  А вот при внесении дополнений можно наделать ошибок и «размазать» ответственность.

                                  Эти ошибки это и есть ошибки проектирования, архитектуры и т.д. Если не можете развести микросервисы так чтобы они не лезли в чужие БД (а это иногда и правда непросто сделать), так может лучше оставаться на монолите и не играть в модные тренды?
                                    0
                                    Речь не идёт о способах разбиения монолита на части. Распределённые системы объективно давно существуют и по разным причинам деградируют.
                                    И хорошо бы попробовать в этом спокойно разобраться.
                                    Свести все к «раздолбайству» ничего не даст. Вряд ли все участники процитированной дискуссии на StackOverflow и других похожих дискуссий были «раздолбаями».
                                    Не все спроектированные самолеты взлетают, и многие из них долго доводят до кондиции уже после начала эксплуатации. У сложных технических систем свои законы. В том числе и у ИТ-систем.
                                      +1
                                      Речь не идёт о способах разбиения монолита на части

                                      Я лишь о том, что ваша проблема «размазывания ответственности» на самом деле проблема неправильного разбиения, которая не нова и давно известна.
                                      Свести все к «раздолбайству» ничего не даст.

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

                                      Доводят, но не приматыванием деталей скотчем. Ваш вариант с чтением двумя микросервисами из одной БД это именно костыль, примотанный скотчем. Можно было перепроектировать сервисы и, например, выделить еще один, который будет один работать с проблемной таблицей. И тогда даже странный кейс с предсказыванием id на hibernate будет работать.
                                        0
                                        Про «раздолбайство» написали действительно не Вы, прошу прощения.
                                        Но и про скотч Вы зря. Во-первых, как проблема была решена, я не писал вообще. И не об этом я хотел рассказать, а о феномене, который можно замечать, а можно не замечать и все сводить к конкретным проблемам и решениям.
                                        Я хотел, чтобы читатели рассмотрели описанную проблему и схожие проблемы на другом уровне абстракции.
                                          0

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

                                            0
                                            Разумеется, в проблеме виноват человек или орган, координирующий разработку. В ситуации с Enterprise системами, разработку которых могут вести разные фирмы на разных континентах, проверять мелкие детали очень сложно. Лучше пытаться следовать общим принципам и опасаться нарваться на анти-паттерн. Я попытался в статье показать пример опасного феномена. Он не сводится к параллельной записи в ДБ и может быть вызван совсем другими причинами.
                                            К сожалению, по некоторым комментариям можно предположить, что этот посыл либо не всеми понят, либо с ним не все согласны.
                                            0
                                            как проблема была решена, я не писал вообще.

                                            Так я, собственно, не о решении, а о самой проблеме, о том почему она появилась.
                                            а о феномене, который можно замечать, а можно не замечать

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

                                    Сурово, это как на полном ходу поезда дёрнуть стоп-кран, если мне резко понадобилось выйти именно тут.
                                      –1
                                      Уж если делать аналогии с физическим миром, то больше подходит такая: велосипедист обгонял стоящую на обочине машину, а водитель в это время открыл дверь…
                                      Впрочем, любые аналогии имеют свои границы.
                                        0
                                        Не согласен. Есть компонент, который корректно работает с БД, вдруг появляется другой компонент, которому позарез нужно что-то впихнуть в эту базу. И вот вместо того, чтобы делегировать эту задачу первому компоненту, пусть и в ущерб скорости (в моём варианте аналогии — дождаться станции и сойти) этот самостоятельный кусок кода совершает несогласованный и незащищённый акт насилия над БД, игнорируя установленные правила (дёргает стоп-кран).
                                        Можно назвать это как угодно, но на мой взгляд, самое культурное определение этого решения — костыль, но никак не «прагматическое решение».
                                      +1
                                      Человек честно рассказал о том, с чем столкнулся, его надо поощрить, а не взыскание накладывать. Мне кажется, что с похожими до изоморфизма ситуациями не сталкивался только тот, кто ничего не делал.
                                        0
                                        Про изоморфизм вы хорошо заметили. И схожесть ситуаций заключается в том, что речь идёт о «размывании ответственности» — процессе, когда компоненты обрастают несвойственными им функциями и данными, которые становится всё труднее контролировать и которые приводят к ошибкам в совместной работе.
                                        0
                                        A Hibernate работает по умолчанию при записи следующим образом. Чтобы оптимизировать процесс записи, она запрашивает в индексе значение следующего primary key один раз, и после этого записывает несколько раз просто увеличивая значение key (по умолчанию — 10 раз).


                                        О — оптимизация. :)

                                        Поразительная корреляция с вчерашней статьёй про ORM.

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

                                        Самое читаемое