Go×RLSで複数テナントのデータを安全に扱う2023/09/28 golang.Tokyo#33スプリームシステム株式会社プロダクトディベロップメント部徳丸 翔平
View Slide
自己紹介• 徳丸 翔平(@shohei36)• 普段の業務では主にJavaやGoを使ってバックエンド開発をやってます• Go歴はもう少しで1年といったところ• 低レイヤーに興味あり
モノリスからマイクロサービスへhttps://acropolium.com/blog/migrating-monolith-to-microservices/
マルチテナントのデータベース戦略https://medium.com/one9-tech/which-database-structure-to-use-in-multi-tenant-application-6f1b9af09634
データベース の Row-Level Security(RLS)機能データの読み込み時にテーブルの行レベルでアクセスを制御するデータベースの機能テナントID ID NAMEtenant01 1000 Bobtenant02 1000 Alicetenant03 1000 Tarotenant03 1000 HanakoSELECTID, NAMEFROM SOME_TABLEWHERE ID = ‘1000’ 「tenant02」が接続
プログラム側も工夫が必要…🤔 RLSが効いたコネクションを正しく制御するには?🤔 DBに依存せずにトランザクションをどのように表現するか?🤔 テナント追加時の作業をゼロにしたい、、
プログラム設計方針以下のアプローチによって実現• データベースセッションの環境変数にセットしたテナントIDでアクセス権を制御• Context 経由で sql.Tx を リポジトリ にパスする
プログラム設計方針以下のアプローチによって実現• データベースセッションの環境変数にセットしたテナントIDでアクセス権を制御• Context 経由で sql.Tx を リポジトリ にパスするこちらのサンプルコードを使用して説明しますhttps://github.com/shohei36/go-rls
環境• WSL2 Ubuntu 20.04 on Windows• PostgreSQL 13.2• Go 1.20
サンプルコードの構成// アプリケーション固有のビジネスルール// トランザクション// DBなど外部システムとのアダプター// ドメインモデル// Main関数(controllerも兼ねる)※サンプルコードの構成Clean Architecture
例)会員情報を更新するユースケース
登場人物
DoInTxを実行usecase.go
sql.DB からコネクション(sql.Conn)を取得transaction.godb.go
DBセッションの環境変数にテナントIDをセットdb.go
補足:テーブルにRLSを適用するDDLpgsql/init/001_ddl.sql
sql.DBのコネクションはスレッドセーフsql.DB の仕組みhttps://please-sleep.cou929.nu/go-sql-db-connection-pool.html公式ドキュメントhttps://pkg.go.dev/database/sql#DB
トランザクション(sql.Tx)開始transaction.go DoInTx
Context に sql.Tx をセットtransaction.go DoInTxSql.Tx を取り出すときに使うKey
トランザクション内の処理を実行transaction.go DoInTxusecase.go
リポジトリのUpdateメソッドをCallusecase.go
DoInTx内でContextにセットしたsql.Txを取得transaction.gorepository.go
Update文を発行repository.go※RLSが効いているので、SQLのWhereの条件にtenant_idは不要
トランザクションをコミットtransaction.go DoInTx
コネクションをsql.DBに返却transaction.go DoInTxhttps://pkg.go.dev/database/sql#Conn.Close
まとめ• Context経由でsql.Txを渡すことで、DBに依存しない形でユースケースでトランザクションの定義が可能に• 参考:https://qiita.com/arkuchy/items/659a11767912c2ec266d• データベースセッションの環境変数にセットしたテナントIDでアクセス権を制御• テナントのロールを追加する必要がなく、基本的にはテナント追加時の作業が不要• sql.DBで管理するコネクションがスレッドセーフ• (感想)Goはマイクロサービス向きの言語であることを実感
Thank you for listening!