アセンブリを「安全でない」として登録する必要なく、SQL CLR 関数で機能するマルチスレッド キャッシュ メカニズムはありますか?
この投稿でも説明されているように、単純にlock
ステートメントを使用すると、安全なアセンブリで例外がスローされます。
System.Security.HostProtectionException:
Attempted to perform an operation that was forbidden by the CLR host.
The protected resources (only available with full trust) were: All
The demanded resources were: Synchronization, ExternalThreading
多くの操作がキャッシュの読み取りと書き込みを同時に実行できるように、関数へのすべての呼び出しですべて同じ内部キャッシュをスレッドセーフな方法で使用する必要があります。基本的ConcurrentDictionary
に、SQLCLR の「安全な」アセンブリで機能する が必要です。残念ながら、ConcurrentDictionary
それ自体を使用すると、上記と同じ例外が発生します。
これを処理するために SQLCLR または SQL Server に組み込まれているものはありますか? または、SQLCLR のスレッド モデルを誤解していますか?
SQLCLR のセキュリティ制限については、できる限り読みました。特に、次の記事は、私が話していることを理解するのに役立つ場合があります。
このコードは最終的に他の人に配布されるライブラリの一部になるので、「安全でない」として実行する必要はありません。
私が検討している 1 つのオプション (以下の Spender によるコメントで取り上げられています) は、SQLCLR コード内から tempdb にアクセスし、代わりにそれをキャッシュとして使用することです。 しかし、それを行う方法が正確にはわかりません。また、メモリ内キャッシュと同じくらいパフォーマンスが高いかどうかもわかりません。 以下の更新を参照してください。
利用可能な他の代替案に興味があります。ありがとう。
例
次のコードは、静的同時実行ディクショナリをキャッシュとして使用し、SQL CLR ユーザー定義関数を介してそのキャッシュにアクセスします。関数へのすべての呼び出しは、同じキャッシュで機能します。ただし、アセンブリが「安全でない」として登録されていない限り、これは機能しません。
public class UserDefinedFunctions
{
private static readonly ConcurrentDictionary<string,string> Cache =
new ConcurrentDictionary<string, string>();
[SqlFunction]
public static SqlString GetFromCache(string key)
{
string value;
if (Cache.TryGetValue(key, out value))
return new SqlString(value);
return SqlString.Null;
}
[SqlProcedure]
public static void AddToCache(string key, string value)
{
Cache.TryAdd(key, value);
}
}
これらは と呼ばれるアセンブリSqlClrTest
にあり、次の SQL ラッパーを使用します。
CREATE FUNCTION [dbo].[GetFromCache](@key nvarchar(4000))
RETURNS nvarchar(4000) WITH EXECUTE AS CALLER
AS EXTERNAL NAME [SqlClrTest].[SqlClrTest.UserDefinedFunctions].[GetFromCache]
GO
CREATE PROCEDURE [dbo].[AddToCache](@key nvarchar(4000), @value nvarchar(4000))
WITH EXECUTE AS CALLER
AS EXTERNAL NAME [SqlClrTest].[SqlClrTest.UserDefinedFunctions].[AddToCache]
GO
次に、データベースで次のように使用されます。
EXEC dbo.AddToCache 'foo', 'bar'
SELECT dbo.GetFromCache('foo')
アップデート
Context Connectionを使用して SQLCLR からデータベースにアクセスする方法を理解しました。この Gistのコードは、ConcurrentDictionary
アプローチと tempdb アプローチの両方を示しています。次に、いくつかのテストを実行し、クライアントの統計 (10 回の試行の平均) から測定された次の結果を使用しました。
Concurrent Dictionary Cache
10,000 Writes: 363ms
10,000 Reads : 81ms
TempDB Cache
10,000 Writes: 3546ms
10,000 Reads : 1199ms
そのため、tempdb テーブルを使用するという考えは破棄されます。私が試すことができるものは他に本当にありますか?