4

環境

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 のこの部分はスレッドセーフではないと思います。奇妙なことに、実行して数時間経過するまでエラーが発生し始めません。

これをさらにデバッグするにはどうすればよいですか?

4

3 に答える 3

0

JsonSerializerSettings次のようなエラーハンドラーを使用している場合にも、これが発生する可能性があることがわかりました。

parsedResponse = JsonConvert.DeserializeObject<T>(
  json,
  new JsonSerializerSettings
  {
    Error = (object sender, ErrorEventArgs args) =>
    {
      throw new MyCustomException(String.Format("Parse error: {0}", args.ErrorContext.Error.Message));
    },
...

スローされたエラーはMyCustomExceptionではなく、「現在のエラー コンテキスト エラーは要求されたエラーとは異なります」です。エラーハンドラー内で例外をスローすると、明らかにJson.NETはそれを好まない

于 2021-10-16T00:59:20.587 に答える