каждый клиент работает с 50 таблицами, то есть запрос одновременно обращается максимум к 50 таблицам. Если используется пулер, то для запроса другого прользователя серверный процесс часть файлов закроет, часть откроет. Проблема была бы, если одному запросу понадобилось бы больше 1000 файлов. Но понятно, что огромное число файлов в кластере баз данных хуже и решение с RLS в этом имеет плюсы, если размер таблиц получится не слишком большим.
Даже если допустить такой исход, это все равно путь к деградации. Задержки неизбежны. В каких-то случаях это допустимо, в каких-то - нет
То есть, в целом, можно рассматривать использование и RLS и view под конкретную задачу и выбрать то, что больше подходит
Согласен, нет идеального решения для всех. Все зависит от задачи
Если забыть создать view - доступа не будет, так как на схему хранения таблиц и на сами таблицы доступ не даётся. Если забыть удалить view - тоже не будет, так как если в таблице нет столбца tenant_id, то доступа и не будет. А если столбец в новой таблице есть, то значит строки предназначены для просмотра и чужих строк view не выдаст.
С RLS наоборот - создал таблицу, забыл добавить политику и всё - доступ есть ко всем строкам.
Соглашусь с вашим тезисом, схема "нет View - нет доступа" действительно надежна. Тут я, пожалуй, погорячился, когда назвал это прямой утечкой. Основной риск здесь скорее в человеческом факторе при масштабировании. Если таблиц станет 100+, кто-то из разработчиков может допустить ошибку. RLS в этом плане кажется более монолитным решением. Но вы правы, убрать полностью человеческий фактор невозможно. Все же, проблема человеческого фактора с RLS легко решается одной проверкой в CI. А вот проверить View на корректность будет сложнее
триггеры создавать не нужно - view простое.
По поводу записи через триггеры, то да, для простых View они не нужны, но если логика усложнится, поддержка триггеров может добавить головной боли.
при изменении структуры таблицы менять view не надо, в нём “select *”.
По поводу SELECT *. Если правильно помню, звездочка раскрывается в список колонок именно в момент создания представления. То есть, если мы добавим колонку в таблицу позже, во View она автоматически не появится, и его придется пересоздавать. Но тут лучше сверится с докой Postgres
для точности: по умолчанию в Postgres max_files_per_processes=1000, в linux max_open_files=1024. Если файлов окрыто 1000, а процессу нужен доступ еще к одному файлу, процесс закрывает открытый файл. Это создает задержку и даже увеличивают лимиты, но при 50 таблицах не слишком критичную, так как таблицы небольшие, то fsync по файлам выполняется быстро
Кажется, вы упустили множитель в моем примере. Речь не о 50 таблицах, а о 500 000 (10к клиентов × 50 таблиц). Если всего 50 таблиц, то проблем не будет
Представления работают, но требуют ручного обновления при изменении схемы таблиц, усложняют запись (триггеры) и оставляют риск утечки через забытую View. С RLS таких проблем не будет
Согласен, этот риск нужно держать в голове. RLS - это не "серебряная пуля". Но архитектура не статична. RLS позволяет нам эффективно жить на одной машине на старте и в фазе активного роста. А когда упрешься в этот потолок (или появится тот самый "жирный" клиент), то ваше решение подойдет идеально. У любого подхода есть свои ограничения
Ваш подход идеально вписывается, когда есть клиенты с хорошими чеками, где затраты на клиента не превышают доходы от него. Это золотой стандарт индустрии, тут спорить никто не станет. А если у вас массовый SaaS с тысячами мелких клиентов, то проблемы изолированного подхода накатываются, как снежный ком. Раскатка миграций на 10 000 баз, управление пулами соединений и сбор сквозной аналитики превращаются в DevOps-ад, который стоит дороже, чем сэкономленные ресурсы. Считаю, что все зависит от бизнес-требований
Для админки есть самый простой вариант. Используйте роль с BYPASSRLS, про которую упоминал в разделе "Грабли №4". Так как роль игнорирует политики, вы получаете доступ ко всем данным и фильтруете их явно
Вы имеете в виду обязательные составные индексы (tenant_id, column) для стабильного Index Scan? Или какой-то более хитрый кейс? На самом деле, тема индексов - это только вершина айсберга. Когда данных становится слишком много, приходится смотреть в сторону партиционирования по tenant_id, но это тянет на отдельный большой разбор (который, кстати, у меня почти уже готов)
Citus - это отличное решение для горизонтального масштабирования, когда данные измеряются терабайтами и шардинг становится неизбежен. Но это инфраструктурное усложнение, которое оправдано только при высоких нагрузках. В своей статье делал упор на логическую изоляцию в рамках стандартного Postgres. RLS позволяет гибко настраивать права доступа (например, разные роли внутри одного тенанта) без раздувания инфраструктуры и лишних затрат на поддержку кластера. Для большинства SaaS-задач на старте и этапе роста RLS выглядит, как более прагматичный выбор
Как упоминал в разделе "Грабли №2", планировщик Postgres - параноик. Даже если есть индекс, без метки LEAKPROOF на операторе поиска он может выбрать Seq Scan, чтобы не "протечь" данными через индекс до проверки прав. Поэтому индекс на tenant_id - это база, но для LIKE/ILIKE часто нужен составной индекс или обертка в LEAKPROOF-функцию, чтобы заставить Postgres поверить, что поиск безопасен
Postgres RLS, про который написл, - это решение скорее для динамичных SaaS-систем, где нужно масштабироваться на тысячи мелких клиентов без раздувания количества объектов в БД. В вашем случае на Postgres основная сложность будет даже не в безопасности, а в том, как оптимизатор справится со сложными планами запросов после Oracle. Просадка в 30% - это еще оптимистичный прогноз от разработчиков. Часто в таких кейсах приходится пересматривать индексы и структуру запросов с нуля. Будет интересно узнать, на какой архитектуре вы в итоге остановитесь. Удачи с миграцией!
Рад, что материал оказался полезен. Если будете внедрять RLS у себя, делитесь фидбеком. Будет интересно обсудить, как решение покажет себя в ваших условиях
Даже если допустить такой исход, это все равно путь к деградации. Задержки неизбежны. В каких-то случаях это допустимо, в каких-то - нет
Согласен, нет идеального решения для всех. Все зависит от задачи
Соглашусь с вашим тезисом, схема "нет View - нет доступа" действительно надежна. Тут я, пожалуй, погорячился, когда назвал это прямой утечкой. Основной риск здесь скорее в человеческом факторе при масштабировании. Если таблиц станет 100+, кто-то из разработчиков может допустить ошибку. RLS в этом плане кажется более монолитным решением. Но вы правы, убрать полностью человеческий фактор невозможно. Все же, проблема человеческого фактора с RLS легко решается одной проверкой в CI. А вот проверить View на корректность будет сложнее
По поводу записи через триггеры, то да, для простых View они не нужны, но если логика усложнится, поддержка триггеров может добавить головной боли.
По поводу SELECT *. Если правильно помню, звездочка раскрывается в список колонок именно в момент создания представления. То есть, если мы добавим колонку в таблицу позже, во View она автоматически не появится, и его придется пересоздавать. Но тут лучше сверится с докой Postgres
Кажется, вы упустили множитель в моем примере. Речь не о 50 таблицах, а о 500 000 (10к клиентов × 50 таблиц). Если всего 50 таблиц, то проблем не будет
Представления работают, но требуют ручного обновления при изменении схемы таблиц, усложняют запись (триггеры) и оставляют риск утечки через забытую View. С RLS таких проблем не будет
Согласен, этот риск нужно держать в голове. RLS - это не "серебряная пуля". Но архитектура не статична. RLS позволяет нам эффективно жить на одной машине на старте и в фазе активного роста. А когда упрешься в этот потолок (или появится тот самый "жирный" клиент), то ваше решение подойдет идеально. У любого подхода есть свои ограничения
Ваш подход идеально вписывается, когда есть клиенты с хорошими чеками, где затраты на клиента не превышают доходы от него. Это золотой стандарт индустрии, тут спорить никто не станет. А если у вас массовый SaaS с тысячами мелких клиентов, то проблемы изолированного подхода накатываются, как снежный ком. Раскатка миграций на 10 000 баз, управление пулами соединений и сбор сквозной аналитики превращаются в DevOps-ад, который стоит дороже, чем сэкономленные ресурсы. Считаю, что все зависит от бизнес-требований
Для админки есть самый простой вариант. Используйте роль с BYPASSRLS, про которую упоминал в разделе "Грабли №4". Так как роль игнорирует политики, вы получаете доступ ко всем данным и фильтруете их явно
Сырые SQL-запросы будут неизбежно появляться по мере роста проекта, а это, опять же, человеческий фактор, от которого пытаемся уйти
Вы имеете в виду обязательные составные индексы (tenant_id, column) для стабильного Index Scan? Или какой-то более хитрый кейс? На самом деле, тема индексов - это только вершина айсберга. Когда данных становится слишком много, приходится смотреть в сторону партиционирования по tenant_id, но это тянет на отдельный большой разбор (который, кстати, у меня почти уже готов)
Citus - это отличное решение для горизонтального масштабирования, когда данные измеряются терабайтами и шардинг становится неизбежен. Но это инфраструктурное усложнение, которое оправдано только при высоких нагрузках. В своей статье делал упор на логическую изоляцию в рамках стандартного Postgres. RLS позволяет гибко настраивать права доступа (например, разные роли внутри одного тенанта) без раздувания инфраструктуры и лишних затрат на поддержку кластера. Для большинства SaaS-задач на старте и этапе роста RLS выглядит, как более прагматичный выбор
Как упоминал в разделе "Грабли №2", планировщик Postgres - параноик. Даже если есть индекс, без метки LEAKPROOF на операторе поиска он может выбрать Seq Scan, чтобы не "протечь" данными через индекс до проверки прав. Поэтому индекс на tenant_id - это база, но для LIKE/ILIKE часто нужен составной индекс или обертка в LEAKPROOF-функцию, чтобы заставить Postgres поверить, что поиск безопасен
Postgres RLS, про который написл, - это решение скорее для динамичных SaaS-систем, где нужно масштабироваться на тысячи мелких клиентов без раздувания количества объектов в БД. В вашем случае на Postgres основная сложность будет даже не в безопасности, а в том, как оптимизатор справится со сложными планами запросов после Oracle. Просадка в 30% - это еще оптимистичный прогноз от разработчиков. Часто в таких кейсах приходится пересматривать индексы и структуру запросов с нуля. Будет интересно узнать, на какой архитектуре вы в итоге остановитесь. Удачи с миграцией!
Рад, что материал оказался полезен. Если будете внедрять RLS у себя, делитесь фидбеком. Будет интересно обсудить, как решение покажет себя в ваших условиях