Service Broker を使用しているアプリケーションは SQL 2008 です。1 日に約 1 回、データベースのパフォーマンスが著しく低下し始めます。これは Service Broker が原因であると判断しました。次のコマンドを使用して、すべてのブローカー接続をハード リセットした場合:
ALTER DATABASE [RegencyEnterprise] SET OFFLINE WITH ROLLBACK IMMEDIATE
ALTER DATABASE [RegencyEnterprise] SET ONLINE
その後、パフォーマンスは翌日頃まで通常に戻ります。また、パフォーマンスが低いときに次のクエリを実行すると、STARTED_OUTBOUND 状態でスタックしている多数 (現在約 1000) の会話が返されることにも気付きました。
SELECT * FROM sys.conversation_endpoints
また、次のクエリではエントリが返されません。
SELECT * FROM sys.dm_qn_subscriptions
SELECT * FROM sys.transmission_queue
このクエリによって返されるアイテムがたくさんある場合、パフォーマンスは問題ないようです。問題が発生するのは、STARTED_OUTBOUND の接続がこの状態のままになっている場合のみです。
SQL Server 2008 インスタンスで Service Broker に対して行った唯一の構成は、次のコマンドを実行することでした。
ALTER DATABASE RegencyEnterprise SET ENABLE_BROKER
SQL エラー ログを調べてみると、このエントリも 1000 回以上見つかりました。
07/11/2013 01:00:02,spid27s,Unknown,The query notification dialog on conversation handle '{6DFE46F5-25E9-E211-8DC8-00221994D6E9}.' closed due to the following error: '<?xml version="1.0"?><Error xmlns="http://schemas.microsoft.com/SQL/ServiceBroker/Error"><Code>-8490</Code><Description>Cannot find the remote service 'SqlQueryNotificationService-cb4e7a77-58f3-4f93-95c1-261954d3385a' because it does not exist.</Description></Error>'.
また、このエラーはログ全体で十数回見られますが、データベースにマスター キーを作成するだけでこれを修正できると思います。
06/26/2013 14:25:01,spid116,Unknown,Service Broker needs to access the master key in the database '<Database name>'. Error code:26. The master key has to exist and the service master key encryption is required.
これらのエラーの数は、キューに残っている会話の数に関連している可能性があると考えています. クエリ通知をサブスクライブするために使用している C# コードは次のとおりです。
private void EstablishSqlConnection(
String storedProcedureName,
IEnumerable<SqlParameter> parameters,
Action sqlQueryOperation,
String serviceCallName,
Int32 timeout,
params MultipleResult[] results)
{
SqlConnection storeConnection = (SqlConnection) ((EntityConnection) ObjectContext.Connection).StoreConnection;
try
{
using (SqlCommand command = storeConnection.CreateCommand())
{
command.Connection = storeConnection;
storeConnection.Open();
SqlParameter[] sqlParameters = parameters.ToArray();
command.CommandText = storedProcedureName;
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddRange(sqlParameters);
if (sqlQueryOperation != null)
{
// Register a sql dependency with the SQL query.
SqlDependency sqlDependency = new SqlDependency(command, null, timeout);
sqlDependency.OnChange += OnSqlDependencyNotification;
}
using (DbDataReader reader = command.ExecuteReader())
{
results.ForEach(result => result.MapResults(this, reader));
}
}
}
finally
{
storeConnection.Close();
}
}
通知を処理する方法は次のとおりです。
public static void OnSqlDependencyNotification(object sender, SqlNotificationEventArgs e)
{
if (e.Info == SqlNotificationInfo.Invalid)
{
// If we failed to register the SqlDependency, log an error
<Error is loged here...>
// If we get here, we are not in a valid state to requeue the sqldependency. However,
// we are on an async thread and should NOT throw an exception. Instead we just return
// here, as we have already logged the error to the database.
return;
}
// If we are able to find and remove the listener, invoke the query operation to re-run the query.
<Handle notification here...>
}
ブローカーの接続がこの状態になる原因を知っている人はいますか? または、これを引き起こしている原因を突き止めるために、どのツールを使用できますか? 現在、通知に登録している Web サーバーは 1 つしかないため、シナリオはそれほど複雑ではありません。
アップデート:
わかりましたので、この投稿から、「存在しないため、リモートサービスが見つかりません」というエラーは、SqlDependencyが適切にクリーンアップされていないことが原因であると判断しました。サービスが終了した後も、ブローカーはアプリケーションに通知を送信しようとしています。だから今、SqlDependency.Start() を呼び出す前に、アプリの起動時に適切にクリーンアップされていないものをすべてクリアする方法を見つける必要があるように思えますが、元のメソッド以外にこれを行う方法が見つかりませんでしたこれはデータベースをオフラインにするため、受け入れられません。これをきれいにする方法を知っている人はいますか?