2020年8月25日火曜日

ADO.NET/Entity Frameworkにてコネクションプールが枯渇する。エラー:タイムアウトに達しました。プールから接続を取得する前にタイムアウト期間が過ぎました。プールされた接続がすべて使用中で、プール サイズの制限値に達した可能性があります。

パフォーマンスモニタでの監視

注目すべきは、コネクションプールが100個を超えた場合、コネクションプールからのコネクション取得タイムアウトは、コネクションタイムアウト15秒を待つことです。
15秒後にエラーが発生します。

この画像は、テストコードでコネクション数を自由にいじってみて監視してみました。テストコードで「U」キーでコネクションを増やして、「D」キーでコネクションを減らして見ました。


パフォーマンスカウンタを動作させる対応

発生しているエラー

System.InvalidOperationException
  HResult=0x80131509
  Message=タイムアウトに達しました。プールから接続を取得する前にタイムアウト期間が過ぎました。プールされた接続がすべて使用中で、プール サイズの制限値に達した可能性があります。
  Source=System.Data
  スタック トレース:
   場所 System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternal oldConnection, DbConnectionInternal& connection)
   場所 System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   場所 System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   場所 System.Data.SqlClient.SqlConnection.TryOpenInner(TaskCompletionSource`1 retry)
   場所 System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   場所 System.Data.SqlClient.SqlConnection.Open()
   場所 ConsoleAppLegacy.Program.<Main>d__0.MoveNext() (D:\dev\sample_gomi\20200819_.NETCore_ADO.NET\ConsoleAppLegacy\ConsoleAppLegacy\Program.cs):行 22
   場所 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   場所 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   場所 System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   場所 ConsoleAppLegacy.Program.<Main>(String[] args)

  この例外は、最初にこの呼び出し履歴 
    System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(System.Data.Common.DbConnection, System.Threading.Tasks.TaskCompletionSource<System.Data.ProviderBase.DbConnectionInternal>, System.Data.Common.DbConnectionOptions, System.Data.ProviderBase.DbConnectionInternal, out System.Data.ProviderBase.DbConnectionInternal)
    System.Data.ProviderBase.DbConnectionInternal.TryOpenConnectionInternal(System.Data.Common.DbConnection, System.Data.ProviderBase.DbConnectionFactory, System.Threading.Tasks.TaskCompletionSource<System.Data.ProviderBase.DbConnectionInternal>, System.Data.Common.DbConnectionOptions)
    System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(System.Data.Common.DbConnection, System.Data.ProviderBase.DbConnectionFactory, System.Threading.Tasks.TaskCompletionSource<System.Data.ProviderBase.DbConnectionInternal>, System.Data.Common.DbConnectionOptions)
    System.Data.SqlClient.SqlConnection.TryOpenInner(System.Threading.Tasks.TaskCompletionSource<System.Data.ProviderBase.DbConnectionInternal>)
    System.Data.SqlClient.SqlConnection.TryOpen(System.Threading.Tasks.TaskCompletionSource<System.Data.ProviderBase.DbConnectionInternal>)
    System.Data.SqlClient.SqlConnection.Open()
    ConsoleAppLegacy.Program.Main(string[]) (Program.cs 内)
    System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(System.Threading.Tasks.Task)
    System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(System.Threading.Tasks.Task)
    System.Runtime.CompilerServices.TaskAwaiter.GetResult()
    ...
    [呼び出し履歴が切り捨てられました] でスローされました

エラーを検証したソース

		/// <summary>
		/// key u			::処理を実行してクローズしない。
		/// key n			::処理を実行してクローズ(normal)
		/// key d			::処理を実行して2つクローズ
		/// </summary>
		/// <param name="args"></param>
		/// <returns></returns>
		static async Task Main(string[] args)
		{
			Console.WriteLine("Hello World!");
			try
			{
				List<SqlConnection> persistantConnections = new List<SqlConnection>();
			start:
				var key = Console.ReadKey();

				SqlConnection con = new SqlConnection("Server=SQLServerIP;Database=kiyotaka;user id=sa;password=p@$$w0rd;");
				con.Open();
				var cnt = 0;
				SqlCommand cmd = new SqlCommand($@"begin tran;
insert test values(1,'{++cnt}');
insert test values(1,'{++cnt}');
commit;", con);

				cnt = await cmd.ExecuteNonQueryAsync();
				switch (key.Key)
				{
					case ConsoleKey.U:
						persistantConnections.Add(con);
						break;
					case ConsoleKey.D:
						con.Close();
						if (persistantConnections.Count > 0)
						{
							persistantConnections[0].Close();
							persistantConnections.RemoveAt(0);
						}
						break;
					default:
						con.Close();
						break;
				}

				Console.WriteLine($"cnt={cnt}");
				goto start;
			}
			catch (Exception ex)
			{
				Console.WriteLine(ex.ToString());
			}
			Console.ReadKey();
			return;
		}

0 件のコメント:

コメントを投稿