Обновить
20

Пользователь

13
Подписчики
Отправить сообщение
Ещё раз повторю, БД — единственный достоверный источник данных. Поэтому отмасштабированное горизонтально приложение всё равно будет ходить в БД для проверок чуть сложнее чем NOT NULL, которые требуют согласованных по чтению и консистентных данных и ещё сильнее нагружать базу, т.к. в приложении валидацию писать это сильно не оптимально.
Мы с Михаилом, который написал комментарий ниже, проводили исследования на простых проверках, ну и БД была сильно впереди
habr.com/ru/post/521292/#comment_22144890

почитайте, если интересно, там в гите все исходники есть, попробуйте сами
а) приведите, пожалуйста, пример проверки на уникальность

б) как они могут не использовать БД? БД — единственная достоверная точка данных

в) да, а потом вы это поле пишите в БД и там тоже идёт проверка NOT NULL, 2 логические операции вместо одной
проблема апп в том что
а) валидациии в нём могут работают не корректно, например, проверка на уникальность
б) в несколько раз больше нагружают базу
в) работают существенно медленнее
не сложнее чем в апп
Есть классическая задача-решение от Тома Кайта, как реализовать бизнес-правило в БД, вроде, чтобы в отделе работало от 1 одного до 8 сотрудников, т.е. не менее одного и не более 8
Прочитайте, тогда Вы сами легко сможете решить свою проблему.

Ещё меня, в своё время, поразила, по уровню мышления, статья, как можно материализованными представлениями в Oracle поддерживать консистентность данных.
habr.com/ru/post/132727

После прочтения, должен быть эффект ещё лучше чем от предыдущей, но уровень вхождения выше. Ну и конечно, шелуха проверок уровня апп пропадёт
у Вас простой кейс. Уверен, проверку в БД реализовать просто, пришлите сокращённую схему, я смогу Вам помочь написать констрейнт
Мне гораздо интересен другая часть мессаджа, а именно зачем нам вообще нужен такой констрейнт. И я как раз доформулировал кейс.

Не про заказы, но у меня есть на практике отличный пример, когда мы описываем товары для каталога. От бизнеса может придти требования, что товары теперь должны удовлетворять новому правилу и из внешней системы товары не проходящие такие правила не будут приняты валидацией. Но никто не пойдет и не поправит сотни тысяч уже заведенных товаров, как миниму потому что не всегда по ним есть возможность эти данные синтезировать. если говорить о реальных примерах — это наш любимый датаматрикс. по старым товарам никто не будет получать коды маркировки, они могут уже не продаваться. Но они обновляются по другим причинам из других источников и должны проходить валидацию.
проблемы нет, со статикой есть проблема:
1. если поставить завтрашнюю дату, а завтра не релизнуть, то послезавтра, миграция, с некоторой вероятностью, не накатиться в момент релиза и будет пожар
2. если поставить +неделю, то завтра у нас может заболеть девопс и понимая сложность текущего релиза мы можем совместно с клиентом принять решение перенести релиз на +2 недели и столкнуться с проблемой №1
3. мы можем поставить +месяц, но тогда послезавтра после завтрашнего обновления к нам прибежит клиент и спросит, а Вы же говорили что это реализовали, а оно не работает

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

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

А мой код и динамическая-то дата чем Вас не устраивает? Она как раз лишена всех этих недостатков которые Вы описываете. В любом случае эти недостатки гораздо меньшее зло чем БД-данные в состоянии Шрёдингера.

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

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

Но вариантов ещё много, например, коммитим данные со статусом сущностей NOT_COMPLETED, делаем долгие внешние пищущие запросы, завершаем бизнес транзакцию, переводя сущности в статус COMPLETED, конечно тут жертвуем консистентностью + нужны компенсирующие транзакции… та же Сага и получается

