database/sqlを使ってマルチテナントDBへアクセスする
Goの[database/sql, 当然といえば当然な気がするけどapartment的世界観をそのまま持ち込むのけっこうむずぴっぴ
database/sql.DBはコネクションプーリングをするやつで、 database/sql.DB.ExecContextとかは内部で透過的に接続をプールから取り出してそれに対してクエリを実行するという世界観なので、
1リクエストあたり (≒テナントごとの処理あたり) にuse文は高々2回 (テナントへの切り替え + デフォルトテナントへ戻す) にしたい、っていう要件を満たそうとすると
database/sql.DBではなくて、1つのコネクションそのものを表すdatabase/sql.Connをリクエスト開始時に確保・テナントを切り替えて、1リクエスト中ではそれを使うという風にしないといけない。
なぜかというと1リクエスト中でDBへクエリを投げる機会は任意の回数だけある = database/sql.DB.ExecContextなどのメソッドは複数回呼び出されるので、
DB.ExecContextなどのメソッドは呼び出しごとにプールからコネクションを取り出そうとするので、1リクエスト中で同じコネクションが使われる保証はない
(トランザクションを使う場合は同一コネクションになるけどこれは別の仕組みで担保されているので別の話)
なのでdatabase/sql.DBを直接使う限りにおいて同一コネクションを使う保証はできないのは明らか
あとはdatabase/sql.Connを使う vs テナントごとにdatabase/sql.DBを持つか、になるけど後者は同時接続数の管理がとても難しくなるのでだめな気がしている
アプリケーションのライフサイクル中で処理すべきテナントの数は基本的に決められないため。
map[string]*sql.DB のキーの数 = 知っているテナントの数が増減するたびに SetMaxConn() を呼び出して調整する手がないこともなさそうだけど
現在保持している接続の数を下回る値を SetMaxConn() に渡しておかしなことにならないのかとか考えはじめると深淵な気がする
あとしばらくリクエストがきていないテナントのsql.DBは解放していいし、するべきな気がするけど、いよいよハイテクすぎる気がする
のでdatabase/sql.DBのかわりにdatabase/sql.Connを使ってDBへクエリを投げる、というのが良い気がしている