環境
ElasticSearch クラスターとの間で大量のデータをインポート/エクスポートするために、C# で並列ジョブ フレームワークを作成しました。これを行うために、フレームワークによってある時点で実行されるオブジェクトとして、1 つのアイテムの各インポートまたはエクスポートをモデル化しました。ElasticSearch とのインターフェースには、NEST (公式の .NET ElasticSearch クライアント ライブラリ) v1.7.1 と JSON.Net 7.0.1 を使用しています。
各インポート/エクスポート タスク オブジェクトは、NEST を使用して ElasticSearch と対話します。パフォーマンス上の理由から、NEST の _msearch API で使用するために、タスク オブジェクトによって生成された検索要求を固定サイズのバッチにグループ化するプロキシ クラスを作成しました。このクラスの呼び出し元は、バッチが戻るまで遅延されます。そのクラスはこちらから入手できます。
私のフレームワークは、モデルの各インポート/エクスポート タスクの結果を「bool」または「Exception」としてラップします。個々の項目でエラーが発生した場合でも、全体のプロセスは続行できます。
問題
数時間のタスクがエラーなしで完了した後、次の例外が何千回も発生しています。
System.InvalidOperationException: Current error context error is different to requested error.
at _____.Matcher.<GetBestMatchAsync>d__15.MoveNext() in C:\\_work\\edc7a363\\_____\\Matcher.cs:line 266
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
_____.MatchBlock`1.<ExecuteAsyncInternal>d__19.MoveNext() in C:\\_work\\edc7a363\\_____\\MatchBlock.cs:line 111
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at _____.Block.BlockBase.<ExecuteAsync>d__11.MoveNext() in C:\\_work\\edc7a363\\_____\\Block\\BlockBase.cs:line 33
これは、例外をスローするコードです (上記のリンクされた一括サーチャー クラスから)。
try
{
var bulkResponse = Client.MultiSearch(searchDescriptor);
var items = bulkResponse.GetResponses<T>().ToList();
// Set response values and release all waiting tasks
var zip = currentBuffer.Zip(items, (op, result) => new { op, result });
foreach (var a in zip)
{
a.op.Response = a.result;
a.op.Cts.Cancel();
}
}
catch (Exception e)
{
foreach (var op in currentBuffer)
{
op.Error = e;
op.Cts.Cancel();
}
}
はどこClient
ですかIElasticClient
。
例外メッセージをグーグルで検索すると、 JSON.Net の JsonSerializerInternalBase クラスにあるこのメソッドにたどり着きます。これは、各逆シリアル化の後に実行されるようです。
private ErrorContext GetErrorContext(object currentObject, object member, string path, Exception error)
{
if (_currentErrorContext == null)
{
_currentErrorContext = new ErrorContext(currentObject, member, path, error);
}
if (_currentErrorContext.Error != error)
{
throw new InvalidOperationException("Current error context error is different to requested error.");
}
return _currentErrorContext;
}
複数のスレッドにわたるすべての操作で単一の NEST オブジェクトが再利用されている場合 (NEST は JsonSerializer インスタンスを 1 つしか使用しないと思います)、JSON.Net のこの部分はスレッドセーフではないと思います。奇妙なことに、実行して数時間経過するまでエラーが発生し始めません。
これをさらにデバッグするにはどうすればよいですか?