(解決策については、この元の投稿の下部にある私の編集を参照してください)
設定
Microsoft SQL Server2005Expressデータベースに2つのストアドプロシージャがあります。
WaitForMyMessage(@myName NVARCHAR(50), @myMessage NVARCHAR(MAX) OUTPUT)
ProvideMessage(@name NVARCHAR(50), @message NVARCHAR(MAX))
誰かが対応する名前でWaitForMyMessage()
電話をかけるまでブロックしたい。ProvideMessage()
誰かがすでにProvideMessage()
その名前で電話をかけていた場合WaitForMyMessage()
は、提供された値ですぐに戻ってきます。
私は当初、FIFOキューの動作を備えた単純なテーブルでこれを実装することを検討しましたが、このテーブルにブロックする方法を見つけることができませんでしたINSERT
。したがってWaitForMyMessage()
、ポーリングする必要があり、それは明らかな理由で受け入れられませんでした。
Q1:
特定のレコードがテーブルに表示されるまでブロックする効率的な方法はありますか?このWAITFOR
ステートメントは素晴らしいものですが、SQLはクエリに対してそれをサポートしていないようです(、、、またはのみをサポートしていDELAY
ますTIME
)RECEIVE
。しかし、それのようなものは素晴らしいでしょう、
例えば:
-- It would be great is SQL supported this, but as far as I can tell it doesn't.
DECLARE @t TABLE (ans NVARCHAR(MAX));
WAITFOR (
WITH A AS (
SELECT TOP (1) *
FROM ProviderMessage A
WHERE ProviderMessage.Name = @myName
ORDER BY A.ID
)
DELETE FROM A
OUTPUT deleted.ID INTO @t
);
SET @myMessage = (SELECT ans FROM @t);
Name
したがって、誰かが適切なレコードをテーブルに挿入するまでアイドル状態にProviderMessage
なり、それが発生するとすぐに、そのレコードは上記によって削除されValue
、呼び出し元に返されるフィールドが取得されます。
その他のアイデア
残念ながら、 Q1の回答が見つからなかったため、ServiceBrokerが提供する実際のメッセージキューを使用してこれを実装しました。Service Brokerのパワーとリーチを考えると、これはやり過ぎのように見えましたが、 Q1に対する回答がなかったため、試してみる必要がありました。サービスと単純なキューを次のように定義しました。
CREATE QUEUE q1
CREATE SERVICE s1 ON QUEUE q1 ([DEFAULT])
そして、次のようにWaitForMyMessage()
なりました。
DECLARE @farHandle UNIQUEIDENTIFIER;
SET @farHandle = (
SELECT FarHandle
FROM ProviderInfo
WHERE ProviderInfo.Name = @myName
);
WAITFOR (
RECEIVE @myMessage = CONVERT(NVARCHAR(MAX), message_body)
FROM q1
WHERE conversation_handle = @farHandle
);
次のようにProvideMessage()
メッセージを送信します。
DECLARE @nearHandle UNIQUEIDENTIFIER;
SET @nearHandle = (
SELECT NearHandle
FROM ProviderInfo
WHERE ProviderInfo.Name = @name
);
SEND ON CONVERSATION @nearHandle (@message)
これはすべて完全に機能しますが、1つだけ例外があります。特定の会話のニアハンドルとファーハンドルの両方を検出することは、ServiceBrokerではサポートされていないようです。両方の手順でプライベートに通信するためにテーブルにデータを入力できるように、両方を知っている必要があります。ProviderInfo
Q2:
新しい会話の近距離会話ハンドルと遠距離会話ハンドルの両方を取得するにはどうすればよいですか?sys.conversation_endpoints
現在、次のよう にクエリを実行してこれを行っています。
-- Create the conversation
DECLARE @nearHandle UNIQUEIDENTIFIER;
BEGIN DIALOG CONVERSATION @nearHandle
FROM SERVICE s1
TO SERVICE 's1'
WITH ENCRYPTION = OFF;
-- Queue an initialization message
SEND ON CONVERSATION @nearHandle ('');
-- Figure out the handle to the receiving side of this conversation
DECLARE @farHandle UNIQUEIDENTIFIER;
SET @farHandle = (
SELECT conversation_handle
FROM sys.conversation_endpoints
WHERE conversation_id = (
SELECT A.conversation_id
FROM sys.conversation_endpoints A
WHERE A.conversation_handle = @nearHandle
) AND conversation_handle <> @nearHandle
);
-- Get our initialization message out of the queue
DECLARE @unused TINYINT;
WAITFOR (
RECEIVE @unused = status
FROM q1
WHERE conversation_handle = @farHandle
);
-- Store both conversation handles, associated with this name
INSERT INTO ProviderInfo (Name, NearHandle, FarHandle)
SELECT @name, @nearHandle, @farHandle
しかし、分散サービスなどを含むはるかに複雑なシナリオをサポートするように設計されたService Brokerアーキテクチャでは、メッセージがsys.transmission_queue
ローカルに配置される可能性があり、その他の実装が複雑であるため、私のアプローチが本番環境に十分に堅牢であるとは確信していません。 。
それで、私がそれをしている方法が頑強でないならば、「正しい」方法はありますか?会話グループを使用してこの必要性を回避しようと考えましたが、本質的に同じ問題(会話グループIDも相手側に伝達されない)のために解決できませんでした。私が見つけたこれらのトピックは解決策を提供しません。また:
結論
これを機能させるためのハードルは、このように使用されることを意図していないことを心配させます。そのため、特定の本番シナリオでは機能しないか、将来的にサポートされなくなる可能性があります。誰かがこの方法が信頼できることを示すドキュメントを提供できますか、または信頼性があり、それでも効率的な代替ソリューション(Service Brokerの有無にかかわらず)を提供できますか?
ありがとう!
編集:(解決策)
ここでの中心的な質問はQ2でした(新しい会話の近距離会話ハンドルと遠距離会話ハンドルの両方を取得するにはどうすればよいですか?)。
何人かの寄稿者の良いアイデアのおかげで、明らかになった(そして今では明らかなようです!)答えは、初期化メッセージのintのSELECT
直後にキュー自体からingするだけで、遠いハンドルを取得することです。キュー列!SEND
SELECT
したがって、元の投稿のようにこれを行う代わりに、次のようにします。
-- Queue an initialization message
SEND ON CONVERSATION @nearHandle ('');
-- Figure out the handle to the receiving side of this conversation
DECLARE @farHandle UNIQUEIDENTIFIER;
SET @farHandle = (
SELECT conversation_handle
FROM sys.conversation_endpoints
WHERE conversation_id = (
SELECT A.conversation_id
FROM sys.conversation_endpoints A
WHERE A.conversation_handle = @nearHandle
) AND conversation_handle <> @nearHandle
);
-- Get our initialization message out of the queue
...
これをはるかに簡単に(そして効率的に!)行うことができます:
-- Queue an initialization message with a unique identifier
DECLARE @initbin VARBINARY(36);
SET @initbin = CONVERT(VARBINARY(32), @nearHandle);
SEND ON CONVERSATION @nearHandle (@initbin);
-- Figure out the handle to the receiving side of this conversation using the known unique identifier
DECLARE @farHandle UNIQUEIDENTIFIER;
SET @farHandle = (SELECT conversation_handle FROM q1 WHERE message_body = @initbin)
-- Get our initialization message out of the queue
...
皆さんありがとう!