すべての SaaS 顧客に独自のコンテンツスペースを与える
すべての顧客に、アプリケーション層の if 文ではなく PostgreSQL の行レベルセキュリティによって強制される分離を備えた、独立したコンテンツスペースを与えましょう。
顧客が 20 社から 50 社のどこかで、「tenant_id カラムを追加すればいい」は計画であることをやめ、負債になります。
この記事は、マルチテナントなコンテンツを実現する 3 つの方法、そのうちの 1 つがなぜ意味のある形でより安全なのか、そして一般的な CMS の料金モデルが顧客数の増加にともなって SaaS の利益率に何をもたらすかについてのものです。
3 つのパターン、1 つの本当の決断
| パターン | 分離 | 運用コスト | 破綻する場所 |
|---|---|---|---|
| テナントごとのデータベース | 最も強固 | 最高 — マイグレーション、バックアップ、監視すべき N 個のデータベース | テナント 200 でのマイグレーション、コネクションプールの枯渇 |
| テナントごとのスキーマ | 強固 | 高 — N 個のスキーマ、同じマイグレーション問題 | Postgres は数千のスキーマで遅くなる |
| 行レベル、共有テーブル | 強制の仕方に完全に依存する | 最低 — 1 回のマイグレーション、1 回のバックアップ | フィルタを忘れたクエリ |
ほとんどの人は行レベルに落ち着きます。他の 2 つの運用コストは現実のものであり、累積していくからです。そのため、最後のセルがすべての問いとなります。フィルタを忘れたクエリを何が止めるのか?
フィルタは必ずいつか忘れられる
チームが不注意だからではありません。算数の問題です。
成熟した製品には何百ものクエリがあります。あるものは、誰も 1 年開いていないレポートジョブの中にあります。あるものは、インシデント対応中に書かれた管理ツールの中にあります。あるものは、外部の請負業者が追加しレビュアーがざっと目を通したモジュールの中にあります。そのすべてが WHERE tenant_id = $1 を覚えていなければなりません。
この障害はサイレントかつ全面的です。フィルタの欠落は例外を投げません。それはある顧客のコンテンツを別の顧客に返し、そのレスポンスは、誰かがドロップダウンの中に競合他社の未公開の料金ページを見つけるまで、完全に正常に見えます。
PostgreSQL の行レベルセキュリティ