Лямбда обнаружила UnobservedTaskException - Невозможно получить доступ к удаленному объекту
Я заметил это исключение в журналах CloudWatch для AWS Lambda.
Кажется, все обрабатывается, поэтому я думаю, что это исключение в коде AWS (в отличие от того, что я написал), которое создается после того, как Lambda закончила работу.
Так как функционально это работает, я игнорирую это, но я обеспокоен тем, что могут быть проблемы, которые я не заметил.
Lambda обнаружила исключение UnobservedTaskException с помощью события TaskScheduler.UnobservedTaskException:
{
"errorType": "AggregateException",
"errorMessage": "A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread. (Cannot access a disposed object.\nObject name: 'System.Net.Sockets.UdpClient'.)",
"cause": {
"errorType": "ObjectDisposedException",
"errorMessage": "Cannot access a disposed object.\nObject name: 'System.Net.Sockets.UdpClient'.",
"stackTrace": [
"at System.Net.Sockets.UdpClient.EndReceive(IAsyncResult asyncResult, IPEndPoint& remoteEP)",
"at System.Net.Sockets.UdpClient.<>c.<ReceiveAsync>b__56_1(IAsyncResult asyncResult)",
"at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)"
]
},
"causes": [ {
"errorType": "ObjectDisposedException",
"errorMessage": "Cannot access a disposed object.\nObject name: 'System.Net.Sockets.UdpClient'.",
"stackTrace": [
"at System.Net.Sockets.UdpClient.EndReceive(IAsyncResult asyncResult, IPEndPoint& remoteEP)",
"at System.Net.Sockets.UdpClient.<>c.<ReceiveAsync>b__56_1(IAsyncResult asyncResult)",
"at System.Threading.Tasks.TaskFactory`1.FromAsyncCoreLogic(IAsyncResult iar, Func`2 endFunction, Action`1 endAction, Task`1 promise, Boolean requiresSynchronization)"
]
}]
}
Лямбда-код довольно прост: он добавляет эти SNS messages
в базу данных SQL с помощью Dapper
,
Я думаю, что могут быть некоторые проблемы с тем, как я делаю async
в обработчике Fucntion. Есть идеи?
public class Function
{
private static string _connectionString;
public async Task<IEnumerable<InsertSnsResult>> FunctionHandler(SNSEvent @event, ILambdaContext context)
{
try
{
context.Logger.LogLine("Adding SNS Messages");
_connectionString = _connectionString ?? await DecryptHelper.DecryptEnvironmentVariableAsync("ConnectionString").ConfigureAwait(false);
var handler = new AddSnsMessageHandler(new SnsMessagesRepository(_connectionString, context.Logger));
return await handler.AddSnsEvents(@event).ConfigureAwait(false);
}
catch (Exception e)
{
context.Logger.LogLine(e.Message);
throw;
}
finally
{
context.Logger.LogLine("Finished SNS Adding Messages");
}
}
}
[Редактировать]
Просто поймите, что это исключение не попадает в блок try/catch. Если бы это было так, это не было бы UnobservedTaskException
, Вот почему у меня проблемы с получением корня проблемы.
И это код хранилища
public async Task<List<InsertSnsResult>> InsertSnsMessages(IEnumerable<SnsEvent> records)
{
using (var connection = new SqlConnection(_connectionString))
{
await connection.OpenAsync().ConfigureAwait(false);
var results = new List<InsertSnsResult>();
foreach (var record in records)
{
try
{
await connection.ExecuteAsync(InsertEventCommand, record).ConfigureAwait(false);
results.Add(new InsertSnsResult(record.CorrelationId, true));
}
catch (Exception ex)
{
_logger.LogLine($"InsertSns failed for {record.Id}. {ex.Message}");
results.Add(new InsertSnsResult(record.CorrelationId, false));
}
}
return results;
}
}
1 ответ
Сообщения журнала просты и объясняют, что происходит:
- У вас есть асинхронная задача
- Эта асинхронная задача обращается к объекту, который уже был удален, возможно, потому, что в вашем рабочем процессе имеется некоторое состояние гонки, в результате которого объект в асинхронном рабочем процессе выходит из строя с другой частью рабочего процесса, которая в этом нуждается. Это означает, что что-то серьезно нарушено в этом рабочем процессе.
- Асинхронная задача никогда не ожидается, либо асинхронно с await, либо (не делайте этого!) Синхронно с Result или Wait. Это означает, что исключительный путь продолжения никогда не берется, и задача замечает это, когда он собирается. Опять же, возможно, что-то серьезно нарушено в вашем рабочем процессе, если у вас есть задача, которую вы никогда не дождетесь результата. Объедините этот факт с фактом из предыдущего пункта: теперь у нас есть два доказательства, которые подтверждают друг друга, что в этом рабочем процессе что-то серьезно нарушено и что оно включает в себя задачу, которая не ожидается, когда это необходимо для обеспечения ограничения порядка,
- И, следовательно, вы получите исключение в вашей ветке финализатора, что действительно плохо.
Так как функционально это работает, я игнорирую это
Однажды я слышал, что когда завод загорается и горит на земле, в среднем было семь разных систем безопасности, которые люди игнорировали или отключали. Избавьтесь от этой привычки думать, что это работает, так что это должно быть безопасно. Может быть, это ничего, но я бы посчитал эти сообщения показательными для серьезной проблемы, пока у меня не будет доказательств обратного.
Я тоже столкнулся с местом, где у меня есть сторонняя библиотека, вызывающая ошибки. Я хотел зарегистрировать это вне CloudWatch. Чтобы Lambda не регистрировала их, я смог сделать некоторую злобную рефлексию, чтобы сбросить обработчик событий.
Вот код, чтобы сделать это самостоятельно. Обратите внимание, что это злой код. Он хрупкий и сломается, когда код будет изменен в CLR или даже если компилятор выполнит оптимизацию (что недавно произошло). Однако это был единственный способ отказаться от этой функции, предоставляемой Lambda.
private void ReplaceLambdaDefaultUnobservedTaskException()
{
try
{
var bindingFlags = BindingFlags.NonPublic | BindingFlags.Static;
Type type = typeof(TaskScheduler);
var field = type.GetField("_unobservedTaskException", bindingFlags);
if (field == null)
{
field = type.GetField("UnobservedTaskException", bindingFlags);
}
var handler = new EventHandler<UnobservedTaskExceptionEventArgs>(TaskSchedulerOnUnobservedTaskException);
field.SetValue(null, handler);
}
catch (Exception ex)
{
logger.Warning(ex, "Unable to do evil reflection.");
}
TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;
}
private void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
e.SetObserved();
logger.Error(e.Exception, "Lambda threw an UnobservedTaskException");
}