"Current error context error is different to requested error" Json.Net 异常
"Current error context error is different to requested error" exception in Json.Net
上下文
我用 C# 编写了一个并行作业框架来 import/export 大量数据 from/to ElasticSearch 集群。为此,我将单个项目的每次导入或导出建模为一个对象,该对象在某个时刻由框架执行。为了与 ElasticSearch 交互,我使用了 NEST(官方 .NET ElasticSearch 客户端库)v1.7.1 和 JSON.Net 7.0.1.
每个 import/export 任务对象都使用 NEST 与 ElasticSearch 交互。出于性能原因,我编写了一个代理 class,它将任务对象生成的搜索请求分组为固定大小的批次,以与 NEST 的 _msearch API 一起使用。此 class 的调用者被延迟到其批次 returns。 That class is available here.
我的框架将每个 import/export 任务的结果包装为“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
这是抛出异常的代码(来自上面链接的批量搜索器 class):
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
。
用谷歌搜索异常消息让我找到 this method in the JsonSerializerInternalBase class in JSON.Net,它似乎在每次反序列化后执行:
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 实例 - 这让我认为 JSON.Net 的这一部分不是线程安全的。尽管奇怪的是错误直到进入 运行.
几个小时后才开始发生
如何进一步调试?
我的同事最终追查到错误 - 这是由于从另一个 JsonConverter 调用的 JsonConverter 内部抛出的异常。 "error context" 是一个内部的 JSON.Net 东西,用于跟踪最后抛出的异常。似乎异常是由错误的 JsonConverter 处理的。我们向内部 JsonConverter 添加了一个标志,让它知道在特定上下文中不要抛出异常。
当使用自定义序列化器将枚举 属性 序列化为 null,然后使用默认反序列化器反序列化时,我遇到了这个错误。
我发现如果您像这样使用 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 不喜欢在错误处理程序中抛出异常
上下文
我用 C# 编写了一个并行作业框架来 import/export 大量数据 from/to ElasticSearch 集群。为此,我将单个项目的每次导入或导出建模为一个对象,该对象在某个时刻由框架执行。为了与 ElasticSearch 交互,我使用了 NEST(官方 .NET ElasticSearch 客户端库)v1.7.1 和 JSON.Net 7.0.1.
每个 import/export 任务对象都使用 NEST 与 ElasticSearch 交互。出于性能原因,我编写了一个代理 class,它将任务对象生成的搜索请求分组为固定大小的批次,以与 NEST 的 _msearch API 一起使用。此 class 的调用者被延迟到其批次 returns。 That class is available here.
我的框架将每个 import/export 任务的结果包装为“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
这是抛出异常的代码(来自上面链接的批量搜索器 class):
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
。
用谷歌搜索异常消息让我找到 this method in the JsonSerializerInternalBase class in JSON.Net,它似乎在每次反序列化后执行:
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 实例 - 这让我认为 JSON.Net 的这一部分不是线程安全的。尽管奇怪的是错误直到进入 运行.
几个小时后才开始发生如何进一步调试?
我的同事最终追查到错误 - 这是由于从另一个 JsonConverter 调用的 JsonConverter 内部抛出的异常。 "error context" 是一个内部的 JSON.Net 东西,用于跟踪最后抛出的异常。似乎异常是由错误的 JsonConverter 处理的。我们向内部 JsonConverter 添加了一个标志,让它知道在特定上下文中不要抛出异常。
当使用自定义序列化器将枚举 属性 序列化为 null,然后使用默认反序列化器反序列化时,我遇到了这个错误。
我发现如果您像这样使用 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 不喜欢在错误处理程序中抛出异常