web-dev-qa-db-ja.com

Oracle Data Provider for .NET:接続要求がタイムアウトしました

OracleデータベースにアクセスするWindows2008 SP2/IIS7でホストされているC#WCFWebサービスがあります。通常、データアクセスは正常に機能しますが、負荷テスト中にタイムアウトし、ログと例外が次のように表示されることがよくあります。

Error occurred when processing XXXXXXXX Web Service
Oracle.DataAccess.Client.OracleException Connection request timed out at Oracle.DataAccess.Client.OracleException.HandleErrorHelper(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, String procedure, Boolean bCheck)
   at Oracle.DataAccess.Client.OracleException.HandleError(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, Object src)
   at Oracle.DataAccess.Client.OracleConnection.Open()
   at MyWorkspace.WorkForceDataAccess.CheckStaffIdInRSW()
   at MyWorkspace.MyClass.MyFunction(MyDataType MyData)

データベースを照会するには、次のようなものを使用します。

OracleConnection orConn = new OracleConnection();
orConn.ConnectionString = "user id=xxx; password=xxx; Connection Timeout=600; Max Pool Size=150; data source= (DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(Host = MYHOST.MYDOMAIN.com)(PORT = 1771)) (CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = MYSERVICE.MYDOMAIN.com)))";
orConn.Open();

using (var cmd = new OracleCommand("MY_UTIL.check_StaffIdInRSW", orConn) { CommandType = CommandType.StoredProcedure })
{
    cmd.Parameters.Add("P_Staff_Id", OracleDbType.Int32);
    cmd.Parameters["P_Staff_Id"].Direction = ParameterDirection.Input;
    cmd.Parameters["P_Staff_Id"].Value = Convert.ToInt32(MyDataObject.StaffId);

    cmd.Parameters.Add("P_retvalue", OracleDbType.Int32);
    cmd.Parameters["P_retvalue"].Direction = ParameterDirection.Output;

    cmd.ExecuteNonQuery(); // Execute the function

    //obtain result
    returnVal = int.Parse(cmd.Parameters["P_retvalue"].Value.ToString());
}

呼び出されているストアドプロシージャが常にかかっているわけではないと確信しています。これは、P_Staff_Idがテーブルに存在するかどうかをすばやく確認して結果を返す非常に単純な手順です。

さらに、これは負荷テスト中にのみ発生します。通常の操作では問題ありませんが、1秒あたり1メッセージの高負荷では、これはしばらくスムーズに実行された後に発生します。

回避策として、接続文字列に「接続タイムアウト= 600、最大プールサイズ= 150」を追加しましたが、問題は解決しませんでした。

同じアプリケーションが開発サーバーで実行されており、正常に動作します。そこでこの問題に遭遇したことはありません。

何を試すかについての提案はいただければ幸いです。オプションが不足しているようです。

7
DjD

同様の問題が発生し、これをデバッグして修正するのに時間がかかりました。多くの入力ファイルと多くのスレッド処理でストレスを感じるコード。各スレッドはEntityFrameworkを使用してOracleDB接続を開き、一連のdbクエリと挿入を実行します。しかし、ほとんどの場合動作します。

OracleConnectionを明示的に開くようにDbContextコンストラクターを変更しました。私はこのようないくつかのコードを追加しました

for (i = 0; i < 5; i++)
   try {
       oracleConnection.Open();
   } catch (OracleException) {
     Sleep for 15 ms and retry. 
     On last attempt I also do OracleConnection.ClearAllPools()
   }

改善しましたが、まだ完全には解決していません。デバッガーからのキャッチを壊して、多くのスレッドが開こうとしていて、処理しているスレッドがほとんどないことを確認しました。 Oracleスタックで開く場合、Oracleは内部目的でThreadPool.QueueUserWorkItemを実行し、その完了を待ちます。スタックの上にその待機を確認できます。ここでは、プールされた接続がたくさん利用できます(デフォルトは100)。私はほとんど10を使用していません。したがって、リソース不足ではありません。

しかし、問題はコードにあり、追加のスロットルなしでThreadPool.QueueUserWorkItemを使用しました。私たちが実行する必要があるすべてのジョブをキューに入れること、これまでにどれだけ必要か、そして.NETにこれを処理させることは、すばらしいことだと思いました。しかし、これには微妙な問題があります。すべてのジョブがキューカウント全体を消費しました。 OracleConnectionは、プールからプールされた接続を取得する場合、スレッドプールにもキューイングします。しかし、それが完了することは決してありません。ジョブはすべてOracleConnection.Openを待機しており、そのキュースレッドプロシージャは引き続きキューにあります。したがって、最後に待機はタイムアウトで終了します。利用可能なプールされた接続がたくさんあるにもかかわらず、すべてのThreadPoolプロシージャを使い果たしており、Oracleのスレッドプールでもチャンスを得られなかったのは残念です。ここでは、ThreadPool.SetMaxThreadsの設定も役に立ちません。問題はまだ同じです。すべてのスレッドプールリソースを占有しますが、Orcaleはそれを見つけることができず、引き続きキューに入れられます。

修正はThreadPoolだけに依存するのではなく、独自のスロットルも追加します。 BlockingCollectionとsempahoresを使用し、ThreadPoolに並行ジョブの制限数(たとえば5)のみを追加しました。このようにして、OracleConnectionは常に使用可能なThreadPoolスレッドを検出し、失敗しません。

7
R. Sridharan

Connection.Close()を使用した後でも、以前はこの問題が頻繁に発生していました。

長い分析の後、私は以下に述べるようにいくつかのことを学びました

  1. Connection.Close()はdb接続を破棄しません
  2. 接続タイムアウトは、問題がデータベースクエリのみにあることを意味するものではありません
  3. 接続のタイムアウトは、接続プールの接続が完全に原因である可能性もあります(db接続の最大セッション数に達したため、これが原因でした)

修正:-分析には長い時間がかかりましたが、修正はわずか2分です

using(DbConnection instance)
{

}

例:-

using (DbConnection  objDbConnection = new DbConnection())
{
   ojDbConnection.PersistData();
}

PersistData();の下Open、closee.tcなどのすべてのdb操作。行われます

ご存知のとおり、「使用」はの短縮形です

try
{

}
catch()
{

}
Finally
{
  Dispose objDbConnection;
}

それが私を助けたので、それが役立つことを願っています

2

最後にconnection.close()を追加してみてください。コードで接続を解放し、それらを接続プールに明示的に返すことはありません。したがって、接続はGCの開始時にのみ接続プールに戻されます。

1
Sasa Ninkovic