Я лично за то, чтобы в БД были только те констрейнты, которые бизнес готов высечь на бумаге (если мы говорим в терминах DDD). Т.е. если бизнес готов сказать, что с полуночи 07.07.2021 мы заказы без емейлов дропаем, даже те, которые в 23:59 еще оформлялись в старом интерфейсе где нет емейла — то и ладно.
Все эти проблемы лечатся динамической датой в констрейтах (см. выше)
соглашусь что это дело привычки, но вижу явные минусы:
— сложнее понимать, т.к. мало где используется
— находя новое выражение, надо вспомнить оператор или глазами вернуться в место объявления оператора, чтобы понять как будем сравнивать
[Op.or]: [<многострочное_выражение>], [<многострочное_выражение>], [<многострочное_выражение>], [<многострочное_выражение>]…
— появляется много излишних скобок, которые тоже не добавляют читаемости коду
Вот за такое делаем ментальное внушение тем, кто такое придумывает. Все изменения в DDL должны фиксироваться миграциями (если у вас конечно есть ревью изменений, тестовые контура, автотестирование и прочее). Если фиксировать дату в миграции… Задержали релиз на день — сидим хотфиксим. Релизим не ровно в полночь — сидим хотфиксим. Решили потестировать на куа базе — сидим хотфиксим.

Вам разве религия не позволяет подставлять дату динамически?
SELECT CONCAT("ALTER TABLE products
ADD CONSTRAINT marking_code_is_not_null CHECK (created_at < '", NOW()
, "' OR created_at >= '", NOW()
, "' AND marking_code IS NOT NULL)"
)
INTO @sql
;
PREPARE stmt1 FROM @sql
;
EXECUTE stmt1
;


А если у вас несколько систем?

Если мы в обе наши системы пишем транзакционно, то надо задуматься о корректности архитектуры. Транзакция на 2 системы с обеспечением ACID — практически не реализуемая инженерная задача.

А если у вас несколько внешний интеграций? Какие констрейнты вы будете отдавать им на работу с этими сущностями?

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

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

Такие кейсы очень хорошо описывать CHECK CONSTRAINT'ами в БД
ALTER TABLE products
ADD CONSTRAINT marking_code_is_not_null CHECK (created_at < '2021-07-07' OR created_at >= '2021-07-07' AND marking_code IS NOT NULL)

— БД гарантирует что данные консистентны и удовлетворяют CHECK'у если миграция прошла
— прямо в коде у нас зафиксирована дата изменения схемы данных
— программист который будет, например, писать отчёт, чётко видит из структуры данных что поле marking_code когда-то было пустым и не совершит ошибку. Например, не напишет INNER JOIN вместо LEFT JOIN там где это не надо

Вот тут дискутировал на эту тему:
habr.com/ru/post/521292/#comment_22142326
Прочитал всю документацию, изучил исходники, мда… такая себе поделка… К автору статьи и к самой статье претензий нет, хороший перевод трети документации, но в 2021 году вообще странно использовать ORM, на дворе ведь не 2001 и не MySQL3. Современные бесплатные СУБД дают широчайший функционал и глупо от него отгораживаться вот такими велосипедами. В СУБД конечно есть небольшие проблемы, которые может поправить ОРМ, но они в современных СУБД настолько ничтожны по сравнению с использованием ОРМ.

Сейчас по делу:
— посчитайте и сравните количество кода, количество строк кода, количество операторов и функций и поглядите какой монстр получается по сравнению с лаконичным SQL
Post.findAll({
  where: {
    [Op.or]: [
      sequelize.where(sequelize.fn('char_length', sequelize.col('content')), 7),
      {
        content: {
          [Op.like]: 'Hello%'
        }
      },
      {
        [Op.and]: [
          { status: 'draft' },
          sequelize.where(sequelize.fn('char_length', sequelize.col('content')), {
            [Op.gt]: 10
          })
        ]
      }
    ]
  }
});


SELECT *
FROM "posts" AS "post"
WHERE (
  char_length("content") = 7
  OR
  "post"."content" LIKE 'Hello%'
  OR (
    "post"."status" = 'draft'
    AND
    char_length("content") > 10
  )
)

ещё внимания заслуживает количество скобочек в js-версии

— особо хочу отметить кривой интерфейс операторов, когда сначала идёт оператор, а потом значения, мозг сломать можно при чтении
    {
      [Op.or]: [
        {
          title: {
            [Op.like]: 'Boat%'
          }
        },
        {
          description: {
            [Op.like]: '%boat%'
          }
        }
      ]
    }
    // title LIKE 'Boat%' OR description LIKE '%boat%'


— MysqlDialect.prototype.defaultVersion = '5.7.0';
ну на дворе ведь реально 2021г и в 8ке огромное количество фич из-за которых база реально стала взрослой

— много оверхэда с релейшенами, зачем руками городить весь этот огород и поддерживать кучу кода, ведь в базе уже есть FOREIGN KEY, пусть ORM подтянет их и создаст связи. По моему так делает SQLAlchemy, за что ему плюсик, ведь это реальное улучшение по сравнению с SQL

— не понятно зачем миграции написаны на новом псевдо языке, не вижу ни одного преимущества по сравнению с SQL. Недостатки:
а) семантически описывает то же, просто другими своими словами, а потом конвертируется всё равно в SQL. Т.е. необходимо изучить какой-то дополнительный синтаксис который не даёт никаких улучшений по сравнению с SQL
б) код Sequelize может содержать ошибки. Я сам натыкался в Laravel на подобное в какой-то версии, вроде добавлял CHARACTER SET к объявлениям числовых типов данных
в) не поддерживает всех DDL возможностей SQL
Итого в целом дополнительная излишняя абстракция и оверинжиниринг в самом явном проявлении.
Единственная эфимерная причина, якобы можно сменить движок БД. На практике это не реально, т.к. в миграциях всё-равно придётся писать SQL, и тот который пока не реализован в Sequelize, и тот который является фичей конкретной БД. Т.е. при использовании миграций Sequelize у вас будут и Sequelize и SQL. Конечно лучше всё писать на SQL, код будет лаконичнее проще, единообразнее.

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

