はじめに
ログに実行したSQLを出力するように修正した際に、ExecuteReaderの使用箇所を1メソッドに纏めるように修正をしたのですが、アプリケーションを使用しているうちに何も動作しなくなり、MaxPoolSizeが超過した例外メッセージが出力されました。
変更内容
変更前
省略して書いてます。
社内のDB操作ライブラリーを使用しているので、記述が一般と若干違います。
using DbProduct manager = new DbAdapter(DBInfo).Create()
{
// DB接続
manager.ConnectDB(false);
string message = "メソッド名";
Logger.DebugSQL(message, sql, sqlParameter);
DbDataReader reader = manager.ExecuteReader(sql, sqlParameter);
}
変更後
Readerメソッド側に接続情報を移動させました。
string message = "メソッド名";
using (DbDataReader reader = Reader(messaage, sql, sqlParameter))
DbProductにusingで囲んでしまうと、呼び出し先のreaderでcloseされているとのエラーになるので、 接続側のusingを外しました。
これにより値は取得できていたので、よしとしていた。
static DbDataReader Reader(string messaage, string sql, params object[] sqlParameter)
{
DbProduct manager = new DbAdapter(DBInfo).Create();
// DB接続
manager.ConnectDB(false);
Logger.DebugSQL(message, sql, sqlParameter);
return DbDataReader reader = manager.ExecuteReader(sql, sqlParameter);
}
MaxPoolSize 超過エラー
ところが、アプリケーションを使用しているうちに何も動作しなくなり、MaxPoolSizeが超過した例外メッセージが出力されました。
そこでPostgreSQLのpsqlコマンドで、2秒単位で接続数を監視できるので実行してみました。
すると、アプリケーションを動かしていくたびに、どんどん接続数が増えていきました。
SELECT numbackends FROM pg_stat_database; \watch
改善内容
接続に関しては、DbProduct に using で囲む必要がありましたね。
でも、出来るだけ使用箇所を纏めたい。
主な使用用途として、存在確認とデータ1個取得です。
それ以外は、これまで通り manager を引数に渡してデータ取得するようにしています。
static DbDataReader Reader(DbProduct manager, string messaage, string sql, params object[] sqlParameter)
{
DbDataReader reader;
if (sqlParameter.Length == 0)
{
Logger.DebugLogSQL(message, sql);
reader = manager.ExecuteReader(sql);
}
else
{
Logger.DebugLogSQL(message, sql, sqlParameter);
reader = manager.ExecuteReader(sql, sqlParameter);
}
return reader;
}
public static bool HasReader(string message, string sql, params object[] sqlParameter)
{
bool result = false;
using (DbProduct manager = new DbAdapter(DBInfo).Create())
{
// DB接続
manager.ConnectDB(false);
using DbDataReader reader = Reader(manager, message, sql, sqlParameter);
result = reader.HasRows;
}
return result;
}
// 使用例
bool result = HasReader("ExistsPass:合格有無確認", sql, sqlParameter);
public static T? Scalar<T>(string message, string sql, params object[] sqlParameter)
{
using (DbProduct manager = new DbAdapter(DBInfo).Create())
{
// DB接続
manager.ConnectDB(false);
using DbDataReader reader = Reader(manager, message, sql, sqlParameter);
if (reader.HasRows)
{
reader.Read();
Object obj = reader.GetValue(0);
switch (obj)
{
case int i:
return (T)(object)i;
case long l:
return (T)(object)l;
case double d:
return (T)(object)d;
case decimal dc:
return (T)(object)dc;
case float f:
return (T)(object)f;
case bool b:
return (T)(object)b;
case DateTime dt:
return (T)(object)dt;
case string s:
return (T)(object)s;
default:
return (T)(object)default!;
}
}
}
return default;
}
// 使用例
int result = (int)Scalar<decimal>("GetMaxRepeatCount:繰り返し最大回数取得", sql, sqlParameter);
そういえば、SQLで、nvl(MAX(pass_type), 0) で値を取得した際の型は、decimal型なんですよね。
ジェネリクスメソッドで、Scalar を指定すると、型変換エラーになってしまいました。
最後に
using (DbDataReader reader = Reader(・・・ として、usingで囲んだから、呼び出し先の接続も閉じると思ってしまったのは失敗でした。
接続は、接続で閉じないと駄目でしたね。