Row Level Security
#PostgreSQL
公式ドキュメント
https://www.postgresql.jp/document/14/html/sql-createpolicy.html
マルチテナントトの兼ね合い
PostgreSQL の行レベルのセキュリティを備えたマルチテナントデータの分離 | Amazon Web Services ブログ
テナントデータどうパーティションするか
用語
サイロ silo
テナントごとに個別のデータベースインスタンスを使用
インフラストラクチャコストが高くなる
テナントのセットアップがより複雑になる
各テナントに、新しいデータベースインスタンスを作成および管理する必要がある
ブリッジ bridge
同じデータベースインスタンスを共有しながら、テナントごとに異なるスキーマを使用する
コストを削減できる
メンテナンスとテナントのセットアップはかなり複雑になる可能性がある
スキーマを分離
マイグレーションがスケールしない
プール pool
各テーブルまたはビューには、データのフィルターに使用するパーティション分割キー (通常はテナント ID) が含める
-> Row Level Securityで安全にフィルタリングする
マルチテナントSaaSのテナント分離をRow-Level Securityに移行した - Sansan Tech Blog
code:SQL
CREATE TABLE invoice (
tenant_id varchar(20) NOT NULL,
invoice_uuid uuid NOT NULL PRIMARY KEY,
publisher varchar(50) NOT NULL
);
ALTER TABLE invoice ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_policy ON invoice USING (tenant_id = current_setting('app.tenant_id'));
tenant_idの指定の仕方
SELECT set_config('app.tenant_id', :tenantId, false);
SETではなくset_configなのはプレースホルダーが使えるため
userをわける必要があるか
ない気がするな...
リクエスト -> コンテキスト作成 -> tenantの特定 -> current_setting で指定
userの場合 リクエスト -> コンテキスト作成 -> tenantの特定 -> 該当ユーザーでログイン
コネクション作り直さないといけない(あるいはユーザー分事前に用意してお必要がある)
NestJS + Prisma2 で作る RLS の世界 - Beatrust techBlog
DB connection = request scopeでいいか
すべてのtenantにまたがる処理をどうするか
リクエストと非同期に処理したい場合どうするか
コネクションプール
理想
グローバルなコネクションプール
コネクションを取得後テナントに対応したユーザーに切り替え(あるいはset_config)
次善策
テナントごとにコネクションプールを用意
テナント増えてきたとき現実的ではなさそう
都度接続
オーバーヘッドが大きい
小規模ならばまぁええやろ
Prisma2との思想にあわない
https://www.prisma.io/docs/concepts/components/prisma-engines/query-engine
Row Level Security で事故らないアプリケーションを構築する - FLINTERS Engineer's Blog
マルチテナントデータを安全に扱うための Row Level Security、そして1ユーザーが複数のテナントに所属するときの対応について - 理系学生日記
Row Level Security | Supabase