А не подскажите где про данное поведение можно почитать? С PostgreSQL не работал, но это ж бомбовая фича, позволяет применять динамические условия и динамические сортировки, без использования querybuilder'ов. В Oracle это боль, в MySQL реализовано частично, но детерминированные функции и выражения не предвычисляет, увы.
А какой-то другой UI-фремворк используйте вместо Vuetify? Или чистый html+css?
Было бы идеально чтобы разрешения в Андроиде можно было отклонять, предоставляя приложению Sendbox подмену. В том числе на список установленных приложений и наличие рута.
Всё что можно проверять на уровне БД надо проверять на уровне БД:
— это быстрее и производительнее
— на уровне БД гарантирует консистентность данных

Например, решили сделать поле position обязательным, на основе того, что оно обязательное написали какие-то сервисы.
Пишем миграцицию:
ALTER mytable MODIFY position VARCHAR(191) NOT NULL
Миграция может отвалиться, тогда мы поймём что у нас есть данные, которые не соответствуют нашим представлениям.
Ну и тут 2 варианта:
— написать UPDATE и по определённым правилам обновить данные
— написать
ALTER mytable
ADD CONSTRAINT position_not_null_check
CHECK (position NOT NULL AND created_at > '2020-02-19')

В любом случае выполнив SHOW CREATE TABLE, я увижу каким критериям соответствуют данные и смогу правильно написать код их обработки.

Дописав же код проверки в приложении, на данные нет абсолютно никаких гарантий.
это был толстый сарказм
Лучше структурированных БД, пока ничего не придумали, поэтому лучше выжимать их них максимум.
Типы данных надо объявлять такими, какими они будут, это позволит:
— отправить мета инфу на фронт и обработать максимально эффективно 95% ошибок
— на уровне БД перехватить ошибки и проксировать их пользователю. Тут опять же максимальная производительность и минимальный код
1.
… все колонки nullable()

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

2. не надо БДшный код пихать в миграции

Информация

В рейтинге
Не участвует
Зарегистрирован
Активность